Application Insights already gives an ASP.NET Core application request, dependency, and exception telemetry. Serilog gives us structured application logging. Connecting the two means every log event can be searched beside the request that produced it, without maintaining a second logging destination.
In this walkthrough, we will replace the default ASP.NET Core logging pipeline with Serilog, send Serilog events to Azure Application Insights, and keep correlation data such as operation IDs intact.
What we are building
The final flow is simple:
- Application code writes through
ILogger<T>or Serilog. - Serilog enriches and filters the event.
- The Application Insights sink converts the event to trace telemetry.
- Azure Monitor stores it in the Application Insights resource.
- Requests, dependencies, exceptions, and logs can be queried together in Log Analytics.
1. Create or choose an Application Insights resource
In the Azure portal, open your Application Insights resource and copy its Connection String from the Overview page. Use a connection string rather than the older instrumentation key-only setup.
For local development, add the value to user secrets:
dotnet user-secrets init dotnet user-secrets set "ApplicationInsights:ConnectionString" "InstrumentationKey=...;IngestionEndpoint=..."
In Azure App Service, Container Apps, or another hosted environment, store the same value in the platform configuration as APPLICATIONINSIGHTS_CONNECTION_STRING. Do not commit it to source control.
2. Install the packages
dotnet add package Serilog.AspNetCore dotnet add package Serilog.Sinks.ApplicationInsights dotnet add package Microsoft.ApplicationInsights.AspNetCore dotnet add package Serilog.Settings.Configuration
Serilog.AspNetCore connects Serilog to the ASP.NET Core host. The Application Insights sink sends structured events as trace telemetry, while the Microsoft package collects the surrounding web telemetry.
3. Configure Program.cs
The following example targets the minimal hosting model used by modern ASP.NET Core applications:
using Microsoft.ApplicationInsights.Extensibility;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.ApplicationInsights.TelemetryConverters;
var builder = WebApplication.CreateBuilder(args);
var connectionString =
builder.Configuration["ApplicationInsights:ConnectionString"]
?? builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]
?? throw new InvalidOperationException(
"The Application Insights connection string is not configured.");
builder.Services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = connectionString;
});
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", builder.Environment.ApplicationName)
.Enrich.WithProperty("Environment", builder.Environment.EnvironmentName)
.WriteTo.Console()
.WriteTo.ApplicationInsights(
connectionString,
TelemetryConverter.Traces)
.CreateLogger();
builder.Host.UseSerilog();
builder.Services.AddControllers();
var app = builder.Build();
app.UseSerilogRequestLogging();
app.MapControllers();
try
{
Log.Information("Starting {ApplicationName}",
builder.Environment.ApplicationName);
app.Run();
}
catch (Exception exception)
{
Log.Fatal(exception, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
The important line is builder.Host.UseSerilog(). It replaces the default logging providers, so logs written through ASP.NET Core’s ILogger<T> also pass through Serilog. This is how we avoid two separate logging pipelines.
4. Log structured properties
Avoid interpolated strings for important values. Message templates preserve each value as a searchable custom property:
public sealed class InventoryService(
ILogger<InventoryService> logger)
{
public void Reserve(string sku, int quantity)
{
logger.LogInformation(
"Reserving {Quantity} units of {Sku}",
quantity,
sku);
}
}
In Application Insights, Quantity and Sku appear in customDimensions. That is much more useful than searching a fully rendered text message.
5. Verify the telemetry
Run the application, call an endpoint that writes a log, wait briefly for ingestion, and open Logs in the Application Insights resource. Start with this Kusto query:
traces
| where timestamp > ago(30m)
| order by timestamp desc
| project timestamp,
severityLevel,
message,
operation_Id,
customDimensions
To find a particular structured property:
traces | where timestamp > ago(30m) | extend Sku = tostring(customDimensions["Sku"]) | where Sku == "ABC-123" | project timestamp, message, Sku, operation_Id
The shared operation_Id is the key to correlation. Use it to join a trace to its request, dependencies, and exceptions:
let operationId = "paste-operation-id-here"; union requests, dependencies, exceptions, traces | where operation_Id == operationId | order by timestamp asc
A note about OpenTelemetry
For a new, vendor-neutral observability platform, OpenTelemetry may be the better long-term foundation. Serilog remains a practical fit when an application already uses its enrichers, sinks, and message-template ecosystem. The approach here is intentionally direct: Serilog owns application logs, while the Application Insights SDK supplies the wider ASP.NET Core telemetry and correlation.
Final result
With this setup, calls to ILogger<T> and Serilog flow to Application Insights as structured traces. You get one searchable location for application events, request context, dependency calls, and exceptions—and a much shorter path from an alert to the log event that explains it.
