Application logging is the kind of software problem that has been solved so many times, in so many ways. It makes it really hard to choose THE right framework to use when starting a new project. Sometimes it almost feel like coding your own framework will be a good idea (Please don’t do that). The first thing you should ask yourself is what are your requirements. Try to think about what will the development, testing, and debugging experience be like. How will you search through your logs to find relevant information?

Here’s the goals I’m usually trying to aim for:

  1. Semantic logging
  2. Static logging entry point
  3. Flexible and configurable sinks
  4. Correlation between log events
  5. Never fails
  6. Footprint must be as small as possible

Now let’s explore them more in details and I’ll show you how they can be implemented using .net EventSource framework. EventSource is my personal favorite logging framework at the moment, it's really simple and flexible. Don’t get me wrong though, there’s a lot of other great logging framework out there, but this one met all my goals.

1. Semantic Logging

Semantic what? What’s that sorcery. Well, in most application when it comes to logging you’ll see something like Logger.Warn(“message”) or Logger.Error(new Exception()). The problem with that approach is that it doesn’t capture any context with the error or the trace. In real life diagnosing a problem with only an error message or a stack trace is pretty much useless as you need to guess what exactly happened and what were the values at that point.

Semantic Logging tries to solve that problem by allowing you to log anything you want in a given context.

public ActionResult SomeControllerMethod(int productId, string catalogName)
{
    var product = this._productRepository.Get(productId, catalogName);
    if(product == null)
    {
        MyEventSource.Log.ProductNotFoundInCatalog(productId, catalogName);
    }
}

With semantic logging you have all the required informations to reproduce and investigate the problem which makes that log entry way more explicit. Some will say that you can do the same thing by formatting a string and by including all the variables. Well, they are not totally right, semantic logging pushed that approach a bit further as the variables are included with the message as a payload that has a strongly typed schema.

Now your question is probably what the MyEventSource looks like. Here it is.

[EventSource(Name = "MyEventSource")]
public sealed class MyEventSource: EventSource
{
    public static MyEventSource Log = new MyEventSource();

    [Event(1, Level = EventLevel.Error, Message = "Failed to get product with id {0} in catalog {1}")]
    public void ProductNotFoundInCatalog(int productId, string catalog)
    {
        this.WriteEvent(1, productId, catalog);
    }
}

2. Static Logging Entry Point

One great debate about logging is

Should my logger be static or injected through my IOC container?

Let’s compare some pros and cons

Static

  • Accessibility: You can use it wherever you want
  • Flexibility: Only one instance of the logger could be problematic if you want to use different configurations in different parts of your application
  • Test: Really hard to use in tests. Specially if your tests run in parallel as you can only have one configuration at a given time and changing it could create a race condition between tests

Injected

  • Accessibility: First of all, if you want to use it you need to injected it with your IOC container. This could be really problematic in static methods where you want to add logging. Moreover, it will add an extra parameter to all constructors or methods where you want to log
  • Flexibility: Really easy to replace your logger as long as you have a good common interface
  • Test: Really nice for testing. You can either inject a fake logger to record behavior or simply inject a dummy one and don’t do anything
    So, why not have the best of both world. In fact this is quite easy to achieve. Simply, decouple your logging entry point from the logic which write to your log store. In case of EventSource they are called EventSource and sinks, other frameworks use the terms logger and appenders. Consider the entry point as an event stream provider and sinks as stream consumers.

A static entry point will give you the benefit to log wherever you want with a minimal footprint in your code and a set customizable sinks will give you the flexibility to filter the event stream to log exactly what you want in the format you want.

3. Flexible and configurable sinks

As you can understand now, sinks play a key role in a good logging framework. It’s really important that they are flexible enough to allow filtering and output customization. It’s where semantic logging really shows its strength. As every parameter is passed as a payload to the sink, the filtering can be much more complex and flexible. I.e you could create a sink that only logs an entry when a specified value is over a specified threshold.

4. Correlation between log events

In a distributed computing world correlation between log events is a must. Even if an operation pass through many servers and over many networks you should be able to correlate all the traces from that single operation together. Achieving that will give you great insights on your system.

I really like the nuget package EventSourceProxy. It remove some annoying boilerplate code required by the .net EventSource class and add an elegant way to manage correlation scopes.

using (EventActivityScope scope1 = new EventActivityScope())
{
	// events are logged under scope1
	using (EventActivityScope scope2 = new EventActivityScope(reuseExistingActivityID: true))
	{
		// events are still logged under scope1
	}

	using (EventActivityScope scope3 = new EventActivityScope(reuseExistingActivityID: false))
	{
		// events are logged under scope3
	}
}

Correlation ids are kept through the call stack even when async/await is used

For more info
https://github.com/jonwagner/EventSourceProxy/wiki/Managing-Activity-&-Correlation-IDs

5. Never fails

This one could seem obvious but a logging framework should NEVER fail with an exception. I’m pretty sure I don’t need to explain why. Don’t worry this doesn’t append with all well-known frameworks, but I’ve seen it so many times when people try to implement their own logging mechanism.

6. Footprint must be as small as possible

A good logging framework should be as unobtrusive as possible in terms of lines of code and performance. If you need to change your architecture to suit your logging framework you’re probably doing something wrong. It should be almost invisible to the developers.

Don’t do any processing in your business logic before logging, simply pass all the variables to the EventSource and make sure to do that logic there. This could include things like hashing PII (Personal Identification Information) values or removing/masking secret information like passwords or connection strings.

Conclusion

I know I haven’t talk much about .net EventSource, except for a few examples. The thing is, there’s a lot of documentation out there to help you out with it. It’s more important to understand why you should use it. It’s one of the best framework out there which as also been around for a long time.

References

https://msdn.microsoft.com/en-us/library/dn774980.aspx
https://github.com/jonwagner/EventSourceProxy/wiki