Dynamically Defined Events in EventSource V4.6

In a previous blog I talked about the second of three interesting features of the new Version of V4.6 .NET EventSource, namely Rich Data Payloads.

In this blog I would like to talk about the last one:  Dynamically Defined Events.

Note that .like the Rich Data Payloads, this feature is fully available in the EventSource Nuget package, however, as always, we strongly encourage people to use the System.Diagnostics.Tracing.EventSource class built into the .NET framework if they can.   If can upgrade to V4.6 you can do this. 

 What are Dynamically Defined Events?

Up until now, if you wished to define a new event in EventSource, you needed to create a new method in your EventSource class.    For example here we define a new EventSource with a 'Load' event. 

sealed class MinimalEventSource : EventSource
    public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }

    public static MinimalEventSource Logger = new MinimalEventSource();

Requiring you to define these methods in general promotes the notion of a clear 'contract' between the code that generates the event and code processing this event (e.g. The receiver can rely on the fact that a 'Load' event will have two fields of the given names and types).  

However this structure does come at a cost, namely you can't add new events at runtime.   Most of the time this is not a problem, but from time to time scenarios crop up where being able to define events dynamically is useful.  EventSource supports this now with the Write<T> method.   Thus instead of

  • MinimalEventSource.Logger.Load(10, "AFile.dll");

I can write

  • MinimalEventSource.Logger.Write("Load", new { ImageBase=10L, Name="AFile.dll" } );

The Write<T> method is relying heavily on the C# anonymous type support (in blue above), which allows you to define a new type 'on the fly' as well as assign values to all the fields.    Thus the Write<T>() overload above takes two arguments, the first is the name, and the second is an arbitrary object, which typically is created using anonymous type syntax.   In the example above the 'new'  expression creates a new type with a long 'ImageBase' field and a string 'Name' field.  It then creates an instance of that type and initializes the fields.   This then is passed to the Write<T> method.  

The result is that I can build a new method 'on the fly' at the call site.   Note that this is not 'complete dynamic' because anonymous types cannot be generated at runtime (the C# compile still needs to generate a type definition at compile time), but it does get you most of the way.   EventSource goes one step further and allows you to define the EventSource itself on the fly.   Thus you could do

EventSource Logger = new EventSoruce("MinimalEventSource");

Logger.Write("Load", new { ImageBase=10L, Name="AFile.dll" } );

 Which allows you to create the EventSource without defining a subclass, and then use it immediately to log methods.  

 Dynamic events fully support the Rich Data Payload formats which mean you can send arrays and or nested structures in your data payloads.  In the Listener the fields of the 'top object' look like the arguments to the event. 

Levels and Keywords

There are a number of overloads to the Write<T>() method, and in particular one that takes a EventSourceOptions.   The most likely thing that you might want to do with this option is to set the 'Level' (verbosity) and keywords (groups used to turn on the event selectively) For example the following code

     EventKeywords Loader = (EventKeywords)0x1;
        new EventSourceOptions() { Level = EventLevel.Warning, Keywords = Loader },
        new { ImageBase = 10L, Name = "AFile.dll" }

logs a 'Load' event with the arguments, however at verbosity level of 'Warning' and under the a newly defined 'Loader' keyword (bit 0 = 1) that allows it to be turned on selectively.  

Dynamic Events and ETW

As mention in my previous post, ETW now supports a 'self describing' event format in addition to its tradition 'Manifest based' event format.  Since the manifest by construction needs to know 'beforehand' about all the possible events in the EventSource, Dynamic events can't possibly work for that case.   Thus like rich payloads Dynamic events are only supported when the self-describing format is used.   In fact Write<T> unconditionally uses the self-describing format even if you don't set this in the EventSource constructor.    Thus dynamic events will never show up in the manifest (which only makes sense).   


 Now there is a reason why I saved the dynamic events feature for last: it is because I think it is the least important of the new V4.6 features.   As one might expect, dynamically defined events are more expensive, but more importantly, they make it very easy to be unclear about the contract between the provider and consumer of the events.   They also tend to put lots of ugly details at the instrumentation point, which is not an improvement.    Thus in general the guidance is don't use dynamic events unless you need them.   The expectation is that you should be able to define most of your events statically, and you only need the dynamic abilities for 'quick and dirty' logging or because you are getting your events from somewhere outside your control (so you have to take what you are given and make evens 'on the fly'.   These are rare cases, so there should not really be huge amounts of Write<T> use.   Still, when you need it, you need it, which is why the API exists. 


In Version 4.6 of the runtime (or the Nuget package), EventSource now supports a Write() method that allows you to define events on the fly.  You can also define EventSources themselves on the fly (rather than making a subclass).   These capabilities can be super useful when you need them, but should really be used only when you need them as it makes misunderstandings between the producer and consumer of the event more likely.  

See the attached specification document for more details.