Skip to content

Common.Logging.ETWLogger

Steve Bohlen edited this page May 31, 2016 · 7 revisions

Common.Logging.ETWLogger

Common.Logging.ETWLogger provides an adapter for connecting Common.Logging to the Event-Tracing for Windows logging subsystem that is available on later editions of the Windows Operating System (both Servers and Clients). See MSDN for more information on Event-Tracing for Windows.

Quick-Start

  • Add the Common.Logging.ETWLogger NuGet package to your project.
  • To configure the logger in code, simply create an instance of the Common.Logging.ETW.ETWLoggerAdapter and call the .GetLogger(...) method on the adapter as follows:
var adapter = new ETWLoggerAdapter();
var logger = adapter.GetLogger(typeof(MyClass));

logger.Warn("Message to Log here!"); 
  • To Configure the logger via e.g., app.config, web.config files, add the relevant sections to your .config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
  </configSections>

  <common>
    <logging>
      <factoryAdapter type="Common.Logging.ETW.ETWLoggerFactoryAdapter, Common.Logging.ETWLogger">
      </factoryAdapter>
    </logging>
  </common>

</configuration>

Advanced Configuration

Common.Logging.ETWLogger offers several configuration options to control its behavior as follows:

Custom EventSource Sub-Class

Common.Logging.ETWLogger makes use of the Microsoft.Diagnostics.Tracing.EventSource NuGet package to expose an API for managed (.NET) code to communicate with the underlying native Event-Tracing for Windows (ETW) subsystem. Among other things, this library provides an EventSource super-class from which developers must derive their own sub-class to communicate with ETW. Common.Logging.ETWLogger provides a default sub-class derived from this EventSource super-class and it is intended to meet the needs of most adopters of Common.Logging.ETWLogger.

For more fine-grained control over the behavior of Common.Logging.ETWLogger, developers can derive their own sub-class from the EventSource super-class and configure Common.Logging.ETWLogger to use your own custom type instead.

The Common.Logging.ETW.ETWLoggerAdapter exposes a property that can be configured to contain an instance of your custom EventSource-derived sub-class in code as follows:

var adapter = new ETWLoggerAdapter();
adapter.ETWEventSource = new MyCustomDerivedEventSource();

var logger = adapter.GetLogger(typeof(MyClass));
logger.Warn("Message to Log here!"); 

This can be configured in e.g., app.config, web.config files as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
  </configSections>

  <common>
    <logging>
      <factoryAdapter type="Common.Logging.ETW.ETWLoggerFactoryAdapter, Common.Logging.ETWLogger">
      	<arg key='commonLoggingEventSourceType' value='MyNamespace.MyCustomDerivedEventSource,MyAssemblyContainingTheCustomEventSource'/>
	  </factoryAdapter>
    </logging>
  </common>

</configuration>

Requirements for custom EventSource Sub-Classes

In order for Common.Logging.ETWLogger to properly interact with your custom EventSource-derived sub-class, the following requirements must be satisfied:

  1. Your custom sub-class must derive from Microsoft.Diagnostics.Tracing.EventSource. Note: the Microsoft.Diagnostics.Tracing.EventSource package also places a number of other restrictions on types derived from EventSource and of course your own sub-class must satisfy all of these as well. There are two MS-WORD documents contained in the Microsoft.Diagnostics.Tracing.EventSource package that cover these additional requirements in full detail.
  2. Your custom sub-class must implement the Common.Logging.ETW.ICommonLoggingEventSource interface. This is a Common.Logging-specific interface that Common.Logging.ETWLogger expects any custom EventSource-derived type to fully-implement. For more detail, see the full interface definition and refer to the default implementation of this interface provided with Common.Logging.ETWLogger as an example of this.
  3. If you intend the ETWLoggerAdapterto be configured to use your custom sub-class via e.g., app.config, web.config, your custom sub-class must expose an empty/zero-arg constructor. At run-time, the ETWLoggerAdapter will use this ctor to instantiate an instance of your custom sub-class from the type declaration in the .config file provided in the commonLoggingEventSourceType argument. If this empty/zero-arg ctor isn't present, Common.Logging.ETWLogger will throw an exception. Note: if you intend to only configure the ETWLoggerAdapter via code (by setting the property on the ETWLoggerAdapter directly), then this empty/zero-arg ctor is not required.

Minimum Log Level

The minimum logging level for Common.Logging.ETWLogger can be controlled in code. The ETWLoggerAdapter exposes a .LogLevel property to control this behavior:

var adapter = new ETWLoggerAdapter();
adapter.LogLevel = LogLevel.Error;

var logger = adapter.GetLogger(typeof(MyClass));
logger.Warn("Message to Log here!"); 

This can also be controlled in e.g., app.config, web.config files as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
  </configSections>

  <common>
    <logging>
      <factoryAdapter type="Common.Logging.ETW.ETWLoggerFactoryAdapter, Common.Logging.ETWLogger">
      	<arg key='level' value='error'/>
	  </factoryAdapter>
    </logging>
  </common>

</configuration>

Note that the LogLevel setting is considered a minimum setting, with greater logging levels implicitly included. Consider the following example:

  • INFO is a lower LogLevel than ERROR
  • setting the LogLevel to ERROR will log all ERROR events but not INFO events
  • setting the LogLevel to INFO will log all INFO events but also all ERROR events

Known Limitations

The known limitations of Common.Logging.ETWLogger all stem from several of the inherent limitations of the Microsoft.Diagnostics.Tracing.EventSource package upon which Common.Logging.ETWLogger depends for its ability to communicate with the native ETW subsystem.

Event Naming Limitations

One of the consequences of using the Microsoft.Diagnostics.Tracing.EventSource package as the basis for communicating with the ETW subsystem is that all logging will be reported to ETW in events of the form EventSourceDerivedTypeName/LoggingMethodName. The Microsoft.Diagnostics.Tracing.EventSource package supports using an attribute to override the EventSourceDerivedTypeName portion of the event name to be reported, and this is the approach used in the default EventSource-derived type provided by Common.Logging.ETWLogger.

As you can see here, the name of the EventSource that will appear in ETW logs when using this default class has been set to Common.Logging.ETWLogger. When combined with the methodnames in that same type, events reported to ETW will appear in e.g., the following form:

  • Common.Logging.ETWLogger/Warn ...
  • Common.Logging.ETWLogger/Info ...

Because the name that is reported to ETW by Microsoft.Diagnostics.Tracing.EventSource is a combination of the name of the EventSource-derived type (or the value of the attribute) and the method name invoked in the EventSource-derived type, there is no opportunity for Common.Logging.ETWLogger to influence these values at runtime. This means that all events reported to ETW from any project using Common.Logging (and the default EventSource-derived type) will have the same event names.

If you desire to have different/unique event names reported to ETW (for your project or even different subsystems of your project), the only mechanism for developers to influence these values is to provide your own custom EventSource-derived type and "switch them out" as needed using the techniques noted in the earlier Advanced Configuration section.

Exception Reporting Limitations

One of the limitations of using the Microsoft.Diagnostics.Tracing.EventSource package as the basis for communicating with the ETW subsystem is that in this form ETW can only accept primitive types (e.g., int, string) and DateTime values to record.

In the case of logging simple text messages (e.g., diagnostic scenarios), this isn't an issue since we can easily pass a string to ETW to record. However, in the case of recording an Exception, we cannot pass the Exception object through to ETW to record. To mitigate this as best we can within the constraints of the Microsoft.Diagnostics.Tracing.EventSource package, Common.Logging.ETWLogger will report the Exception in its string representation by first calling .ToString() on the logged Exception and then reporting it to ETW.

This will result in a recording of the type of the Exception being logged and its .Message property, but may not preserve fully-detailed stack-traces in all cases (due to the inherent limitations of the default implementation of the .Tostring() method in the .NET System.Exception class). If you desire more detailed logging of exceptions in ETW, you're only recourse is to throw your own Exceptions, overriding the .ToString() method to provide the detail you're seeking.

Another limitation of the Microsoft.Diagnostics.Tracing.EventSource package is that all logging methods in the EventSource-derived type must have unique names -- overloaded methods (those with identical methodnames but whose param signature differs) are not permitted. This means that while the Common.Logging.ILog interface can offer methods like the following:

  • logger.Warn(string message)
  • logger.Warn(string message, Exception exception)

...such overloads cannot be reproduceed in EventSource-derived types (since the methodname is a component of the reported event name in ETW, the name of each method that logs to ETW must be unique -- regardless of param signature).

To mitigate this limitation, Common.Logging.ETWLogger must use two different methods in its EventSource-derived type to handle logging with and without an Exception. This strategy is illustrated in the definition of the ICommonLoggingEventSource interface that offers e.g., the following typical duality of method to handle this situation:

  • .Warn(string message)
  • .WarnException(string message, string exception)

Note that unless you are implementing your own EventSource-derived type, much of this complexity is hidden from the consumer of Common.Logging.ETWLogger because you would (typically) code against the Common.Logging.ILog interface which of course continues to expose a richer set of overloaded method signatures e.g.,

  • ILog.Warn(string message)
  • ILog.Warn(string message, Exception exception)
Clone this wiki locally