Trace context in logs (legacy agent)
The following content pertains to
AppOptics agents are no long receiving updates. The new SolarWinds Observability libraries 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 migrating to SolarWinds Observability.
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 ID2019-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 object2019-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 context15: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 object15: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.