Updating an ASP.NET Core web application with Serilog to Azure.Monitor.OpenTelemetry.AspNetCore

ritmo2k 971 Reputation points
2026-05-15T13:23:09.0766667+00:00

I am migrating an application from on-premises to Azure and could use some guidance on how to address the changes with correlation id handling. Currently, I use the same pattern utilized in stevejgordon/CorrelationId and update the log format string in Serilog to insert that value in every message. This works well specifically in that log entries for requests are all populated and easy to correlate.

However, I don't think this is required with OpenTelemetry and while a request can be traced in Azure, how do I manage the following two cases:

  • How do I get what will be the correlation id while running in Azure to appear in the console (log stream) when required?
  • What is the generally accepted configuration for local development when I would likely only ever use console/file logging? Thank you!
Developer technologies | ASP.NET Core | ASP.NET API
0 comments No comments

Answer accepted by question author
Jack Dang (WICLOUD CORPORATION) 18,980 Reputation points Microsoft External Staff Moderator
2026-05-18T03:17:52.3766667+00:00

Hi @ritmo2k ,

Thanks for reaching out.

With OpenTelemetry, you generally do not need to keep a separate custom correlation ID just to connect request logs together. In ASP.NET Core, the value you want to line up with Azure Monitor/Application Insights is the current Activity trace ID. That trace ID is created and flowed using the W3C trace context, and Azure Monitor uses it to correlate requests, dependencies, traces, and logs.

If you want that same correlation value to show up in the console or App Service log stream, the key is to include the current Activity identifiers in your console logging output. For the built-in logging providers, you can enable activity tracking like this:

builder.Logging.Configure(options =>
{
  options.ActivityTrackingOptions =
    ActivityTrackingOptions.TraceId |
    ActivityTrackingOptions.SpanId |
    ActivityTrackingOptions.ParentId;
});

builder.Logging.AddSimpleConsole(options =>
{
  options.IncludeScopes = true;
  options.SingleLine = true;
  options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
});

Then configure Azure Monitor OpenTelemetry in the normal way:

builder.Services.AddOpenTelemetry()
  .UseAzureMonitor();

When a request is being processed, Activity.Current.TraceId is the value that works as the correlation ID. That is the value you can use to compare console or log stream output with what you see in Application Insights.

If you are keeping Serilog for console or file formatting, that is also fine. In that case, enrich the log events from Activity.Current and include those properties in the output template. For example, the output template could include TraceId and SpanId:

[{Timestamp:HH:mm:ss} {Level:u3}] trace={TraceId} span={SpanId} {Message:lj}{NewLine}{Exception}

You can do this with a Serilog enricher package, or with a small custom enricher. The important part is that the log stream is just showing what your application writes to stdout/stderr, so if you want the correlation value visible there, it needs to be rendered as part of the log message format.

For local development, I would usually keep the setup as close to production as practical, but only export to Azure Monitor when a connection string is configured. That lets developers use console or file logs locally without needing to send everything to Azure.

For example:

var openTelemetry = builder.Services.AddOpenTelemetry();

if (!string.IsNullOrWhiteSpace(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
{
  openTelemetry.UseAzureMonitor();
}

With this approach, local development can still use console or file logging, and the logs can still include the same TraceId and SpanId values. In Azure, when the Application Insights connection string is present, the same OpenTelemetry setup exports the correlated telemetry to Azure Monitor.

So the general pattern I would recommend is to treat the OpenTelemetry trace ID as the correlation ID, render it explicitly in console/file logs when you need to see it, and only keep a separate custom correlation ID if there is a specific business requirement for one, such as exposing a friendly request ID back to users or matching an existing external system.

Hope this helps! If my explanation and the information I provided were helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Newest
  1. AI answer

    2026-05-15T13:23:23+00:00

    Azure Monitor’s OpenTelemetry distro already creates and flows correlation identifiers (trace/activity IDs) for requests, dependencies, and logs. The goal is to align logging with those built‑in IDs instead of maintaining a separate custom correlation ID system.

    For ASP.NET Core with Azure.Monitor.OpenTelemetry.AspNetCore:

    1. Configure OpenTelemetry and Azure Monitor

    Use the Azure Monitor distro so that traces, metrics, and logs are all correlated and exported to Application Insights:

    <ItemGroup>
      <PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.3.0-beta.1" />
    </ItemGroup>
    

    In Program.cs (before builder.Build()):

    builder.Services.AddOpenTelemetry(otel =>
    {
        // configure tracing, metrics, logging, etc.
    
        if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
        {
            otel.UseAzureMonitor();
        }
    });
    
    builder.Logging.AddOpenTelemetry(); // route logs through OTel
    

    UseAzureMonitor() wires up the exporters and common instrumentation for ASP.NET Core and .NET so that logs, metrics, and traces share the same trace/activity identifiers and are sent to Application Insights.

    1. Getting the correlation ID into console/log stream output

    The correlation identifier in OpenTelemetry is the current Activity’s trace ID and span ID. When builder.Logging.AddOpenTelemetry is used, log records are correlated with the current Activity automatically. To see those IDs in console output:

    • Configure the console sink (Serilog or built‑in logging) to include the TraceId and SpanId that OpenTelemetry attaches to log scopes.
    • Because all logging is routed through OpenTelemetry, the same IDs that appear in Application Insights will be present in the log record metadata and can be rendered in the console/file format string.

    This way, the “correlation ID” seen in Azure (trace ID) is the same value visible in console/log stream when needed.

    1. Generally accepted configuration for local development

    For local development where console/file logging is primary:

    • Keep the same OpenTelemetry setup (AddOpenTelemetry + UseAzureMonitor) but:
      • Use an Application Insights connection string from a dev resource via APPLICATIONINSIGHTS_CONNECTION_STRING in appsettings.Development.json or environment variables, as shown:
            "AzureMonitor": {
                "ConnectionString": "InstrumentationKey=12345678-abcd-abcd-abcd-12345678..."
            }
        
      • Or omit the connection string to disable export to Azure while still using OpenTelemetry locally.
    • Route all logging through OpenTelemetry (builder.Logging.AddOpenTelemetry) and configure console/file providers to include the trace/activity identifiers in the log format.

    This matches the Modern Web App guidance: use the Azure Monitor OpenTelemetry distro, route all logging through OpenTelemetry, and rely on the built‑in trace/activity identifiers for end‑to‑end correlation across HTTP, Service Bus, and other boundaries.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.