I’ve been an advocate of good logging in applications (especially server side ones) for many years, and today I’m working on something for myself and wanted to use the latest and greatest event logging framework so chose System.Diagnostics.Tracing (which has been around since .NET 4 days). I’ve used it before but today I came across an issue that had me stumped for some time.
I’d created a custom event source, and derived this from an interface so that I could inject an implementation in at runtime…
public interface ILogger
{
void T1(string message);
void T2(string message);
}
public class Logger : EventSource, ILogger
{
public void T1(string message) { this.WriteEvent(1, message); }
public void T2(string message) { this.WriteEvent(2, message); }
}
But when I used PerfView to view my events there were none. After a lot of head scratching I tried a sample from Vance’s blog (which seems to be the main place to get any information about this feature) and of course that worked first time. I did some more fiddling in code and then stumbled across the reason as I was debugging the code. It’s worthy of a blog post, as this may well catch someone else out.
I’d created the interface and implemented it in the EventSource derived class so that I could mock out the logger code for testing – however this was the part that caught me out. When the code runs, the EventSource class builds a manifest that contains details of the "events" that are written out – and this uses the following bit of reflection to get all the methods that we want to expose as "events" in the ETW trace…
MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
I’ve highlighted the offending enum value above. This is saying "find me all methods whether public or not, defined on this Instance ONLY". So, because I’d created an interface and then implemented that interface, my methods were not found by the code that generates the manifest, and hence I wasn’t getting anything useful in the ETL file.
The net effect of this is that I cannot directly use an interface to define an interface for the events I want to emit. There are a few ways around this I can think up…
- Hard-code the logger class as in the examples on Vance’s posts. Nope, not going to fly, I this stuff to be mockable/testable!
- Create an EventSource derived class and add shim methods to call the actual logging methods
- Write my own version of EventSource, seems like a lot of work for little gain!
- Create a shim class that forwards all calls to the EventSource derived class
Of these I picked the last, as I do want my event source code to be mockable, and it seemed to be the least bad of the options, so I ended up with the following class...
public class ActualLogger : EventSource
{
public void T1(string message) { this.WriteEvent(1, message); }
public void T2(string message) { this.WriteEvent(2, message); }
public static ActualLogger Instance { get { return _instance; } }
static ActualLogger _instance = new ActualLogger();
}
public class Logger : ILogger
{
public void T1(string message) { ActualLogger.Instance.T1(message); }
public void T2(string message) { ActualLogger.Instance.T2(message); }
}
Well, something like that anyway!
As an aside, if you want to look at the manifest that is generated from your EventSource derived class, there's a static method on EventSource that can be useful...
var manifest = EventSource.GenerateManifest(typeof(ActualLogger), typeof(ActualLogger).Assembly.Location);
Hope this helps someone!