Question

I'm trying to use Event Tracing for Windows (ETW) in my .NET application via the EventSource class that was included in .NET 4.5. I'm subclassing EventSource as MyEventSource and trying to implement an interface IMyEventSource (for mocking purposes) as follows:

public interface IMyEventSource
{
  void Test();
}

public class MyEventSource : EventSource, IMyEventSource
{
  public static MyEventSource Log = new MyEventSource();

  [Event(1)]
  public void Test()
  {
    this.WriteEvent(1);
  }
}

When I run PerfView and execute this code, I get an IndexOutOfRangeException on the call to WriteEvent. If I remove the interface by modifying the code...

public class MyEventSource : EventSource
{
  public static MyEventSource Log = new MyEventSource();

  [Event(1)]
  public void Test()
  {
    this.WriteEvent(1);
  }
}

...then everything works just fine.

Here is the code I used for testing in both cases:

static void Main(string[] args)
{
  MyEventSource.Log.Test();
}

Why does my subclass of EventSource break if it simply implements an interface?

Here is a related post.

Was it helpful?

Solution

At time of asking the question, the answer from @LarsSkovslund was correct. However with the stable version of Microsoft.Diagnostics.Tracing.EventSource, Microsoft changed this according to their blog post:

With the RTM release we’ve relaxed some of the event source validation rules in order to enable certain advanced usage scenarios.

The two changes in this area:

  • EventSource types may now implement interfaces to enable the use of event source types in advanced logging systems that use interfaces to define a common logging target.

  • The concept of a utility event source type (defined as an abstract class deriving from EventSource) is introduced to support sharing code across multiple event source types in a project (e.g. for optimized WriteEvent() overloads).

System.Diagnostics.Tracing.EventSource, provided with the .NET Framework, supports these scenarios as of .NET 4.6

OTHER TIPS

While you can't have the event source implement an interface, it is possible to wrap it with another class that does. (This is an instance of the Adapter design pattern)

public class EventSourceAdapter : IEventSource
{
    private MyEventSource log;

    public EventSourceAdapter(MyEventSource log)
    {
        this.log = log;
    }

    public void Test()
    { 
        log.Test()
    }
}
} 

When the EventSource class is building up its event structure base on reflection it will consider direct methods only e.g. inherited members are not considered as in your case with the use of IMyEventSource.

You are getting the IndexOutOfRangeException because WriteEvent will use the event id parameter to lookup a descriptor block with an index matching the event id thus throwing the exception when index does not exist.

So in short DONT used interfaces to define your ETW events using EventSource.

Cheers Lars

As of today (Sept 29, 2014), the code the original poster provided does not work with the native code that ships with .NET 4.5. It still generates an "IndexOutOfRange" exception, and as he says, it does this only if the ETW events are being monitored (I'm using PerfView).

That said, I checked with .NET version 4.0 using the Microsoft EventSource Library from nuget.org, and his code does work with that.

I next installed Microsoft EventSource Library from nuget in a .NET version 4.5 project. I made sure to inherit from Microsoft.Diagnostics.Tracing.EventSource and not from System.Diagnostics.Tracing.EventSource from the native .NET 4.5 library. This worked, but I also found that I had to mark those methods that inherited from the interface with the [Microsoft.Diagnostics.Tracing.Event(int)] attribute.

I have also observed some strange behaviors I couldn't explain. Sometimes some of my events show up in PerfView as being named "EventID(0)" instead of the method name. Sometimes I got unexpected IndexOutOfRange exceptions. As best I can guess the registration from a previous trial remained in memory. I started renaming my EventSource class between trials, and I wasn't getting these issues anymore.

JR

There is a workaround for this issue (sorry I don't know how to explain the problem). If you're method is decorated with the NonEventAttribute you will be able to use your interface.

Your interface :

public interface IMyEventSource
{
  void Test();
}

And the implementation :

public class MyEventSource : EventSource, IMyEventSource
{
    public static MyEventSource Log = new MyEventSource();

    [NonEvent]
    public void Test()
    {
        this.InternalTest();
    }

    [Event(1)]
    private void InternalTest()
    {
        this.WriteEvent(1);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top