Reputation: 5503
I've been struggling trying to get Serilog
to send telemetry over to the .NET Aspire
dashboard. Once the Aspire
dashboard opens, the Structured logs
tab is always empty.
If I comment out the Serilog
configuration in Program.cs
(thus using the default ILogger
) then I start seeing structured logs on the dashboard.
There are currently two issues on the Aspire
repo which didn't seem to help me:
This is where our Serilog configuration stands at the moment:
// This extension is auto generated when adding Aspire. More on that below..
builder.AddServiceDefaults();
builder.Host.UseSerilog((hostContext, services, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.Enrich.WithPropertiesMasking(builder.Configuration)
.Enrich.WithProperty("ServiceName", DaprConstants.API_SERVICE_NAME)
.Enrich.WithThreadId()
.Enrich.WithCorrelationIdHeader("X-Correlation-ID")
.WriteTo.ApplicationInsights
(
new TelemetryConfiguration() { ConnectionString = builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"] },
TelemetryConverter.Traces
)
.WriteTo.OpenTelemetry(opts =>
{
opts.Endpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]!;
opts.ResourceAttributes.Add("service.name", builder.Configuration["OTEL_SERVICE_NAME"] ?? "Unknown");
})
);
If you're unfamiliar with some of those environment variables you can read more about it here: .NET Aspire telemetry
From the GitHub
issues I linked to above I've also modified the Extensions
within the ServiceDefaults
project that gets auto generated when you add Aspire
to your application. I should note that I also tried keeping all the configuration in the Extensions
class the same as well.
public static class Extension
{
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
{
builder.ConfigureOpenTelemetry();
/* Other configuration... */
return builder;
}
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
{
// Tried commenting this out.
/*builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});*/
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
if (builder.Environment.IsDevelopment())
{
// We want to view all traces in development
tracing.SetSampler(new AlwaysOnSampler());
}
tracing.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
});
builder.AddOpenTelemetryExporters();
return builder;
}
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
if (useOtlpExporter)
{
// In one of the GitHub issues David Fowler suggested commenting this line out.
//builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
}
if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
{
builder.Services.AddOpenTelemetry()
.UseAzureMonitor();
}
return builder;
}
}
I didn't show the AppHost
here as I didn't think it mattered to the aforementioned issue.
Has anyone who is currently trying out Aspire
with Serilog been able to get this to work correctly?
If so, any pointers?
Upvotes: 5
Views: 3765
Reputation: 31
This is automatically supported now with little effort:
dotnet add package Serilog.Sinks.OpenTelemetry
Setup your serilog configuration in your appsettings.json file or appsettings.development.json file. The key here is to configure to WriteTo OpenTelemetry sink.
"Serilog": {
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "log.log",
"rollingInterval": "Day"
}
},
{
"Name": "OpenTelemetry"
}
]
},
Finally, add this to the WebApplicationBuilder
builder.Host.UseSerilog((_, config) => config.ReadFrom.Configuration(builder.Configuration));
Profit Aspire Structured Logs Screenshot
Upvotes: 3
Reputation: 1133
I was facing the same issue, after digging GitHub posts, I am able to wire up Serilog
with Aspire
dashboard's Structured Logs
tab. I found this at here on GitHub and thanks to David Fowler's reply I am able to wired it up properly.
builder.Host.UseSerilog((ctx, lc ) => lc
.Enrich.FromLogContext()
.WriteTo.OpenTelemetry(options =>
{
options.Endpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
var headers = builder.Configuration["OTEL_EXPORTER_OTLP_HEADERS"]?.Split(',') ?? [];
foreach (var header in headers)
{
var (key, value) = header.Split('=') switch
{
[string k, string v] => (k, v),
var v => throw new Exception($"Invalid header format {v}")
};
options.Headers.Add(key, value);
}
options.ResourceAttributes.Add("service.name", "apiservice");
//To remove the duplicate issue, we can use the below code to get the key and value from the configuration
var (otelResourceAttribute, otelResourceAttributeValue) = builder.Configuration["OTEL_RESOURCE_ATTRIBUTES"]?.Split('=') switch
{
[string k, string v] => (k, v),
_ => throw new Exception($"Invalid header format {builder.Configuration["OTEL_RESOURCE_ATTRIBUTES"]}")
};
options.ResourceAttributes.Add(otelResourceAttribute, otelResourceAttributeValue);
})
.ReadFrom.Configuration(ctx.Configuration)
);
This is how it looks on Aspire Dashboard under Structured Logs
tab.
After updating code to remove duplicate. This is how it looks under the Aspire
dashboard.
Upvotes: 5