SLAB : Limitations and Best Practices (Sharing the EventSource between different assemblies)

Topics: Contributing, General, Using
Dec 8, 2014 at 5:07 PM
Our application has some components and it has the logging methods as well. The same component is shared with multiple different and independent assemblies and each logical functional component has a separate Eventsource as per the SLAB recommendation. However, when looking at the actual trace output, we are not able to differentiate the Common logging who is actually calling the common (assuming multiple components are calling the Common at the same time).

To make the problem more clear, I have created a sample as given below:
  1. Common.dll contains the Database logic and CommonLogger.
  2. SLAB.FTE.dll contains the FTELogger and speaks to Common for database.
  3. SLAB.Vendor.dll contains the VendorLogger and speaks to Common for database.
  4. Research_SLAB.exe actually makes use of all of the above to work with FTE or Vendor based on business conditions.
when Research_SLAB invokes SLAB.FTE, Traces SQL table is displaying FTELogger and CommonLogger as provider. Can we just display FTELogger for CommonEvents as FTELogger is the actual invoker?
Dec 9, 2014 at 4:09 AM
Edited Dec 9, 2014 at 5:10 AM
An EventSource (which is used by SLAB to log events) is an ETW Provider. So, the provider name and provider ID are directly related to the EventSource.

If you want to capture additional application related context (e.g. who is calling the EventSource) you could add this as a parameter to the Event and publish that information as part of the payload (e.g. void InfoMessage(string message, string caller)). If you don't want to add that parameter to all Event methods then you could create a method marked with the NonEventAttribute and have that method determine who the caller is (e.g. StackFrame(1).GetMethod().Name along with NoInlining) and pass that information to a private Event method.

However, since SLAB is using .NET 4.5 we can use the new .NET 5 Caller Information attributes to combine and improve the above approaches. In this case, we can use System.Runtime.CompilerServices.CallerMemberName, System.Runtime.CompilerServices.CallerFilePath, and System.Runtime.CompilerServices.CallerLineNumber. These attributes have the benefit of being injected at compile time so avoid the the overhead of looking up the caller (and also issues with method inlining etc.). Here's a sample:
[Event(1, Level = EventLevel.Informational)]
public void InfoMessage(string message,
    [System.Runtime.CompilerServices.CallerMemberName] string callerMemberName = "",
    [System.Runtime.CompilerServices.CallerFilePath] string callerFilePath = "")
    if (this.IsEnabled())
        this.WriteEvent(1, message, callerMemberName, callerFilePath);

Which would output something like the following to the console:
EventId : 1, Level : Informational, Message : , Payload : [message : test 1] [callerMemberName : Main] [callerFilePath : c:\app\Program.cs] , EventName: InfoMessageInfo, Timestamp : 2014-12-09T05:05:44.2885304Z, ProcessId : 4608, ThreadId : 8640
Randy Levy
Enterprise Library support engineer
Support How-to