Question

The Semantic Logging Application Block (SLAB) is very appealing to me, and I wish to use it in a large, composite application I am writing. To use it, one writes a class derived from 'EventSource', and includes one method in the class for each event they want to log as a typed event, vs. a simple string.

An application such as mine could have hundreds of such events. I could have an 'EventSource' based class with just one event, "SomethingHappened", and log everything through that, at the one extreme end of the effort and accuracy spectrum, and I could have one event for every operation I perform.

It strikes me as a good idea to have EventSource derivatives for different functional areas. The app has little to know business logic itself; that is all provided by MEF plugin modules, so I could have event sources for bootsrapping, security, config changes etc. and any plugin module can define an event source for whatever events it wants to log.

Is this a good strategy, or are many EventSource derived loggers an undesirable app feature?

Was it helpful?

Solution

From your question

... I wish to use it in a large, composite application I am writing...

I can deduce that large is meant in the context of a single developer. In that case you can derive from EventSource and add all events you possibly could want into that class. It does not make much sense to create an extra EventSource derived class for every part of your composite application since it would pollute the eventsource registration database where already 2K of providers are registered. Besides that it would make it hard to enable logging for your application if you need to remember 20 guids you need to enable to follow your application logic through several layers.

A compromise would be to define in your EventSource class some generic event like

public void WriteViolation(string Subsystem, string Message, string Context)

where you have in your components a logger class for each component

public static class NetworkLogger
{
   public static void Violation(string message)
   {
      GenericSource.Instance.Violation("Network", message, NetworkContext.Current);
   }
}

public static class DatabaseLogger
{
  public static void Violation(string message)
  {
      GenericSource.Instance.Violation("Database", message, DBContext.Current);
  }
}

That way you can keep the loggers component specific and you can add e.g. automatically contextual information to the generic event when necesssary. Another approach is to use in your application tracing where your trace method enter/leave, info, warning, error and your EventSource derived class knows only these events. When you add for every trace entry the type name + method name you can filter by namespaces and group by classes in WPA to see what you were doing. An example is shown in Semantic Tracing For .NET 4.0. For a large application you can check out on your machine the file

C:\Windows\Microsoft.NET\Framework\v4.0.30319\CLR-ETW.man

You can open it with ecmangen.exe from Windows SDK to get a nice GUI to see how the events are structured. .NET has only two Event Providers defined. The many events are grouped via keywords to enable specific aspects of .NET e.g. GC, Loader, Exceptions, .... This is important since you can pass while you enable a provider specific keywords to it to enable only some events of a large provider.

You can also check out Microsoft.Windows.ApplicationServer.Applications.45.man to find out how the Workflow guys think about ETW events. That should help to find your own way. It is not so much about how exactly you structure your events since the real test is finding production bugs at customer sites. The probability is high that you need to take several iterations until you have found the right balance to log/trace relevant information that helps you to diagnose failures in the field.

OTHER TIPS

This is a bit of handwaving as its too long for a comment. But how about templating and then a factory service?

This then doesn't change and you bind everything up on application start and after loading plugins.

interface IReportable
{
    void Report(object param);
}

interface IKernel
{
    T Get<T>();
}

class EventSource2 : EventSource
{
    private IKernel _factory;

    public EventSource2(IKernel factory)
    {
        _factory = factory;
    }

    public void Report<TReportable>(object param = null) where TReportable : IReportable
    {
        var reportable = _factory.Get<TReportable>();

        reportable.Report(param);

        //... Do what you want to do with EventSource
    }
}

Group Events logically into a different smaller provider (EventSource classes) and not into 1 large file.

This has the advantage that you can enable the Events only for providers that you care in special cases.

Don't think of the EventSource as a listing of every possible log event you could possibly perform in your application. Remember there are ways to filter your events by using Keywords and Verbosity/event levels. You can even drill down further and use OpCodes and Tasks. Version 1.1 of the SLAB supports ActivityID and RelatedActivityID. Version 2.0 (https://slab.codeplex.com/wikipage?title=SLAB2.0ReleaseNotes&version=2) released earlier this week now supports process and thread id.

To give you an example, I have a very small EventSource derived class and have methods for StartLog, LogStatus, StopLogging, LogError, LogDebug and CreateDump with the first three using the same event level but different event ids due to differences in formatting and the remaining ones use different event levels so I don't debug or create dumps unless I dynamically enable it with a configuration file setting. The point is I can use the same methods from an asp.net site as well as class libraries or console apps. Don't forget this only defines the logging events. You still have to have a sink subscribe to the event, giving you more possibilities. You could have debug messages go to a file and error messages go to a database and/or email. The possibilities are endless.

One last thing. I thought I painted myself into a corner when I did my testing and found multiple assemblies were logging to the same file because they were using the same event methods (and therefore the same event id, keyword, event level, etc). I modified my code to pass the calling assembly name which is now used om the filter process when determining if a log message should be written (from the config file setting) and where (to a log file based on the assembly name). Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top