views:

4026

answers:

6

Greetings,

I have been looking for a logging framework for .net (c#) and decided to give log4net a go after reading up on a few question/answer threads here on stackoverflow. I see people mentioning over and over that they use a wrapper class for log4net and I am wonder what that would look like.

I have my code split up into different projects (data access/business/webservice/..). How would a log4net wrapper class look like? Would the wrapper class need to be included in all of the projects? Should I build it as a separate project all together?

Should the wrapper be a singleton class?

A: 

My understanding is that a wrapper class for log4net would be a static class which takes care of initializing the logging object from app.config/web.config or by code (e.g. integration with NUnit).

devio
A: 

a possible use for a log4net wrapper could be a class that gets the calling class and method via reflection to get an idea of where your logging entry happened. at least i use this frequently.

Joachim Kerschbaumer
+24  A: 

Essentially you create an interface and then a concrete implementation of that interface that wraps the classes and methods of Log4net directly. Additional logging systems can be wrapped by creating more concrete classes which wrap other classes and methods of those systems. Finally use a factory to create instances of your wrappers based on a configuration setting or line of code change. (Note: you can get more flexible - and complex - using an Inversion of Control container such as StructureMap.)

public interface ILogger
{
    void Debug(object message);
    bool IsDebugEnabled { get; }

    // continue for all methods like Error, Fatal ...
}

public class Log4NetWrapper : ILogger
{
    private readonly log4net.ILog _logger;

    public Log4NetWrapper(Type type)
    {
        _logger = log4net.LogManager.GetLogger(type);
    }

    public void Debug(object message)
    {
        _logger.Debug(message);
    }

    public bool IsDebugEnabled
    {
        get { return _logger.IsDebugEnabled; }
    }

    // complete ILogger interface implementation
}

public static class LogManager
{
    public static ILogger GetLogger(Type type)
    {
        // if configuration file says log4net...
        return new Log4NetWrapper(type);
        // if it says Joe's Logger...
        // return new JoesLoggerWrapper(type);
    }
}

And an example of using this code in your classes (declared as a static readonly field):

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

You can get the same slightly more performance friendly effect using:

private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

The former example is considered more maintainable.

You would not want to create a Singleton to handle all logging because Log4Net logs for the invoking type; its much cleaner and useful to have each type use its own logger rather than just seeing a single type in the log file reporting all messages.

Because your implementation should be fairly reusable (other projects in your organization) you could make it its own assembly or ideally include it with your own personal/organization's framework/utility assembly. Do not re-declare the classes separately in each of your business/data/UI assemblies, that's not maintainable.

cfeduke
I was going to say that you don't wan't to have a single logger, but this is more of a logging factory which is what you want.Each class should have it's own logger, because it helps with logging reflection.
Omar Kooheji
+2  A: 

What benefits are you planning on getting out of writing a wrapper for log4net. I'd recommend getting comfortable with the log4net classes first before writing a wrapper around them. cfeduke is right in his answer on how to write said wrapper, but unless you need to add actual functionality to his example a wrapper would only succeed in slowing the logging process down and adding complexity for future maintainers. This especially true when refactoring tools available in .Net make such changes super easy.

+13  A: 

Assuming you were going with something like cfeduke's answer above, you could also add an overload to your LogManager like this:

public static ILog GetLogger()
{
    var stack = new StackTrace();
    var frame = stack.GetFrame(1);
    return new Log4NetWrapper(frame.GetMethod().DeclaringType);
}

That way in your code you can now just use:

private static readonly ILogger _logger = LogManager.GetLogger();

instead of either of these:

private static readonly ILogger _logger =
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILogger _logger = 
    LogManager.GetLogger(typeof(YourTypeName));

Which is effectively equivalent of the first alternative (i.e. the one that uses MethodBase.GetCurrentMethod().DeclaringType), only a little simpler.

Alconja
A: 

Alconja, I like your idea of using the stacktrace to jump back to the calling method. I was thinking of further encapsulating the calls, to not just retrieve the logger object, but to perform actually perform the logging. What I want is a static class that handles the logging, by abstracting from the specific implementation used. I.e.

LoggingService.LogError("my error message");

That way I only need to change the internals of the static class, if I later decide to user another logging system.

So I used your idea to get the calling object using the stack trace :

public static class LoggingService
{
    private static ILog GetLogger()
    {    
        var stack = new StackTrace();    
        var frame = stack.GetFrame(2);    
        return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType);
    }

    public static void LogError(string message)
    {
        ILog logger = GetLogger();
        if (logger.IsErrorEnabled)
            logger.Error(message);
    }
    ...
}

Does anybody see a problem with this approach?

I think your code sample misses the point of `IsErrorEnabled` (and related properties). These exist to allow you to avoid the run-time cost of building the string that gets passed to the corresponding log method. Since you have wrapped all of this code in your `LogError` method you lose this benefit. For more details see http://log4net.sourceforge.net/release/1.2.0.30316/doc/manual/faq.html#fastLogging
Richard Ev