In order to try and get a nice logging abstraction with Log4net, I took the abstraction from this SO post and the adapter from this SO post and tried to get them working together.
All that was really left to do was configure the container and that is the part which I have not succeeded in doing.
The config which I have tried is
public static class InfrastructureRegistry
{
public static void RegisterLoggingServices(this Container container)
{
container.RegisterConditional(typeof(ILog), c => LogManager.GetLogger(
c.Consumer.ImplementationType).GetType(),
Lifestyle.Scoped, c => true);
container.RegisterPerWebRequest<ILogger, Log4netAdapter>();
}
}
As you can see from the code, I would like a specific log4net logger which takes its Type from the class into which it is injected. Whilst most logging would be done in a catch-all, I want some logging to happen in lower layers e.g. when a form validation fails.
The ActivationException which I get with that configuration is :
The constructor of type LogImpl contains the parameter with name
'logger' and type ILogger that is not registered. Please ensure
ILogger is registered, or change the constructor of LogImpl.
Not quite sure where to go from here, so any help would be appreciated.
Edit
Sorry, I should point out that I am trying to write it such that I only have to write this config once. The following factory function works, but I don't want to have to manually add more config every time I want to inject a logger:
container.RegisterPerWebRequest<ILog>(() => LogManager.GetLogger(typeof(LoginController)));
The example adapter you point at assumes a single logger for every component in the application, while what you wish is to have a specific logger that 'knows' about its consumer, so it can relate the log messages to the originating class.
Although this seems to be a very common practice when working with tools like log4net and NLog, in my experience, this requirement often comes from the fact that logging is done at too many places in the code. Please read this stackoverflow q/a for more information.
That said, if you want to register the logger conditionally, you will have to change the adapter to a generic class; that way you can make the registration conditional:
public class Log4netAdapter<T> : ILogger
{
private static readonly log4net.ILog logger = LogManager.GetLogger(typeof(T));
public void Log(LogEntry entry)
{
if(entry.LoggingEventType == LoggingEventType.Information)
logger.Info(entry.Message, entry.Exception);
else if(entry.LoggingEventType == LoggingEventType.Warning)
logger.Warn(entry.Message, entry.Exception);
else if(entry.LoggingEventType == LoggingEventType.Error)
logger.Error(entry.Message, entry.Exception);
else
logger.Fatal(entry.Message, entry.Exception);
}
}
With this generic class, you can do the following conditional/contextual registration:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Log4netAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
Related
I am (as something of a novice) implementing my own custom logger for use in ASP.NET Core MVC apps. I have this logger working functionally in every regard. But I cheated a little so far, namely I implemented the ILogger.IsEnabled method as follows:
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
Functionally, this works fine, since the framework ensures that the Log() method is only invoked if the log level is at or higher than the one specified. So the correct "things" are being logged and the lower-level "things" are not being logged as expected.
However, I also want to support the following kind of situation in my code, where _logger is typed as ILogger and is properly injected in my controller:
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("This is an expensive message to generate: " +
JsonConvert.SerializeObject(request));
}
To make this effective, my IsEnabled() method should be able to know what the log level IS for the instance of the logger that was created with my LoggerProvider, but I don't know how to get that information directly, or how to pass it properly to the injected instance of the the logger I am working with.
Complex examples and tutorials I have been able to find seem to be constructed in every case for console app types, not network app types, and so far I have been unsuccessful at figuring out how to do this through the templated Startup class in ASP.NET MVC.
What is the simplest and most effective way to stop cheating at my custom IsEnabled() method in order to avoid the unnecessary serialization (in my example) if none of the registered loggers in the injected instance are handling the Debug log level? Or do you have a favorite example or tutorial in the ASP.NET core setting you can point me to?
You can take a look at built-in loggers source code and see how they implement it.
In short, they only check that logLevel != LogLevel.None, but depending on the logger logic, you might also want to check some other configuration. For example, DebugLogger logger also checks the Debugger.IsAttached property and EventLogLogger checks the EventLogSettings.Filter (supplied via constructor).
Update
To make this effective, my IsEnabled() method should be able to know what the log level IS for the instance of the logger that was created with my LoggerProvider, but I don't know how to get that information directly, or how to pass it properly to the injected instance of the the logger I am working with.
You can create an implementation of ILoggerProvider which in turn can make use of dependency injection to get the configuration you want. If you want to use the options pattern to configure it, you must do something along the lines of:
public class MyLoggerProvider : ILoggerProvider
{
private readonly IOptions<MyLoggerOptions> _options;
public MyLoggerProvider(IOptions<MyLoggerOptions> options)
{
_options = options;
}
public ILogger CreateLogger(string name)
{
return new MyLogger(name, _options.Value);
}
}
And optionally add an extension method to make registration easier:
public static class MyLoggerExtensions
{
public static ILoggingBuilder AddMyLogger(this ILoggingBuilder builder, Action<MyLoggerOptions> configure)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, MyLoggerProvider>());
LoggerProviderOptions.RegisterProviderOptions<MyLoggerOptions, MyLoggerProvider>(builder.Services);
builder.Services.Configure(configure);
}
}
Following this answer I did this :
public class Log4netAdapter<T> : ILogger
{
private static readonly log4net.ILog logger = LogManager.GetLogger(typeof(T));
public void Log(LogEntry entry)
{
if(entry.LoggingEventType == LoggingEventType.Information)
logger.Info(entry.Message, entry.Exception);
else if(entry.LoggingEventType == LoggingEventType.Warning)
logger.Warn(entry.Message, entry.Exception);
else if(entry.LoggingEventType == LoggingEventType.Error)
logger.Error(entry.Message, entry.Exception);
else
logger.Fatal(entry.Message, entry.Exception);
}
}
And then on the Simple Injector :
container.RegisterConditional(
typeof(ILogger),
c => typeof(Log4netAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
This works great if I inject the ILogger on every class constructor I need. My problem is that I have some classes that I cannot use the constructor injection. For this cases I would tipically do :
var logger = SimpleInjectorInitializer.Container.GetInstance<ILogger>();
However the above method does not work, it throws an error on the simple injector class since the c.Consumer is null.
Here is one of the examples I need to resolve ILogger, this class is registered on the webapi startup class.
public class ExceptionWebApiHandlingAttribute : IExceptionFilter
{
public ExceptionWebApiHandlingAttribute()
{
}
}
Is there any alternative ?
Thanks
When working on the application boundary, it is sometimes hard or impossible to use constructor injection. Typical examples are MVC filter attributes or ASP.NET Web Form Page classes that require a default constructor.
A typical solution to these problems is to make such boundary class into a Humble Object, where all interesting logic is extracted from the boundary class into a component. The boundary class should only contain the call to the Service Locator and call one method on the resolved service. This minimizes the amount of untestable code in the application.
In all other cases, constructor injection should be preferred.
The fact however that you resolve an ILogger implies that your boundary class does too much. Instead this ILogger should be a constructor dependency of the component that you extracted from the boundary class to become a Humble Object.
Once you've done this, you won't be resolving ILogger directly anymore and this solves your problem; ILogger has become a dependency of a consumer and this ensures that Simple Injector is able to build the correct Logger<T> on your behalf.
When it comes to applying dependencies to exception filters in Web API (your particular case), a good solution is to create a proxy for your exception filters that will delegate the call to the real filter that gets resolved. This can be a bit of infrastructure and the concept is explained here.
If it is impossible to apply the above advise, for whatever reason, you can always request a Logger<T> directly from the container:
ILogger log = SimpleInjectorInitializer.Container.GetInstance<Logger<MyHumbleObject>>();
Im beginning to learn about dependency injection and have decided to try to build my own (simple) logging facade as an introduction to it. So far I have the logging facade working with the basic functionality of NLog and log4net using Ninject.
(I know that Ninject has its own logging facade, but this is a learning exercise)
However I have run into a problem. What I want to do is to replicate what I log to file in a RichTextBox (Im using winforms). I am able to do this using NLog and log4net directly. The problem I have is that when Ninject wires up the interfaces it creates an implementation of a logger before the richtextbox on my winform has been created and consequently the logger does not find the richtextbox.
What I think I need to do is create the form then get Ninject to create the logger and inject it into the form, but I have no idea how to do this. Although I might be looking at this in completely the wrong way?
Please see below for the code im using to tie this together:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
IKernel kernel = new StandardKernel(new DI.NLog.Logger());
var form = kernel.Get<Form1>();
Application.Run(form);
}
}
and the constructor of my form is where the logger is injected.
public Form1(ILog log)
{
InitializeComponent();
_log = log;
}
Any help or advice would be appreciated.
Thank you
Here is one way to think about this:
First, make the form class implement ILog. What this means is that the form it self is a logging destination. When the logging methods are invoked on the form, write what you need to write to the RichTextBox control.
Now, the form itself is an ILog and it also depends on ILog. Also, I assume there is another ILog destination that you would like to log to, e.g., a Log4net adapter.
Here is how I imagine the object graph:
The arrow from X to Y means that X depends on Y, or that Y is injected into X.
The CompositeLog class is a composite ILog, it depends on multiple ILog objects, and when it is invoked to log something, it broadcasts such request to all the ILog dependencies.
As you see from the graph, we have a circular dependency. This can be solved by using Property Injection.
We can use Property Injection inside the CompositeLog class like this:
public class CompositeLog : ILog
{
private ILog[] logs;
public ILog[] Logs
{
set
{
if(logs != null)
throw new Exception("The logs dependencies has been set before");
logs = value;
}
}
public void LogInformation(string message)
{
foreach(var log in logs)
log.LogInformation(message);
}
//Other methods here
//...
}
Notice that this class does not take any dependencies on construction. We later can inject its dependencies via the Logs property.
If you are using Pure DI, your Composition Root would look like this:
var compositeLog = new CompositeLog();
Form1 form = new Form1(compositeLog);
compositeLog.Logs = new ILog[]{ form , new Log4NetAdapter()};
I don't know if this can be done with NInject easily. The bindings are probably going to be complex. In such cases where there are multiple implementations of a single interface, I think that DI containers fail. I suggest you use Pure DI.
We are using an in house simple Logger class for our application's logging tasks (.NET 3.5).
The logger code is pretty old, and is designed similarly to this:
public class Logger : ILogger
{
private ILogger instance;
private static ILogger Instance
{
// Initialized on first use.
get { return instance; }
}
public static void Debug(string msg)
{
instance.Debug(msg);
}
public static void Error(string msg)
{
....
}
}
The instance itself is being initialized on first usage (lazily).
This is not a Singleton according to its strict "by the book" implementation, but nonetheless, the access to this class from all calling code is a static access.
I would like, for testing purposes and for other architectural reasons, to be able to replace the internal instance with something else (inject it).
How can i achieve this easily? we are not using any IoC container at the moment, but i would not want to expose a setter to the Instance property since that would defeat the whole Singleton like design.
Any suggestions on how to come up with a solution for this?
Consider using Fakes Framework for testing purposes. You could stub the call to static method with something like this
ShimLogger.Instance = () => new LoggerMock();
In case of .net 3.5 you can use Moles Framework to stub static method call. Configuration code will look something like:
MLogger.Instance = () => new LoggerMock();
It would require to make static method Instance public, but after this configuration every call to static method will return your mocked instance.
Indeed, a setter does not sound like a good choice.
Instead, I would consider two possible approaches. First, an explcit configuration method:
public class Logger : ILogger {
public void ConfigureLogger( ILogger logger ) {
this.instance = logger;
}
}
An advantage of such approach is that the intention is clear plus you have to call this method in an explicit way.
Another option would be to allow one to pass a type of your logger in your configuration:
<appSettings>
<add key="loggerType" value="The.Type.From, Some.Assembly" />
</appSettings>
Then, in your Logger class you rewrite the initialization routine so that if the configuration parameter is present, you prefer the type provided in the configuration OVER the default type.
An advantage of such approach is that you can reconfigure the client with the configuration change with no changes to the code.
Anyway, IoC containers don't bite. Introduce one as it pays off in a long term.
I wouldn't roll your own. I use the Enterprise Library for almost all my logging needs. It works on desktop and asp.net projects. Asp.net can be a bit more problematic since you have to deal with security on the server but I've done it.
http://entlib.codeplex.com/
People also like Log4Net but I've never used it so I can't comment on it.
I would modify the code using the Logger. Instead of accessing the logger through Logger.Instance, pass in the desired instance of the logger into the object. Then in your factories and/or composition root you pass Logger.Instance as the source of the logger in your production code, and in your unit tests it is easy to use a mock logger.
public class Foo
{
private readonly ILogger logger;
public Foo(ILogger logger)
{
if (logger == null)
throw new ArgumentNullException("logger");
this.logger = logger;
}
public void Func()
{
try
{
// do something
}
catch (Exception ex)
{
// call the provided logger dependency
this.logger.WriteError(ex);
// not the static singleton property
Logger.Instance.WriteError(ex);
}
}
}
Another idea would be to make an internal setter for your Instance property and use the InternalsVisibleTo attribute to make the internal setter visible to your test assembly. Note that if the assembly that contains your logger is strong named, then you must specify the PublicKey in the InternalsVisibleTo attribute. Obviously this is most helpful (in the sense of not letting other developers accidentally - or on purpose - setting Instance to something else) if your logger lives in is own assembly or in some kind of infrastructure assembly where most development/logging is NOT taking place.
I was reading about the disadvantages of singleton patterns. A valid use of singleton suggested in many forums is the Logging application. I was wondering why this is a valid use of the pattern. Aren't we maintaing the state information in memory throughout the application?
Why not just use a function:
class Logger
{
public static void Log(string message)
{
//Append to file
}
}
To answer "why not just use a function": this code works incorrectly in multi-thread logging. If two threads try to write the same file, an exception will be thrown. And this is why it's good to use singleton for logging. In this solution, we have a thread safe singleton container, other threads push messages(logs) into the container safely. And the container(always a thread-safe queue) writes the messages/logs into a file/db/etc one by one.
It is better to declare interface:
interface ILogger
{
public void Log(string message);
}
Then implement specific type of logger
class FileLogger : ILogger
{
public void Log(string message)
{
//Append to file
}
}
class EmptyLogger : ILogger
{
public void Log(string message)
{
//Do nothing
}
}
And inject where required. You will inject EmptyLogger in tests. Using singleton will make testing harder, because you'll have to save to file in tests too. If you want to test if class makes correct log entries, you can use mock and define expectations.
About injection:
public class ClassThatUsesLogger
{
private ILogger Logger { get; set; }
public ClassThatUsesLogger(ILogger logger) { Logger = logger }
}
ClassThatUsesLogger takes FileLogger in production code:
classThatUsesLogger = new ClassThatUsesLogger(new FileLogger());
In tests it takes EmptyLogger:
classThatUsesLogger = new ClassThatUsesLogger(new EmptyLogger());
You inject different loggers in different scenarios. There are better ways to handle injections, but you'll have to do some reading.
EDIT
Remember you can still use singleton in your code, as others suggested, but you should hide its usage behind interface to loosen dependency between a class and specific implementation of logging.
I'm not sure what you are referring to when you ask about state information remaining in memory, but one reason to favour singleton over static for logging is that singleton still allows you to both
(1) program to abstractions (ILogger) and
(2) adhere to the dependency inversion principle by practicing dependency injection.
You can't inject your static logging method as a dependency (unless you want to pass something like Action<string> everywhere), but you can pass a singleton object, and you can pass different implementations like NullLogger when writing unit tests.
A singleton logger implementation allows for you to control easily how often your logging is being flushed to disk or the db. If you have multiple instances of the logger then they could all be trying to write at the same time which could cause collisions or performance issues. The singleton allows this to be managed so that you only flush to the store during quiet times and all your messages are kept in order.
In most circumstances the Singleton design pattern is not recommended, because it is a kind of Global State, hides dependencies (making APIs less obvious) and also hard to test.
Logging is not one of those circumstances. This is because logging does not affect the execution of your code. That is, as explained here: http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :
your application does not behave any different whether or not a given
logger is enabled. The information here flows one way: From your
application into the logger.
You probably still don't want to use Singleton pattern though. Not quite at least. This is because there's no reason to force a single instance of a logger. What if you wanted to have two log files, or two loggers that behaved differently and were used for different purposes?
So all you really want for logger is to make it easily accessible from everywhere when you need it. Basically, logging is a special circumstances where the best way to go is to have it globally accessible.
The easy way is to simply have a static field in your application that contains the instance of logger:
public final static LOGGER = new Logger();
Or if your logger is created by a Factory:
public final static LOGGER = new LoggerFactory().getLogger("myLogger");
Or if your logger is created by a DI container:
public final static LOGGER = Container.getInstance("myLogger");
You could make your logger implementation be configurable, either through a config file, that you can set to "mode = test" when you are doing testing, so that the logger in those cases can behave accordingly, either not logging, or logging to the console.
public final static LOGGER = new Logger("logConfig.cfg");
You could also make the logger's behavior be configurable at runtime. So when running tests you can simply set it up as such: LOGGER.setMode("test");
Or if you don't make the static final, you can simply replace the static LOGGER with a test logger or mocked logger in the setup of your test.
Something slightly fancier you can do that is close to a Singleton pattern but not quite is:
public class Logger
{
private static Logger default;
public static getDefault()
{
if(default == null)
{
throw new RuntimeException("No default logger was specified.");
}
return default;
}
public static void setDefault(Logger logger)
{
if(default != null)
{
throw new RuntimeException("Default logger already specified.");
}
default = logger;
}
public Logger()
{
}
}
public static void main(String [] args)
{
Logger.setDefault(new Logger());
}
#Test
public void myTest()
{
Logger.setDefault(new MockedLogger());
// ... test stuff
}