Skip to content

Adding Additional Logging Context

Jon Wagner edited this page Oct 24, 2013 · 4 revisions

Adding Additional Logging Context

Sometimes you need to log the same data across all methods of a type. For example, you may want to log the security context at each operation. ESP can automatically add context information for any interface implementation (this does not work for class-based EventSources).

Let's say you have this interface and you want to always log the current Windows identity.

public interface IMyLogger
{
	void LogSomething(int number, SomeData data);
}

Adding Context using TraceParameterProvider

You can simply add a TraceParameterProvider rule at startup:

public class PrincipalNameContext
{
	public static void GetPrincipalName()
	{
		return WindowsIdentity.GetCurrent().Name;
	}
}

// at startup
TraceParameterProvider.Default
	.ForAnything()
		.AddContext("windowsidentity", () => PrincipalNameContext.GetPrincipalName();

Adding a Custom TraceContextProvider

To do this, derive from TraceContextProvider and register the provider for your logging type.

class PrincipalNameContextProvider : TraceContextProvider
{
	public override string ProvideContext(InvocationContext context)
	{
		return WindowsIdentity.GetCurrent().Name;
	}
}

EventSourceImplementer.RegisterProvider<IMyLogger>(new PrincipalNameContextProvider());
var data = new SomeData() { Count = 6, Name = "Jones" };
var log = EventSourceImplementer.GetEventSourceAs<IMyLogger>();
ConsoleEventListener listener = new ConsoleEventListener();
listener.EnableEvents((EventSource)log, EventLevel.LogAlways, (EventKeywords)(-1));
log.LogSomething(11, data);

Be sure to register the provider before creating any instances of your logger.

When you attach a context provider to an interface, ESP will automatically add an additional context parameter to the end of the parameter list, then call the context provider to fill in the context for the call.

The code above outputs:

SourceId : 202f3f6c-8ea0-5ddb-a393-8d22d099ca64
EventId : 1
Keywords : 1
Level : Informational
Message :
Opcode : Info
Task : 65533
Version : 0
Payload : [number : 11] [data : {"Count":6,"Name":"Jones"}] [Context : TEST\jonw]

EventName : LogSomethingInfo
Timestamp : 2013-03-04T20:18:09.4463374Z

Notice how there is an additional context parameter. If you need to output a complex object, you are responsible for serializing the object as a string.

TraceContextProviderAttribute

You can also attach your TraceContextProvider to a class or interface by using the TraceContextProvider Attribute:

[TraceContextProvider(typeof(PrincipalNameContextProvider))]
public interface IMyLogger
{
	...
}

Granular TraceContext Control

By default, if you attach a TraceContextProvider to an interface, it will add context to all of the methods on the interface. You can disable that by implementing ShouldProvideContext:

public override bool ShouldProvideContext(InvocationContext context)
{
	return context.MethodInfo.Name == "MethodThatNeedsContext";
}

You can also add the TraceContext attribute to the class or method to control context logging:

[TraceContext(false)]
public interface IMyLogger
{
	[TraceContext(true)]
	void LogSomething(int number, SomeData data);

	void LogSomethingWithoutContext(int number, SomeData data);
}

Note that attributes on methods override the attributes on the class.

Exception Handling

If you write your own context provider, we recommend that you trap any exceptions in your provider. You don't want turning on logging to affect the behavior of your code.