Documentation forSolarWinds Observability

.NET trace context in logs

When you add trace context to application logs, you can correlate the log messages from a single transaction, and if sampled, the log messages to the transaction trace detail.

The SolarWinds Observability .NET Library offers several options to include trace context into logs managed by several popular .NET logging frameworks. It also provides a custom SDK call that can be used if the automatic options do not support your logger.

How to add trace context to logs

There are three options to include trace context in logs:

  • Automatic insertion
  • Automatically adding an attribute
  • Using the SDK

Automatic insertion

For supported frameworks, the .NET Library can automatically insert the trace context to the end of the layout's output template. As a result, all log messages will have TraceId=<Trace ID>, SpanId=<Span ID>, and TraceFlags=<Trace Flags> appended to the end of the message.

Automatic attribute

For supported frameworks, the .NET Library can automatically add Trace Context values to the logging event object. This feature is disabled by default. To enable it, see the Automatically created attribute configuration section.

To include the Trace ID values in the logging layout, use the following patterns.

Log4net:

  • Trace ID value: %property{TraceId:<format>}
  • Span ID value: %property{SpanId:<format>}
  • Trace Flags value: %property{TraceFlags:<format>}

Serilog:

  • Trace ID value: {TraceId<format>}
  • Span ID value: {SpanId<format>}
  • Trace Flags value: {TraceFlags<format>}

Using the SDK

Trace Context can be manually inserted into log messages if the automatic options do not support your logging framework. The string value of the Trace Context can be obtained using the SDK GetCurrentLogTraceId method. Note that if the library is not available or not started properly, the value is an empty string.

Supported frameworks

Log4net

PatternLayout with automatic insert

When you use Automatic insertion, the PatternLayout is updated with %property{TraceId:long} at the end. An example of the updated layout is:

<layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%date [%thread] %level %logger - %message %property{TraceId:long} %property{SpanId:long}  %property{TraceFlag:long} %newline" />
</layout>

When a message is logged, the Trace ID is added at the end of the log message. An example of the output is:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController - hello world TraceId=33eef8a08fb0afa6797ec5b20da24720 SpanId=168d517543a46f5e TraceFlags=01

When you use Automatic attribute, update the PatternLayout layout in the log4net.config with %property{TraceId:<format>}, %property{SpanId:<format>}, and %property{TraceFlags:<format>}. An example of how the layout should be updated is:

<layout type="log4net.Layout.PatternLayout">
  <conversionPattern value="%date [%thread] %level %logger %property{TraceId:long} %property{SpanId:long}  %property{TraceFlag:long} - %message%newline" />
</layout>

An example of the output is:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController TraceId=33eef8a08fb0afa6797ec5b20da24720 SpanId=168d517543a46f5e TraceFlags=01 - hello world

When you use automatic attribute, no format or the long or json formats can be used. For example, to add Trace ID to the layout the following options are available:

%property{TraceId}

The output contains only the log Trace ID:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController 33eef8a08fb0afa6797ec5b20da24720 - hello world
 
%property{TraceId:long}

The default format used for auto inserting; the log contains TraceId= followed by the Trace ID:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController TraceId=33eef8a08fb0afa6797ec5b20da24720 - hello world
 
%property{TraceId:json}

The output is a JSON object:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController {"TraceId":"33eef8a08fb0afa6797ec5b20da24720"} - hello world
 

XmlLayout with automatic attribute

When you use Automatic attribute with the XMLLayout layout, the trace context attributes TraceId:<format>, SpanId:<format>, and TraceFlags:<format> are added to the LogEvent object. An example of the XMLLayout is:

<layout type="log4net.Layout.XmlLayout"></layout>

An example of the output is:

<log4net:event logger="WebApp.Controllers.HelloController" timestamp="2022-04-22T15:35:22.0626731-07:00" level="INFO" thread="6" domain="/LM/W3SVC/5/ROOT-1-132034701117372141" username="IIS APPPOOL\MVC5_TestLog4net">
  <log4net:message>hello world</log4net:message>
  <log4net:properties>
    <log4net:data name="log4net:HostName" value="TEST-MACHINE" />
    <log4net:data name="TraceId" value="33eef8a08fb0afa6797ec5b20da24720" /> 
    <log4net:data name="SpanId" value="168d517543a46f5e" /> 
    <log4net:data name="TraceFlags" value="01" /> 
  </log4net:properties>
</log4net:event>

When you use a SimpleLayout layout and automatic insertion is enabled, the trace context attributes TraceId:<format>, SpanId:<format>, and TraceFlags:<format> are added to the output. An example of the SimpleLayout is:

<layout type="log4net.Layout.SimpleLayout"></layout>

An example of the output is:

INFO - hello world TraceId=33eef8a08fb0afa6797ec5b20da24720 SpanId=168d517543a46f5e TraceFlags=01

Serilog 2.8.0

This feature is only available when log events are enriched with built-in Serilog.Context.LogContext enricher. When a logger object is created, a call to .Enrich.FromLogContext() method must be added.

When you use automatic insertion, the output template is updated at runtime to contain Trace Context attributes. An example of the code you can use:

// Output template
string outTemplate = "{Timestamp:HH:mm} [{Level}] {Message}{NewLine}{Exception}";

// Create serilog logger using provided output template

Log.Logger = new LoggerConfiguration()
  .Enrich.FromLogContext()
  .WriteTo.File(logFile, rollingInterval: RollingInterval.Infinite, outputTemplate: outTemplate)
  .CreateLogger();
  
Log.Information("hello world");

An example of the output is:

15:55 [Information] hello world TraceId=33eef8a08fb0afa6797ec5b20da24720 SpanId=168d517543a46f5e TraceFlags=01

When you use automatic attribute, the output template must be manually updated with the Trace Context attributes.

// Output template
string outTemplate = "{Timestamp:HH:mm} [{Level}] {TraceIdLong} {SpanIdLong} {TraceFlagsLong} {Message}{NewLine}{Exception}";

// Create serilog logger using provided output template

Log.Logger = new LoggerConfiguration()
  .Enrich.FromLogContext()
  .WriteTo.File(logFile, rollingInterval: RollingInterval.Infinite, outputTemplate: outTemplate)
  .CreateLogger();

Log.Information("hello world");

An example of the output is:

15:55 [Information] TraceId=33eef8a08fb0afa6797ec5b20da24720 SpanId=168d517543a46f5e TraceFlags=01 hello world

When you use automatic attribute, no format or the long or json formats can be used. For example, to add Trace ID to the layout the following options are available:

{TraceId}

The output contains only log trace context:

15:55 [Information] 33eef8a08fb0afa6797ec5b20da24720 hello world
{TraceIdLong}

This is the default format used for automatic insertions; the log contains TraceId= followed by the Trace ID:

15:55 [Information] TraceId=33eef8a08fb0afa6797ec5b20da24720 hello world
{TraceIdJson}

The output is a JSON object:

15:55 [Information] {"TraceId":"33eef8a08fb0afa6797ec5b20da24720"} hello world

When you use automatic attribute in combination with the CompactJsonFormatter formatter class, the Trace Context attributes are added as properties of the JSON record. For example if you use this code:

Log.Logger = new LoggerConfiguration()
  .Enrich.FromLogContext()
  .WriteTo.File(new CompactJsonFormatter(), logFile)
  .CreateLogger();
  
Log.Information("hello world");

An example of the output is:

{"@t":"2019-05-28T21:44:00.3293747Z","@mt":"hello world","TraceId":"33eef8a08fb0afa6797ec5b20da24720", "SpanId":"168d517543a46f5e","TraceFlags":"01"}

The scripts are not supported under any SolarWinds support program or service. The scripts are provided AS IS without warranty of any kind. SolarWinds further disclaims all warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The risk arising out of the use or performance of the scripts and documentation stays with you. In no event shall SolarWinds or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the scripts or documentation.