views:

826

answers:

6

My solutions has several projects which includes several libraries and one project for UI. Currently it is a windows forms application and I use log4net for logging. This UI project has only reference to log4net and this project maintains the configuration files. But I would like to log from my libraries as well.

Usual method for doing this is to wrap the logging calls behind an interface. Create a common project something called utilities and add this interface to this project. Now this project can be used in all the projects and can use this interface for logging.

I am thinking about an alternative design which involves passing delegates and reducing coupling and avoiding unnecessary interfaces.

Consider following class is one from my library.

public sealed class Foo
{
    Action<string> log;
    Action<string, Exception> logException;

    public Foo(Action<string> log, Action<string,Exception> logException)
    {
        this.log = log;
        this.logException = logException;
    }

    public void Work()
    {
        WL("Starting work");
        WL("Completed step1");
        .........
    }

    void WL(string message)
    {
        if(log != null) log(message);
    }

    void WL(string message, Exception exception)
    {
        if(logException != null) logException(message, exception);
    }
}

Now from the calling code, I can easily pass the logging method. Something like

Foo foo = new Foo(message => Console.WriteLine(message), 
                (message, exception) => Console.WriteLine("{0},{1}", message, exception));
foo.Work();

Used a console for explaining, in reality I will use the logging code here.

1 - Do you think this as a better solution? I think this is better as this is more loosely coupled.

2 - Is there any other better solutions available?

This is the only related question I have found here

Any thoughts...?

A: 

Unless you have different pieces of code using different logging frameworks, I'd have a singleton LogDispatcher or something similar that all code which would try and log would call into, perhaps passing in a message level to determine the correct logging method. This prevents the delegates for logging from needing to be passed around the entire codebase, and centralizes all of the code which is responsible for the logging policy.

Another approach is to use a framework like Log4Net. Even if you don't end up using it, their design is a good one to base your own logging on.

Yuliy
+1  A: 

Quoting Jon S. "Simple is almost always better than clever" - IMHO your use of delegates looks more of the latter.

If you want the library projects to log, they should setup-and-use their own logger. I'd not ask clients to pass in a logger (object or interface) - which then travels all the way deep down the type dependency graph. It just pollutes the interface a bit with unnecessary logger object/interface/delegate etc. parameters.

If you're using Log4XXX frameworks, I believe it emphasises the concept of "hierarchical logging architecture" (the names they come up with in s/w ;), where each type/class can maintain and write to its own log file. If the ctor of Foo creates a logger internally, I'd like that. And since it is configurable, specific clients may change the configuration files to redirect the output elsewhere too.

Gishu
Good suggestion. I like hierarchical logging. But again all project requires log4net reference, right? Is that ok?
Appu
Not necessarily.. Use a Dependency Injection framework like Spring.Net to inject the logger dependency - you can change an xml file to change the type of logger and you don't need to reference a particular dependency in your project.
Gishu
+4  A: 

Don't use delegates if there are multiple signatures flying in close formation. All you're doing is avoiding defining classes and interfaces that would be meaningful. log4net provides an ILog interface which is an excellent example of a simple wrapper interface you can pass in.

If you're going to use a logging framework, especially log4net, don't wrap it and don't create a single global (static OR singleton) entry point. I've written about this before, and you may be interested in the question about best practices as well.

Jeffrey Hantin
+2  A: 

I have a thin layer that exposes a logging API very similar to Log4Net, that uses a prov-der-model design pattern to allow you to plug in any suitable logging framework. I've implemented providers for:

  • System.Diagnostics.Trace

  • log4net

  • EntLib

This means I can use logging throughout all my apps without any direct dependency on a specific logging framework, and users of my components can plug in their own favorite logging framework.

Joe
sounds like a very good approach (+1)
Juri
+1  A: 

Google for "AOP logging".

Here's some chat about this from Ayende.

Arnis L.
I like the AOP for logging
Juri
+2  A: 

My advice is to add a reference to log4net to all your projects but leave the logger configuration in the UI project. This still leaves you with the flexibility to define different logging levels on a per assembly basis. Logging is such a low level activity and log4net is such a mature product that I wouldn't spend any time trying to come up with a clever solution just to satisfy "best practices". I might even argue, over a beer or two, that referencing log4net is no different than referencing System.Core.

Jamie Ide