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;
     Logger.Write("Load",
        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).   

Guidance

 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. 

Summary

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. 

Vance

 

EventSourceDynamicEvents.docx

Comments (9)

  1. aelij says:

    The attached document mentions "TraceEvent Nuget Package Version 1.1.35 or later", but on NuGet the latest version is 1.0.38, and this version indeed does not play well with self-describing events. When will the newer version be available?

  2. What precisely does not work?   Is should be working.   If it is not, we need some sort of description of what is wrong so we can repro it.  

  3. Note that I have a guess as to what may be wrong.   As described in the TraceEvent Programmers manual blogs.msdn.com/…/_5F00_TraceEventProgrammersGuide.docx you must use a TraceEventParser, to decode events.   The decoder for Self-Describing events happens to be the RegisteredTraceEventParser (this parser asks the OS to decode the event, and the OS knows how to do this for self-describing events).    Thus you need to use that Parsers (There is a 'Registered' property on TraceEventSource that fetches this parser for your source)  

    This is probably counter-intuitive for users, and they typically think DynamicTraceEventParser is the correct parser to use.   Rather than having to explain this, I updated TraceEvent (see new version 1.0.39), so that either the RegisteredTraceEventParser or the DynamicTraceEventParser will resolve self-describing events.

    There are also a number of small bug fixes, that while they probably will not affect your scenario, but are good to have in case your scenarios expand, so I do recommend upgrading.      

  4. Dimitar Moev says:

    How to set explicitly Id and Message properties of the dynamically defined events?

  5. @Dimitar Moev – Dynamic events don't support Id or Messages.   The lack of support for IDs is by design, as the point of dynamic events is not having a contract (or a very loose one), and managing event IDs really do require a stricter contract.    Messages could be added, but because there is a question of how to do message  localization.  The cost benefit does not seem to be there, so we avoided the feature.  

  6. ajhuddymsdn says:

    Hi Vance,

    I'm looking for a way to write an event into the OS (using .net 4.5 event source, or your nuget package) **and** specifying the timestamp of the event.  I don't want to use the timestamp the OS comes up with.

    I'm in a multi-computer eventing environment, and one computer is delegating the "correct" time.

    Can this be done?  I hope so…

  7. You don't say whether you are using ETW (or the EventLog) when you say 'write an event to the OS'.  I am assuming you are because EventListeners don't actually give you a timestamp.  

    There is no way of overriding the timestamp that ETW marks on the event.    You can of course log a timestamp of your choosing as part of the payload (the official time), which can then be used to 'correct' the ETW timestamps at processing time.  

  8. ajhuddymsdn says:

    I'm using .NET 4.5 EventSource in client apps, and TraceEventHandler nuget as a service on each computer.

    Ok…so payload is the only workaround.

  9. Dimitar Moev says:

    Windows Azure Diagnostics does not see events generated from Dynamic EventSource. Any idea why?

Skip to main content