Monday, September 2, 2013

Event Source & Implementing Interface - Thinking DI

In this post we are going to discuss one rather unexpected feature of EventSource. As we have been discussing that the type is introduced in .net framework 4.5 for generating ETW [Event Tracing for Windows]events. It makes it easier to generate these events by providing simple inherited type for EventSource providing definitions of logging methods. The compiler and run-time would make sure that the necessary manifest is generated and used at compile and run-time.

The unexpected behavior is seen when our custom EventSource implements an interface. In order to understand the problem, let's create a sample console application EventSourceMembers.



Now let's introduce a simple custom Event source. It just has one method decorated with Event attribute. Let's add the following code to the project just created.

namespace EventSourceMembers
{
using System.Diagnostics.Tracing;
[EventSource(Name="CustomEventSource")]
public sealed class CustomEventSource : EventSource
{
public static CustomEventSource Log = new CustomEventSource();
private CustomEventSource()
{ }
[Event(1, Message="User logged-in: {0}")]
public void Login(string userName)
{
WriteEvent(1, userName);
}
}
}

For simplicity, we can simply use the event source as follows:



Now we build the project. Now we can use PerfView to verify the generated events by the application.

PerfView /OnlyProviders=*CustomEventSource run "C:\Tools\PerfView\ProjectOutput\CustomEventSource\EventSourceMembers.exe"

And we do see the logs written out. As you can see PerfView registered the event.



Now let's use our amazing refactoring skills using Visual Studio to extract an interface from our CustomEventSource type. It just has one method Login.



Here ICustomEventSource interface is extracted as follows:

namespace EventSourceMembers
{
interface ICustomEventSource
{
void Login(string userName);
}
}

Now we again run the application using PerfView using the same command as used before. We see the following ETL data. You should notice that it is missing Login method, which is unlike before.



As you have been following, we have just implemented an interface for the type. In order to find out what could have gone wrong, let's see the PerfView log. You might see the log button flashing on the bottom right to draw our attention when the tool started running because of unexpected failure.



We can also see PerfView log here:



How to explain this?
Now we know what have caused this exception. It is implementing an interface for the EventSource sub-type. But is there an explanation for this or this is just a weird bug? Actually EventSource members implementing an interface members are not candidate for ETW manifest. Only direct methods in the sub-type qualify for this. So for the Login method, there is no ETW manifest generated. So calling WriteEvent expects and [EventId = 1], which is not the case in the manifest.

In order to further prove our point, let us add another method to the custom event source type. So DirectLogin is not an implementation of the method from the interface. Let's use the same method in Main method as well. We are updating the type as follows:

namespace EventSourceMembers
{
using System.Diagnostics.Tracing;
[EventSource(Name="CustomEventSource")]
public sealed class CustomEventSource : EventSource, ICustomEventSource
{
public static CustomEventSource Log = new CustomEventSource();
private CustomEventSource()
{ }
[Event(1)]
public void DirectLogin(string userName)
{
WriteEvent(1, userName);
}
[Event(2, Message="User logged-in: {0}")]
public void Login(string userName)
{
WriteEvent(1, userName);
}
}
}

If we use PerfView again with the same command, we don't see the exception anymore. We do see the event for the new method, which proves our point.



Is there an alternative?
Actually there is. We don't have to implement [Event] methods from the interface. There can be [NonEvent] methods implementing the interface methods which can use those [Event] methods. The runtime doesn't even require the [Event] methods to be public. Here we are keeping the implementation as singleton. I think if you change it and register it with as a singleton with your DI container, it should still be fine. For unity, we can use ContainerControlledLifeTimeManager It definitely doesn't have any issue with EventSourceAnalyzer. Still need to see the performance implication. [See the Discussion]

namespace EventSourceComplexTypesApp
{
using System;
using System.Diagnostics.Tracing;
public interface IEventSourceWithComplexType
{
void StudentCreated(Student student);
}
[EventSource(Name = "EventSourceWithComplexType")]
public sealed class EventSourceWithComplexType : EventSource, IEventSourceWithComplexType
{
#region Private Fields
private static readonly Lazy<EventSourceWithComplexType> Instance =
new Lazy<EventSourceWithComplexType>(() => new EventSourceWithComplexType());
#endregion Private Fields
#region Private Constructor
private EventSourceWithComplexType() { }
#endregion Private Constructor
#region Public Properties
public static EventSourceWithComplexType Log
{
get { return Instance.Value; }
}
#endregion Public Properties
#region Non-Event Public Methods
[NonEvent]
public void StudentCreated(Student student)
{
StudentCreatedCore(student.StudentId, student.StudentName);
}
#endregion Non-Event Public Methods
#region Event Private Methods
[Event(1, Message="Student Id: {0}, Name: {1}", Level = EventLevel.Informational)]
private void StudentCreatedCore(int studentId, string studentName)
{
WriteEvent(1, studentId, studentName);
}
#endregion Event Private Methods
}
}

Download


No comments: