Search code examples
c#azureazure-application-insightsetw-eventsourceevent-flow

Using a System.Exception Object in EventSource


I'm trying to use Event Source (Microsoft.Diagnostics.EventFlow.Inputs.EventSource) to create an event that is handled by Event Flow (Microsoft.Diagnostic.EventFlow) and whose output is passed to Application Insights (Microsoft.Diagnostics.EventFlow.Outputs.ApplicationInsights) for analysis.

The event flow library seems to require that I pass the full System.Exception object to event flow in order to for it to be successfully classified as an exception event in Application Insights.

Here is the filter I am using in Event Flow for my exception:

    {
      "type": "metadata",
      "metadata": "exception",
      "include": "EventId == 21",
      "exceptionProperty": "shark"
    }

Here are my methods where I am currently generating the Event which I wish to handle with event flow. Currently this does appear inside application insights however I belie I have implemented it poorly as I see message below in the output window during runtime.

The parameters to the Event method do not match the parameters to the WriteEvent method. This may cause the event to be displayed incorrectly.

    private const int TestExceptionEventId = 21;
    [NonEvent]
    public void TestException(string operationType, Exception ex)
    {
        string shark = ex.ToString();
        TestException(operationType, shark);
        WriteEvent(TestExceptionEventId, operationType, ex);
    }

    [Event(TestExceptionEventId, Level = EventLevel.Error, Message = "{0} - {1}, {2}", Keywords = Keywords.Exception)]
    public void TestException(string operationType, string shark)
    {

    }

Here is the method where the logging event is fired:

 //EXCEPTION
 //id = 21
 try
 {
     int value = 1 / int.Parse("0");
 }
 catch (DivideByZeroException exception)
 {
     //id = 21
     _eventSource.TestException("hello", exception);
 }`

Can anyone provide some clarity on the correct way to implement this and what is the correct way to pass an System.Exception Object through Event Flow and Onto Application Insights.

Many Thanks.


Solution

  • There are two separate issues here. One is the error you get and the other one is the EventFlow configuration for the exception metadata. I will address both.

    The error

    The method that is decorated with the [Event] attribute is the one that should call WriteEvent. For example:

    private const int TestExceptionEventId = 21;
    
    [NonEvent]
    public void TestException(string operationType, Exception ex)
    {
        string shark = ex.ToString();
        TestException(operationType, shark);
    }
    
    [Event(TestExceptionEventId, Level = EventLevel.Error, Message = "{0} - {1}", Keywords = Keywords.Exception)]
    public void TestException(string operationType, string shark)
    {
        WriteEvent(TestExceptionEventId, operationType, shark);
    }
    

    Note: your original Message property had the value Message = "{0} - {1}, {2}", yet you provide only 2 parameters to the method (string operationType and string shark). Hence the error. That is why I changed it to Message = "{0} - {1}"

    There are some specific rules in order to have it working. From the docs:

    When you implement a method that is identified as an ETW event in an EventSource-derived class. You must call the base class WriteEvent method passing the EventId and the same arguments as the implemented method

    EventFlow configuration

    Here lies the biggest issue. The EventSource class does not allow you to write non-primitives using WriteEvent. That includes an Exception. From the docs:

    If the event has additional data, these should be passed as arguments. Currently only primitive types, DateTime, and string are allowed to be logged with the event.

    There is an issue on the GitHub repo that outlines your possibilities:

    I think you have a few options.

    One is to use EventSource.Write method https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.write(v=vs.110).aspx This should work well if you are using .NET Core, although I will be honest--I haven't tested it. Unfortunately with full framework there is a bug in the framework that prevents EventFlow from reading the event level correctly, so it is not a recommended way if you are using full (desktop) framework.

    Second option is to use a different logging library (e.g. Serilog) that allows you to pass arbitrary objects into EventFlow.

    Yet another option is to use custom EventFlow input. That should be not nearly as troublesome as a custom output for AI. I think you could even integrate it with your EventSource e.g. by making the EventSource implement IObservable and using [NonEvent] methods to raise events that carry exception objects. This way you will have just one logging API, your EventSource, for the application to use.

    My 2 cents

    My advice (I ended up doing that): forget about EventSource and use another logging library. The best part about using the EventSource is the fact that it features structured logging. I ended up using SeriLog that features this as well. It does support non-primitive types to be written to the log. EventFlow supports SeriLog as an input as well or you can configure the Application Insights (AI) sink for SeriLog that sends the data to Application Insights.

    If you decide to go for that option you can take a look at my implementation