Skip to content

About .NET EventSource

Jon Wagner edited this page Oct 24, 2013 · 2 revisions

About .NET EventSource

.NET 4.5 introduced the EventSource class to make it easier to work with ETW. EventSource is a base class that you derive from to define a logging interface. The EventSource class takes care of registering the provider and generating the manifest for your events, as well as converting your method parameters to event data.

Here's an example of using EventSource directly:

[EventSource(Name="Calculator")]
public class CalculatorEventSource : EventSource
{
	[Event(1, Message="Clear", Level=EventLevel.Informational, Keywords=Keywords.Clear)]
	public void Clear()
	{
		if (IsEnabled(EventLevel.Informational, Keywords.Clear))
			WriteEvent(1);
	}

	[Event(2, Message="Add {0} {1}", Level=EventLevel.Informational, Keywords=Keywords.Add)]
	public void Add(int x, int y)
	{
		if (IsEnabled(EventLevel.Informational, Keywords.Add))
			WriteEvent(2, x, y);
	}

	[Event(3, Message="Multiply {0} {1}", Level=EventLevel.Informational, Keywords=Keywords.Multiply)]
	public void Multiply(int x, int y)
	{
		if (IsEnabled(EventLevel.Informational, Keywords.Multiply))
			WriteEvent(3, x, y);
	}

	public class Keywords
	{
		public const EventKeywords Clear = (EventKeywords)1 << 0;
		public const EventKeywords Add = (EventKeywords)1 << 1;
		public const EventKeywords Multiply = (EventKeywords)1 << 2;
	}
}

Let's explain a few of these pieces:

  • EventSource Attribute - lets you change the name and/or GUID of the EventSource. The GUID is used by listeners to identify this particular provider. If you provide just a name, the GUID is generated from the name by a standard procedure.
  • Event Attribute - lets you control the output of the event.
    • Message - is a .NET-style format string that is used by loggers to display a pretty message.
    • Level - is a severity level of the event. The controller can enable events logging according to level. For example, you may choose to only log warnings and errors.
    • Keywords - is a ulong bitmask (64 bits) that also controls which events are logged. In this case, we chose to give each event its own keyword so we can turn logging on or off on a per method basis.
  • IsEnabled - each method calls IsEnabled first to see if logging is turned on for the given level and keyword. If logging is not enabled, then we skip potentially expensive logging operations.
  • WriteEvent - actually performs the logging. The first parameter is the event ID, and the other parameters are the parameters to be logged.
  • Keywords class - this class contains the keywords for the events. It must be a public class called Keywords.

Although this is significantly easier than writing directly to ETW, there are still a few issues:

  • The class has to derive from EventSource. There is no way to log an interface or other class.
  • Most of the code is boilerplate. Nobody should have to write boilerplate code.
  • Constants such as the Event ID and Event Level are duplicated in the attribute and the call to IsEnabled. This is a copy/paste error waiting to happen.
  • Keywords and other constants must be embedded in the EventSource class and cannot be shared across log sources. This is potentially another source of errors.
  • Methods must return void, and the parameters must be certain fundamental types such as strings, ints, etc. That makes it hard to implement more complex tracing scenarios.

We will try to solve these problems with EventSourceProxy. Let's learn about Implementing an EventSource.