I'm having at the moment a big solution with different projects sending logs with Nlog to LogReceiverService target.
I would like to use an interface so I have only 1 installation of Nlog in 1 project of my solution.
So I created a new interface to call instead of NLog.LogReceiverService.ILogReceiverClient (inherited from it) but when I initialize my logger, it's still looking for the contract NLog.LogReceiverService.ILogReceiverClient
Is this a normal operation. Is there a way to change this contract?
Related
I wonder if there is a .NET interface that makes the transition between Redis and Rabbit, without (big) changes in the code?
In case I eventually want to change Rabbit to Redis, because during my tests I needed more performance, for example.
I want to do this without much effort with an interface.
Is this possible?
It's classic inversion of control example, where you want defer implementation details.
This, as suggested in comment, is achieved by app specific interface. So for your code to be closed to modifications, it is required for it to be placed in separate project, where you'd also define interfaces, such as
public interface IService
{
Task DoSomethingAsync(CancellationToken cancellationToken);
}
and then create separate project for Rabbit and one for Redis (each project would then have further, specialized nugets to handle specific communication).
Now the trick is in each project implement interface IService and method implementation will use underlaying provider.
This way once you need Rabbit, you have separate project for it and you just implement the interface again, but using Rabbit provider.
And you defer the decision on what implementation to use to calling code, which is composition root, where you define services. It will most probably look like this:
services.AddScoped<IService, RabbitService>();
and you use instances of IService everywhere.
Once you need to switch, you will just change above line to:
services.AddScoped<IService, RedisService>();
... so here you have the way to have this changes with close to no code changes.
If your dependcy injection configuration lies within some external config (which is not widely applied) you don't have to touch even the code.
We have the following scenario where we encounter an issue with Serilog due to .NET's AppDomain isolation:
|App| --> |3rd| --> |Class| --> Log.Debug("...") --> [NOK]
`-------------> |Class| --> Log.Debug("...") --> [OK]
(1) (2) (3) (4) (5)
We create a global Logger object in our app's Main() method and assign it to Log.Logger.
We invoke a third-party tool that internally creates a new AppDomain. (We have no control over this and cannot change this behavior.)
Instance objects of Class is created both within our app and from the third-party tool.
Class executes code for logging using Serilog's static Log class.
Log.Logger references the correctly configured Logger when accessed from the Class object created from our app, but is just set to a default SilentLogger in the Class object created via the third-party tool.
This, I guess, is the expected behavior since the two Class objects belongs to two different AppDomains and static variables are isolated to their own AppDomain.
Is there some way to work around this? Can we somehow make use of Serilog's convenient static Log class? Is there something else in the Serilog framework other than the Log class solving this kind of issue, that we might have missed? I found a similar issue in #380 but without a final solution, as far as I could see.
We considered just creating a new Logger object in the new AppDomain. However, that means properties that we pushed to the Logger somewhere between 1-2 and 1-3 is not included in the new Logger's log events.
Our team has created a library that automates the implementation of exception handling and logging for WCF Services. Using this library, developers need only to decorate a service with a custom attribute, setup some simple configuration file entries, and they can already take advantage of a generic exception handling and logging mechanism.
Here's an example of how the library is used:
[ErrorHandlingBehavior (LogWriterOption.EmailLogWriter, LogWriterOption.SQLLogWriter)]
public class SampleService : ISampleService
{
public string GetData(int value)
{
throw new DivideByZeroException();
//return string.Format("You entered: {0}", value);
}
}
The ErrorHandlingBehavior class makes use of a Logger object that takes in the LogWriterOption enums in the parameters to figure out where to log.
Our original intention was to allow the developer to specify his own logging mechanism and supply it to the ErrorHandlingBehavior, so as to remove the dependency of the solution on the Logger class (instead it takes in any class that implements ILogger). However, specifying attributes in the manner below produces an error:
[ErrorHandlingBehavior (new Logger (new HashSet<LogWriterOptions>
{LogWriterOption.EmailLogWriter, LogWriterOption.SQLLogWriter}))]
It seems we cannot instantiate anything when specifying attributes, and therefore we are now unable to let users specify their own logging mechanism.
Would anyone know of a way around this? How can we feed an instance of a class implementing ILogger to our attribute instead of hard-wiring the depenendency?
I also wrote some logging exception handling behaviors. In situations like this I always asked myself:
What would log4net do?
Your LogWriterOptions appear to translate to log4net appenders. Appenders are generally best done through xml configuration because their requirements change by environment. (Its the logging equivalent of don't put your WCF client binding in code.) In other words, when developing locally: don’t send email and just output to a local text file. When running in QA: output to the DB but don’t send email with a tester breaks something on purpose. In production: do something else completely different. Log4net appenders support all these types of post compile changes (and more).
Back to your question:
In your approach, I would pass the ErrorHandlingBehavior a behavior name as a string like “StandardLogging” which would look up a configurable behavior that results in EmailLogWriter and SQLLogWritter being used.
An alternate approach which is common in logging frameworks is to pass the type of the class being logged. If that type is not explicitly configured, it gets the default appenders.
Note that this configuration approach has the added benefit of
Centralizing logging output options for the entire application. If you change your standards, you don't have to update many class files.
Standardizing what log writer options different pieces of code are using. In the code review meeting you simply ask “Are you using standard logging output?” Check.
Update in response to your "keep it simple" comment:
If keep it simple is the goal, I would say pass nothing to your behavior's constructor. Instead put all the log4net config information in its own log4net.config file and store that as a part of the common logging libraries in source control. Then new projects (or junior devs) are just required to add
<configuration>
<log4net configSource="log4net.config" />
</configuration>
to app.config. The bonus to this approach is that as a part of our build process we defined different log4net.config files for deployment to different environments.
You could use the factory pattern. Have the developer specify a type that will be used to provide an ILogger instance:
[ErrorHandlingBehavior(LoggerFactoryType = "FooBar.MyLoggerFactory")]
This type could implement an interface of yours:
public interface ILoggerFactory
{
ILogger GetLogger();
}
and then inside your custom attribute you could first get the factory type using the Type.GetType method, check if it implements the ILoggerFactory interface, instantiate the factory using the Activator.CreateInstance method and finally call the GetLogger method on that instance.
I have description of my Application Services using my fancy classes (ServiceDescription class that contains collection of ServiceMethod description, for simplification).
Now, I want to expose one Application Service as one WCF Service (one Contract). The current solution is very lame - I have console application that generates *.svc file for each Application Service (ServiceDescription). There is one method (Operation) generated for one ServiceMethod.
This works well but I would like to make it better. It could be improved using T4 template but I'm sure that there is still better way in WCF.
I would still like to have one *.svc file per one Application Service but I don't want to generate methods (for corresponding Application Service methods).
I'm sure that there must be some interfaces that allow to discover operations dynamically, at runtime. Maybe IContractBehavior...
Thanks.
EDIT1:
I don't want to use generic operation contract because I would like to have the ability to generate service proxy with all operations.
I'm sure that if I write WCF service and operations by hand then WCF uses reflection to discover the operations in the service.
Now, I would like to customize this point in order not to use reflection, just use my "operations discovering code" instead.
I think there is nothing wrong with static code generation in that case. In my opinion, it is a better solution than dynamic generation of contracts. Keep in mind that your contract is the only evidence you have/provide that a service is hosting a particular set operations.
The main issue I see about the dynamic approach is about versioning and compatibility. If everything is dynamically generated, you may end up transparently pushing breaking changes into the system and create some problems with existing clients.
If you have a code generator when you plan on implementing some changes in the application services, it will be easier to remember that the changes you make on the services may have a huge impact.
But if you really want to dynamically handle messages, you could use a generic operation contract (with the Action property set to *), and manually route the messages to the application services.
Keep in mind that you would lose the ability to generate from the service a proxy containing a list of operations available.
What would be the best way to implement NLog in my Prism / CAL WPF application. This might be an amateur question, I am a bit new to the whole Prism framework :)
I thought about putting the reference to the NLog dll in the Infrastructure module and make a wrapper singleton class e.g. MyLogger. My thinking was to be able to have the reference to 1 logger implementation somewhere in a central place that everything has reference to, and the only thing that I know of in Prism would be your Infrastructure module.
The obvious other way is to add a reference to NLog to each module but I think that would defeat the purpose of decoupling and all of that.
Any ideas would be most helpful
Regards
I would recommend something similar to your first idea, although it leverages an already existing interface in Prism.
While I'm not sure the exact method signatures available to you in NLog, you may want to consider using Prism's ILoggerFacade interface, which is typically defined in your Bootstrapper (see the StockTraderRI application for an example of how this is set up). Typically, this acts as a pass through to Microsoft's Composite Logging interface, but there's no reason why you can't use this to hook into your own logger.
A few reasons to consider this approach:
It uses the already existing ILoggerFacade interface in the Prism framework, which other developers will be familiar with
If you later decide to go to a different logging framework, you just have to replace the object behind the ILoggerFacade implementation
The other approach would be to do as you suggest: create an interface that defines a service to NLog (or expose an existing NLog interface) in your infrastructure DLL and register the implementation of that service in your bootstrapper. You can then you your dependency injection container to get a reference to the logger service in your modules. Note, however, that this really just reproduces what the ILoggerFacade interface already gives you.