Connecting .NET Logs and Traces

Connecting .NET Logs and Traces

You can set up your logging library and .NET tracing configurations so that trace and span IDs are injected into application logs, providing you with application performance monitoring data correlated with log data.

Note: Automatic injection works only for logs formatted as JSON. Otherwise, use manual injection.

Configure the .NET Tracer with Unified Service Tagging for the best experience and helpful context when correlating application traces and logs.

The .NET Tracer supports the following logging libraries:

Getting started

Whether you use automatic or manual trace injection, do these setup steps:

  1. Configure the .NET Tracer with the following tracer settings:

    • DD_ENV
    • DD_SERVICE
    • DD_VERSION
  2. In the logs Agent configuration for the specified files to tail, set source: csharp so that log pipelines can parse the log files.

  3. Update the logging configuration based on the logging library:

Examples:

Trace and span IDs are injected into application logs only after you enable log context enrichment, as shown in the following example code:

var log = new LoggerConfiguration()
    // Add Enrich.FromLogContext to emit Datadog properties
    .Enrich.FromLogContext()
    .WriteTo.File(new JsonFormatter(), "log.json")
    .CreateLogger();

For additional examples, see the Serilog automatic trace ID injection project on GitHub.

Trace and span IDs are injected into application logs only after you enable mapped diagnostic context (MDC), as shown in the following example code:

  <layout type="log4net.Layout.SerializedLayout, log4net.Ext.Json">
    <decorator type="log4net.Layout.Decorators.StandardTypesDecorator, log4net.Ext.Json" />
    <default />
    <!--explicit default members-->
    <remove value="ndc" />
    <!--remove the default preformatted message member-->
    <remove value="message" />
    <!--add raw message-->
    <member value="message:messageobject" />
    <!-- Add value='properties' to emit Datadog properties -->
    <member value='properties'/>
  </layout>

For additional examples, see the log4net automatic trace ID injection project on GitHub.

Trace and span IDs are injected into application logs only after you enable mapped diagnostic context (MDC), as shown in the following example code for NLog version 4.6+:

 <!-- Add includeMdlc="true" to emit MDC properties -->
  <layout xsi:type="JsonLayout" includeMdlc="true">
    <attribute name="date" layout="${longdate}" />
    <attribute name="level" layout="${level:upperCase=true}"/>
    <attribute name="message" layout="${message}" />
    <attribute name="exception" layout="${exception:format=ToString}" />
  </layout>

For NLog version 4.5:

 <!-- Add includeMdc="true" to emit MDC properties -->
  <layout xsi:type="JsonLayout" includeMdc="true">
    <attribute name="date" layout="${longdate}" />
    <attribute name="level" layout="${level:upperCase=true}"/>
    <attribute name="message" layout="${message}" />
    <attribute name="exception" layout="${exception:format=ToString}" />
  </layout>

For additional examples, see the automatic trace ID injection projects using NLog 4.0, NLog 4.5, or NLog 4.6 on GitHub.

Next, complete the setup for either automatic or manual injection.

Automatic injection

If your application logs are in JSON, the last step in setting up automatic trace ID injections is:

  1. In the .NET Tracer’s environment variables, enable DD_LOGS_INJECTION=true. For alternative ways to configure the .NET Tracer, see Configuring the .NET Tracer.

Manual injection

If your application logs aren’t in JSON, you can manually enrich the logs with APM data:

Required keyDescription
dd.envGlobally configures the env for the tracer. Defaults to "" if not set.
dd.serviceGlobally configures the root service name. Defaults to the name of the application or IIS site name if not set.
dd.versionGlobally configures version for the service. Defaults to "" if not set.
dd.trace_idActive trace ID during the log statement. Defaults to 0 if no trace.
dd.span_idActive span ID during the log statement. Defaults to 0 if no trace.

Note: If you are not using a Datadog Log Integration to parse your logs, custom log parsing rules must parse dd.trace_id and dd.span_id as strings. For information, see the FAQ on this topic.

To finish setting up manual log enrichment, after doing the three steps above:

  1. Reference the Datadog.Trace NuGet package in your project.

  2. Use the CorrelationIdentifier API to retrieve correlation identifiers and add them to the log context while a span is active.

Examples:

Note: The Serilog library requires message property names to be valid C# identifiers. The required property names are: dd_env, dd_service, dd_version, dd_trace_id, and dd_span_id.

using Datadog.Trace;
using Serilog.Context;

// there must be spans started and active before this block.
using (LogContext.PushProperty("dd_env", CorrelationIdentifier.Env))
using (LogContext.PushProperty("dd_service", CorrelationIdentifier.Service))
using (LogContext.PushProperty("dd_version", CorrelationIdentifier.Version))
using (LogContext.PushProperty("dd_trace_id", CorrelationIdentifier.TraceId.ToString()))
using (LogContext.PushProperty("dd_span_id", CorrelationIdentifier.SpanId.ToString()))
{
    // Log something
}
using Datadog.Trace;
using log4net;

// there must be spans started and active before this block.
try
{
    LogicalThreadContext.Properties["dd.env"] = CorrelationIdentifier.Env;
    LogicalThreadContext.Properties["dd.service"] = CorrelationIdentifier.Service;
    LogicalThreadContext.Properties["dd.version"] = CorrelationIdentifier.Version;
    LogicalThreadContext.Properties["dd.trace_id"] = CorrelationIdentifier.TraceId.ToString();
    LogicalThreadContext.Properties["dd.span_id"] = CorrelationIdentifier.SpanId.ToString();

    // Log something

}
finally
{
    LogicalThreadContext.Properties.Remove("dd.env");
    LogicalThreadContext.Properties.Remove("dd.service");
    LogicalThreadContext.Properties.Remove("dd.version");
    LogicalThreadContext.Properties.Remove("dd.trace_id");
    LogicalThreadContext.Properties.Remove("dd.span_id");
}
using Datadog.Trace;
using NLog;

// there must be spans started and active before this block.
using (MappedDiagnosticsLogicalContext.SetScoped("dd.env", CorrelationIdentifier.Env))
using (MappedDiagnosticsLogicalContext.SetScoped("dd.service", CorrelationIdentifier.Service))
using (MappedDiagnosticsLogicalContext.SetScoped("dd.version", CorrelationIdentifier.Version))
using (MappedDiagnosticsLogicalContext.SetScoped("dd.trace_id", CorrelationIdentifier.TraceId.ToString()))
using (MappedDiagnosticsLogicalContext.SetScoped("dd.span_id", CorrelationIdentifier.SpanId.ToString()))
{
    // Log something
}

Further Reading