Logging is one of the most important part of an application, the ability to detect cause of failures or malfunctions from the log is invaluable, but generating good logs is not easy.

The fundamental step to obtain a good logging infrastructure, is to use one of the libraries available in the open source world, and probably you will end using log4net  because it is really good. One of the most important characteristic of a good infrastructure for logging is the ability to change the destination of logs without recompiling, and the possibility to extend the basic logging facility with custom destination storage.

One of the most useful appender is the AdoNet one, that permits you to store logs into database, then you can query them with the full power of SQL, even from a remote machine. This is really good but sometime testers call me telling “hey, when I do this and that the page raise an error” so I need to connect to log database, then look into the log trying to see what’s happened…  this is good but not enough, I need a live logger, a way to attach to a remote process and look at the logs in realtime, in this way I can tell to the tester, please do again all the action that lead to the bug, while I’m watching at all the logging that are generated. log4Net has some network appender, the UDPAppender but I need to connect to a internet machine, and I want the log to travel in the network only when there is attached client.

With this need in mind I proceed to create a Wcf appender for log4net using a publish subscribe technique, here is the two contract interfaces.

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IL4NClient))] public interface IL4NServer { [OperationContract] Boolean Register(); } [ServiceContract] public interface IL4NClient { [OperationContract(IsOneWay = true)] void ReceiveLog(LogMessage log); }

For those not familiar with wcf the CallbackContract is a technique for the server to obtain a callback to sent one way messages to clients. Here it is a simple server that uses these two interfaces

public class AppenderService : IL4NServer { #region IL4NServer Members private static List<IL4NClient> clients = new List<IL4NClient>(); bool IL4NServer.Register() { lock (clients) clients.Add(OperationContext.Current.GetCallbackChannel<IL4NClient>()); return true; } #endregion /// <summary> /// Called by the appender, it append a log. /// </summary> /// <param name="loggingEvent"></param> public static void Append(LoggingEvent loggingEvent) { LogMessage message = new LogMessage() {Message = loggingEvent.MessageObject.ToString()}; lock (clients) { for (Int32 I = clients.Count - 1; I >= 0; --I) { try { clients[I].ReceiveLog(message); } catch (Exception) { clients.RemoveAt(I); } } } } }

thanks to the OperationContext.Current.GetCallbackChannel, the server is able to obtain an instance to a proxy that can be used to communicate back to the client through the IL2NClient interface. For each client that calls Register(), the server add the client callback to a inner static list. The Append() static method can be called from everywhere to send a log to all registered clients. As you can see, the code cycle trough all registered clients, and for each clients that generates exception that client is removed from the list.

Now I can create a very simple appender for Log4Net

public class WCFAppender : AppenderSkeleton { private ServiceHost host; public WCFAppender() { try { host = new ServiceHost(typeof(AppenderService)); host.Open(); } catch (Exception ex) { throw; } } protected override void Append(LoggingEvent loggingEvent) { AppenderService.Append(loggingEvent); } override protected bool RequiresLayout { get { return true; } } protected override void OnClose() { host.Close(); base.OnClose(); } #endregion Override implementation of AppenderSkeleton }

This appender is really simple, it host a communication foundation server for the AppenderService in his constructor and in Append() method it simply called static Append() method of the AppenderService.

With this simple class I can now configure an application to use this custom appender, and then you can connect to it and intercept all the log from another application, creating a live logging listener. Here is a screenshot of a running demo

image

As you can see I create a simple windows form with a button to generate warn, then I can open a simple console application that connects and listen to all generated logs. You can attach how many listener program as you want.

A complete example can be found here http://nablasoft.googlecode.com/svn/trunk/Log4NetLive.

alk.

Tags:

DotNetKicks Image

10 Responses to “A custom publish/subscriber appender for log4net”

  1. i like this idea, and i downloaded your code.
    i cannot get it to work; as i saw you included it in an windows app, therefore your service creates its own host.

    i want to include the contract/service in a web-app, and at the moment i’m stuck because it seems that there is a problem with the protocols.

  2. If you have IIS6 or IIS5 you can host only services based on HttpBinding, and the appender will not work. If you have IIS7 you can host services with different types of binding, but I do not know if this would work. With web application I used Elmah to keep track of errors, and a database appender. If I have a little bit of spare time I’ll try to make it work on IIS7 for windows application.

  3. So I am looking for a WCF hook for Log4net, I found some other web site, nice little zip file to download everything including a tester / demo. Problem is it does not work well. Well it crashes pretty bad. I hack the daylights out of it and get it to work. But I don’t feel good about hacking the daylights out of it to make it work. So I look for anyone else who has done this and I find yours. And I think, oh joy. Another example.

    Then I click the link to look at the code, oh no its just a directory tree. Oh but it is this subversion stuff I have been hearing about. Of course I have not used svn yet, because I have not needed to. Why … because I am tired of spending countless hours to learn every little thing just to have it replaced next month. Yes I have turned into my parents. I am on my 7th or 8th cell phone and am thinking I just want a plain simple phone where I can keep contacts. Why? Because I am tired of learning new crap I dont need.

    So I download SVN and install it. I get the command line version because the Windows version wants you to register so they can fill yor email with junk. Well maybe they wont but I am so sick and tired of registering every time I want to fart. Directions? Oh sure just type help and it will be obvious … yeah right … for the time I spend trying to find a good example I can write my own.

    Sign me tired and annoyed. Now to UNINSTALL svn, because it is anything but obvious on how to get http://nablasoft.googlecode.co.....og4NetLive

    svn update http://nablasoft.googlecode.co.....og4NetLive
    does not work, I dont have a day or 2 to research how to use that damn tool.

    So your WCF appender might be wonderful, but I will never know.

    Sorry for the rant, just frustrated at another wasted hour.

  4. Sorry to heard this, I suggest you to install tortoiseSVN, it integrates with windows explorer, all you need to do is right click on a folder, choose tortoise svn and then the command repo browser.

    Insert the link to the source code http://nablasoft.googlecode.co.....og4NetLive then you can browse the repository, now if you right click on the tree structure on the left, you can simply choose checkout and copy all data on a local folder.

    alk.

  5. Thank you for the explanation and implementation, very cool. Would you please clarify the licensing of this work.

  6. Well, It is a little snippet, there is no licensing ;), do everything you want to do with it :)

    Alk.

  7. great post, thanks a lot .. especially for the SVC-checkout-how to!

    cheers
    Christian

  8. Hah am I actually the only comment to your awesome read?

  9. Very interesting read. Truely.

Trackbacks/Pingbacks

  1. Live logging with log4net and WCF part 2