LogManager.GetLogger and xmlconfigurator.configure sequence - c#

I have custom xml file with the log4net configurations. Below code is used for configuring the log4net. It is working fine.
The question is how does LogManager.Getlogger gets the "MyLogger" when it doesnt know the config file details?
Should we maintain any sequence while calling LogManager.GetLogger and xmlconfigurator.configure ?
Class LoggerClass
{
private static readonly ILog fileLogger = LogManager.GetLogger("MyLogger");
public LoggerClass()
{
FileInfo logConfiguration = new FileInfo("ConfigFile.xml");
//Loading the configuration from the xml file.
XmlConfigurator.Configure(logConfiguration);
}
public void Log(string msg)
{
fileLogger.Log(......);
}
}

LogManager class has static methods that are used by a client to request a logger instance. The GetLogger method is used to retrieve a logger.
The GetLogger method return the object of type ILog which contains methods for logging at different levels and also has properties for determining if those logging levels are enabled in the current configuration.
And about the sequence, the invocation of the Xmlconfigurator.Configure() method sets up the logging functionality, hence before writing any log, the log4net library must be set up using this command Xmlconfigurator.Configure().
And the sequence of calling LogManager.GetLogger() and Xmlconfigurator.Configure() does not matter. Just make sure that before any logging, you have initialized the logger using LogManager.GetLogger() method and have set up the logger using Xmlconfigurator.Configure().
Reference: log4net documentaion
I hope, this helps and answers your question :)

Usually XmlConfigurator.configure() is done at application start in global.asax in ASP.NET application, so that all classes in application get log4net configurations.
//Global.asax
void Application_Start(object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
}

Related

How to properly implement ILogger.IsEnabled() in custom logger in ASP.NET Core MVC

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);
}
}

Log4Net message construction cost when not debugging

According to this log4net article you should check if debug is enabled prior to any Log.Debug statements to eliminiate the statement construction cost. Is there a better alternative to always having to check if(Log.IsDebugEnabled) prior to any log statements?
Log4Net example:
if (log.IsDebugEnabled)
{
log.Debug("This is entry number: " + i );
}
I don't want to pay the overhead cost of statement construction, but also don't want to check prior to every log statement.
#Grhm and #David have good ideas, but I don't think that David's wrapper is as good as it could be. Wrapping log4net that way. Simply implementing Debug, Info, etc on the wrapper and delegating those down to log4net's Debug, Info, etc methods break log4net's ability to log the call site information. If you wrap this way and tell log4net to log the call site info, log4net will write out the call site in the wrapper, not the call site in your actual code, which is what you want.
I personally don't like using a singleton logger as you lose the ability to tweak logging levels in different parts of your program. If you are working on several components, you might want Info level logging turned on for one component, but only Warn logging (or none at all) for other components. With a singleton logger, all logging in all of your application will be at the same level.
You are denying yourself a lot of log4net's built in (and powerful) capabilities when you wrap log4net incorrectly and when you use a single logger to cover your entire application.
I answered a similar question (about maintaining call site information) here:
how to log method name when using wrapper class with Log4net
To save time, I have included a code example here (uncompiled and untested, but should be close)...
public class MyLog4NetWrapper
{
ILog log;
public MyLog4NetWrapper(string loggerName)
{
log = LogManager.GetLogger(loggerName)
}
public MyLog4NetWrapper(type loggerType)
{
log = LogManager.GetLogger(loggerType)
}
public void Info(string message)
{
if (log.IsInfoEnabled) log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, message, null);
}
//Defer expensive calculations unless logging is enabled - thanks Grhm for the example
public void Info(Func<string> formattingCallback )
{
if(log.IsInfoEnabled)
{
log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, formattingCallback(), null);
}
}
//Debug, Warn, Trace, etc are left as an exercise.
}
You can create these loggers in your code like this:
public class MyClass
{
private static readonly ILog log = new MyLoggerWrapper(typeof(MyClass));
public void DoSomething()
{
log.Info("Hello world!");
}
}
The trick to writing a log4net wrapper that preserves the call site information is to use the Log method and to pass the type of your wrapper as the first parameter.
If you are going to write a wrapper in order to implement the functionality that you asked about (deferring execution of any expensive code in the logging call without explicitly checking to see if the desired logging level is enabled), then you might as well put that code in the wrapper rather than implement it as an extension method (which will also suffer from the same loss of call site problem I described above).
Good luck!
The easiest and cleanest way might be the use of the DebugFormat method which actually does the check if the debug level is enabled (see Github-Code of log4net).
but also don't want to check prior to every log statement
When you find yourself repeating the same code over and over, it sounds like a common abstraction may be in order. In this case you can, for example, create a custom wrapper for Log4Net. Something as simple as:
public static class Logger
{
private static ILog _log;
static Logger()
{
log4net.Config.XmlConfigurator.Configure();
_log = log4net.LogManager.GetLogger("Log4Net");
}
public static void Debug(string message)
{
if (_log.IsDebugEnabled)
_log.Debug(message);
}
public static void Info(string message)
{
_log.Info(message);
}
public static void Warn(string message)
{
_log.Warn(message);
}
public static void Error(string message)
{
_log.Error(message);
}
public static void Error(string message, Exception ex)
{
_log.Error(message, ex);
}
public static void Fatal(string message)
{
_log.Fatal(message);
}
public static void Fatal(string message, Exception ex)
{
_log.Fatal(message, ex);
}
}
In this case I made the logger instance static. I'm not 100% sure that will always work as expected. Normally I use this behind a dependency injection framework and configure the logger dependency to be a singleton, handled by the framework. You might instead make this an instance class with instance methods and put it behind a static factory class instead. Test and tweak as necessary.
There are a couple of added benefits here:
Your dependency in Log4Net is isolated to a single class. So if you ever want to use a different logger, you only have to change one class instead of everything in the entire project.
You can easily abstract this behind a dependency injector.
Any other common functionality you want to include in all logging statements can be easily and globally included here.
An example I commonly use for the third benefit might be something like this:
private static string GetLocation()
{
var frame = new StackTrace(1).GetFrame(1);
var method = frame.GetMethod();
return string.Format("{0}:{1}.{2}({3})", Environment.MachineName, method.ReflectedType.FullName, method.Name, frame.GetFileLineNumber().ToString());
}
This gets more meaningful debugging information from the runtime system (though there may be a performance hit, for high-volume systems it's worth testing). So my pass-through error logging function might look like this:
public void Error(string message, Exception ex)
{
_log.Error(string.Format("{0}:{1}", GetLocation(), message), ex);
}
You could use a lambda expression. Like:
log.Debug(() => "This is entry number:" + i);
That way the lambda is only evaluated after the .IsDebugEnabled call.
We have an extension class defined (taken from http://www.beefycode.com/post/Extension-Methods-for-Deferred-Message-Formatting-in-Log4Net.aspx) that has extension methods like:
public static class Log4NetExtensionMethods
{
public static void Debug( this ILog log, Func<string> formattingCallback )
{
if( log.IsDebugEnabled )
{
log.Debug( formattingCallback() );
}
}
// .. other methods as required...
}
I'm not sure if log4net have added lamda type support in more recent releases - but this has been working for me.
If you include the namespace log4net.Util, you are able to call the following extension methods on log4net ILog:
public static void ErrorExt(this ILog logger, Func<object> callback)
This will only call the lambda function when logging error level is enabled. No need to write your own extension methods. It also protects from creating an error while constructing the actual log message by wrapping the creation in a try catch method.
I would look at preprocessor (precompile?) directives.
#if DEBUG
{your logging code here}
#endif
Something like that should do it for you, and then the code only gets compiled in Debug Mode.
You can also use the the Conditional attribute on a method like this:
[System.Diagnostics.Conditional("DEBUG")]
private void YourMethodNameHere(YourMethodSignatureHere)
Take a look at this old question for more information on when/why/how you might use them.
http://stackoverflow.com/questions/3788605/if-debug-vs-conditionaldebug

DI/IoC with a Singleton Logger class

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.

Why use singleton for application logging?

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
}

How to disable creation of empty log file on app start?

I have configured log4net in my app successfully but one thing is a little bit annoying for me.
The log file is created (empty) after my app start even if no error occurs. I would like to log file be created only after some error.
I actually found a way to do this in this thread:
http://www.l4ndash.com/Log4NetMailArchive/tabid/70/forumid/1/postid/18271/view/topic/Default.aspx
I've tested the first method and it works. Just in case that link is not longer good I'll reproduce the code here. Basically the author states that there are two ways of doing this.
First way:
Create a new locking model that only acquires a lock (and creates the file) if the appropriate threshold for that logger works.
public class MyLock : log4net.Appender.FileAppender.MinimalLock
{
public override Stream AcquireLock()
{
if (CurrentAppender.Threshold == log4net.Core.Level.Off)
return null;
return base.AcquireLock();
}
}
Now in the config file, set the threshold to start out as:
<threshold value="OFF" />
and make sure you set this new LockingModel as you model:
<lockingModel type="Namespace.MyLock" />
I'm using this with a rolling file appender.
The second method is listed at the link. I haven't tried this technique but it seems to be technically sound.
I know this is an old question but I think this can be useful for someone else.
We came across a similar situation where it was required that the application shouldn't leave an empty log file if no errors occurred.
We solved it by creating the following custom LockingModel class:
public class MinimalLockDeleteEmpty : FileAppender.MinimalLock
{
public override void ReleaseLock()
{
base.ReleaseLock();
var logFile = new FileInfo(CurrentAppender.File);
if (logFile.Exists && logFile.Length <= 0)
{
logFile.Delete();
}
}
}
It is derived from the FileAppender.MinimalLock class that will release the lock on the log file after writing each log message.
We added extra functionality that will delete the log file if it is still empty after the lock is released. It prevents the application from leaving empty error log files if the applications runs and exits without any errors.
Pros
It will still create an empty log file during the configuration phase of Log4Net, ensuring that logging is working before the rest of the app starts. However, the log file is deleted immediately.
It doesn't require you to turn off logging in your config file by setting threshold value to "OFF" and than, later on, turn on logging programmatically before writing your first log event.
Cons
This is most likely a slow method of managing your log files because the ReleaseLock method, and the check on the file length, will be called after every log event that is written to the log file. Only use it when you expect to have very few errors and it is a business requirement that the log file shouldn't exist when there are no errors.
The log files are created and deleted when empty. This might be a problem if you have other tools monitoring the log directory for file system changes. However, this was not a problem in our situation.
The following worked for me.The first call to OpenFile() occurs when the logger is configured. Subsequent calls are when actual log message is generated.
class CustomFileAppender : RollingFileAppender
{
private bool isFirstTime = true;
protected override void OpenFile(string fileName, bool append)
{
if (isFirstTime)
{
isFirstTime = false;
return;
}
base.OpenFile(fileName, append);
}
}
And in the config file, change the appender
<log4net>
<appender name="RollingFile" type="<your namespace>.CustomFileAppender">
...
</log4net>
The sequence from log4Net source is as below:
The first call to OpenFile() is because of ActivateOptions() called from FileAppender's constructor.
When log message is generated, AppenderSkeleton's DoAppend() calls PreAppendCheck()
PreAppendCheck() is overridden in TextWriterAppender, the base of FileAppender.
The overridden PreAppendCheck() calls virtual PrepareWriter if the file is not yet open.
PrepareWriter() of FileAppender calls SafeOpenFile() which inturn calls OpenFile()
The problem with that approach is that then if the file exists but is read-only, or is in a directory which doesn't exist etc, you won't find out until another error is already causing problems. You really want to be confident that logging is working before the rest of the app starts.
There may be a way of doing this anyway, but if not I suspect that this is the reason.
Another method that is quite simple is described in this message of the mailing list archive
Basically, with log4net, the log file is created when the logger is configured. To configure it to do otherwise is a bit hacky. The solution is to defer the execution of the configuration. The message above suggests doing the following when setting up the logger:
private static ILog _log = LogManager.GetLogger(typeof(Program));
public static ILog Log
{
get
{
if(!log4net.LogManager.GetRepository().Configured)
log4net.Config.XmlConfigurator.Configure(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
return _log;
}
}
I usually configure log4net with the assembly attribute, which configures the logger automatically (thus creating the log file), and a simple getter for the log:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
...
public static log4net.ILog Log { get { return _log; } }
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
But removing that and adding in the above getter with the additional logic instead solved the problem for me.
Note: in general I agree that in most cases it would be best to configure the logger and create the file (and even write to it) on application startup.
AcquireLock and ReleaseLock method worked for me, but it bothered me that the file was created/deleted that many times. Here is another similar option that shuts down the logger and deletes the empty logfile when the program completed. Just call RemoveEmptyLogFile when you are done logging.
/// <summary>
/// Sets the logging level for log4net.
/// </summary>
private static void RemoveEmptyLogFile()
{
//Get the logfilename before we shut it down
log4net.Appender.FileAppender rootAppender = (log4net.Appender.FileAppender)((log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository()).Root.Appenders[0];
string filename = rootAppender.File;
//Shut down all of the repositories to release lock on logfile
log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories();
foreach (log4net.Repository.ILoggerRepository repository in repositories)
{
repository.Shutdown();
}
//Delete log file if it's empty
var f = new FileInfo(filename);
if (f.Exists && f.Length <= 0)
{
f.Delete();
}
} // RemoveEmptyLogFile
private static ILog _log = LogManager.GetLogger(typeof(Program));
public static ILog Log
{
get
{
if(!log4net.LogManager.GetRepository().Configured)
log4net.Config.XmlConfigurator.Configure(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
return _log;
}
}

Categories

Resources