Getting started with ETW using .NET’s EventSource

2 minute read

.NET 4.5 introduced the EventSource class, allowing convenient access to Event Tracing for Windows (ETW) from managed code. This is a boon for enterprise developers, and I encourage you to go read up on it at MSDN. Also, be sure sure to check out Vance Morrison’s EventSource blog entries. Another useful blog by MS MVP Kathleen Dollard is Leaning into Windows, and Muhammad Shujaat Siddiqi’s blog is worth checking out as well.

Once you’ve got the hang of EventSource, take it to the next level with the Enterprise Library Semantic Logging Application Block (SLAB). You can do some pretty cool stuff with it, including verification of your EventSource class validity, and automatic routing of ETW events to different storage platforms (such as databases, Azure tables, and text files). Start with the developer’s guide.

Finally, if you take a close look at the documentation of the EventSource class, you’ll notice the following note:

There is a NuGet version of the EventSource class that provides more features. For more information, see Microsoft EventSource Library 1.0.16.

I couldn’t find any documentation for this mysterious NuGet package, but I did find its samples package. Once you install it you can take a look at the extensively documented code as well as debug and step into the parts you’re interested in. Be sure to read the guides that accompany the samples: _EventRegisterUsersGuide.docx and _EventSourceUsersGuide.docx – they pertain to the vanilla library that comes with .NET as well (not just the NuGet package).

One thing I couldn’t find is a list of differences between the vanilla .NET EventSource and the NuGet package. Going by the signatures, I only saw a couple more WriteEvent overloads. If you’ve read the documentation, you should know by now that that’s a good thing, however digging a little into the code I found another important difference – event type support.

The .NET version currently supports the following types (taken from ManifestBuilder.GetTypeName):

  1. Enum
  2. Boolean
  3. SByte
  4. Byte
  5. Int16
  6. UInt16
  7. Int32
  8. UInt32
  9. Int64
  10. UInt64
  11. Single
  12. Double
  13. DateTime
  14. String
  15. Guid

The NuGet package adds support for the following:

  1. Char
  2. IntPtr
  3. Byte*
  4. Byte[]

Finally, the beta pre-relase includes a very interesting overload: _public void Write(string eventName, T data). _The _data _parameter is documented as follows:

The object containing the event payload data. The type T must be an anonymous type or a type with an [EventData] attribute. The public instance properties of data will be written recursively to create the fields of the event.

In other words, we should be able to use annotated custom types (in a similar fashion to WCF DataContract-annotated objects). But even better than that, we should be able to say:

[Event(1)]
public void Bar(int i, string s, DateTime d)
{
    Write(null, new {I = i, S = s, D = d});
}

Notice I said should, because I could make neither work (beta after all). But why do we even need this when we already have the WriteEvent(int eventId, params Object[]) overload? Well, let’s look at the documentation of the latter:

By default, the compiler calls this overload if the parameters for the call do not match one of the other method overloads. This overload is much slower than the other overloads, because it does the following:

  1. It allocates an array to hold the variable argument.
  2. It casts each parameter to an object (which causes allocations for primitive types).
  3. It assigns these objects to the array.
  4. It calls the function, which then determines the type of each argument so it can be serialized for ETW.

Using an anonymous types, we save the boxing (bullet 2) and unboxing (post bullet 4). I’m not sure how big of a benefit that is, so we may still be forced to use the _WriteEventCore _method, but I suppose every little bit helps!

Leave a Comment