Documentation forAppOptics

Trace context in logs (legacy agent)

The following content pertains to trace context in logs for the legacy AppOptics NET Agent.

AppOptics agents are no long receiving updates. The new SolarWinds Observability libraries can send APM data in AppOptics and are regularly updated with new features and improvements. If you are still relying on the AppOptics agents and your components are supported by the new libraries, consider transitioning to the SolarWinds Observability libraries for your APM needs. For more information about the benefits of migrating to the SolarWinds Observability libraries. Alternatively, you can use SolarWinds Observability as your primary APM solution.

If you have already transitioned to the new SolarWinds Observability NET Library, see the SolarWinds NET Library documentation for trace context in logs information.

SolarWinds Observability libraries are not compatible with AppOptics agents. Do not use a mix of SolarWinds Observability libraries and AppOptics agents to instrument applications that are part of a distributed trace.

Adding trace context to application logs provides the ability to correlate the log messages from a single transaction, and if sampled, the log messages to the transaction trace detail.

The AppOptics .NET agent offers several options to include trace context into logs managed by several popular .NET logging frameworks, and provides a custom SDK call which can be used if the automatic options do not support your logger.

How to Add Trace Context to Logs

There are 3 options to include trace context into logs:

Automatic Insert

For supported frameworks (see below) the .NET agent can automatically insert the trace context to the end of the layout's output template. As a result, all log messages will have the Trace ID appended at the end.

This feature is disabled by default, to enable it see the configuration section.

Automatic Attribute

For supported frameworks (see below) the .NET agent can automatically add the %property{ao.traceId:long} attribute for Log4net, or the {aoTraceIdLong} attribute for Serilog, to the logging event objext. This attribute contains the Trace ID value that can then be included in the logging layout by using pattern such as %property{ao.traceId:long} for Log4net or {aoTraceIdLong} for Serilog.

This feature is disabled by default, to enable it see the configuration section.

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 agent is not available or not started propertly, the value will be an empty string.

Supported Frameworks

Log4net

When using automatic insert, the PatternLayout is updated with %property{ao.traceId:long} at the end and when a message is logged the Trace ID will be at the end of the log message.

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

Output:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController - hello world ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1

When using automatic attribute, the PatternLayout layout must be manually updated %property{ao.traceId:long}

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

Output:

2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 - hello world

The %property{ao.traceId:<format>} allows the following formats:

  • %property{ao.traceId} - output contains only the log Trace ID

    2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 - hello world
  • %property{ao.traceId:long} - default format used for auto inserting, the log Trace ID is prefixed with ao.traceId=

    2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 - hello world
  • %property{ao.traceId:json} - output is a JSON object

    2019-05-23 15:02:49,708 [6] INFO WebApp.Controllers.HelloController {"ao":{"traceId":"C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1"}} - hello world

When using the XmlLayout layout and automatic attribute is enabled

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

Output:

<log4net:event logger="WebApp.Controllers.HelloController" timestamp="2019-05-27T15: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="ao.traceId" value="C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1" />
  </log4net:properties>
</log4net:event>

When using an SimpleLayout layout and automatic insert is enabled

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

Output:

INFO - hello world ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1

Serilog 2.8.0

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

When using automatic insert, the output template is updated at runtime to contain {aoTraceIdLong} attribute

// 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");

Output:

15:55 [Information] hello world ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1

When using automatic attribute, the output template must be manually updated with {aoTraceIdLong} attribute

// Output template
string outTemplate = "{Timestamp:HH:mm} [{Level}] {aoTraceIdLong} {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");

Output:

15:55 [Information] ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 hello world

The {aoTraceId<format>} allows the following formats:

  • {aoTraceId} - output contains only log trace context

    15:55 [Information] C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 hello world
  • {aoTraceIdLong} - default format used for auto inserting, the log Trace ID is prefixed with ao.traceId=

    15:55 [Information] ao.traceId=C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1 hello world
  • {aoTraceIdJson} - output is a JSON object

    15:55 [Information] {"ao":{"traceId":"C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1"}} hello world

When using automatic attribute in combination with CompactJsonFormatter formatter class, trace context is added as aoTraceId property of the JSON record

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

Output:

{"@t":"2019-05-28T21:44:00.3293747Z","@mt":"hello world","aoTraceId":"C2E7EA546BC9C31BC18E1877C3191A0C6E952C08-1"}

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.