.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.
Application logs for your service entity are not included in the data sent by APM libraries. Configure your server or application to send application logs to SolarWinds Observability SaaS. See Add logs from services.
If trace context is added to logs and those logs are sent to SolarWinds Observability SaaS, use the Traces Explorer to see the logs associated with a traced transaction.
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:
|
|
%property{TraceId:long}
|
The default format used for auto inserting; the log contains
|
|
%property{TraceId:json}
|
The output is a JSON object:
|
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:
|
{TraceIdLong}
|
This is the default format used for automatic insertions; the log contains
|
{TraceIdJson}
|
The output is a JSON object:
|
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.