I am configuring DI as follows in my .NET Core 3.1 Console application:
services.AddSingleton<ITelemetryInitializer, TelemetryInitializer>();
services.AddApplicationInsightsTelemetryWorkerService(instrumentationKey);
In appSettings.json
I have:
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.ApplicationInsights"
],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "ApplicationInsights",
"Args": {
"restrictedToMinimumLevel": "Information",
"InstrumentationKey": "...",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
Serilog is configured as follows:
Host.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)
)
.UseConsoleLifetime()
.Build()
.Run()
My initializer class:
public class TelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "something-something";
}
}
The Initialize
method however is not being called when I log using ILogger
. If I resolve TelemetryClient
directly and log using that, Initialize
is called.
Am I missing some configuration for Serilog?
It seems to be a glitch of Serilog.Sinks.ApplicationInsights
. When initializing sink from config (code reference), it creates a new TelemetryClient
which results in an active TelemetryConfiguration
before the registered TelemetryInitializer
could be associate with it.
To get the TelemetryInitilizer
injected with Active configuration, it needs to have the TelemetryClient
instance created once before the Serilog initializes the App Insights sink. Otherwise it would be too late to respect it since internally AddApplicationInsightsTelemetryWorkerService
registers TelemetryClient
as Singleton. So below is my working code which just makes a dummy resolve of TelemetryClient
before initializing loggerConfiguration of Serilog.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddApplicationInsightsTelemetryWorkerService(instrumentationKey);
services.AddSingleton<ITelemetryInitializer, TelemetryInitializer>();
services.AddHostedService<Worker>();
})
.UseSerilog((hostingContext, serviceProvider, loggerConfiguration) => {
serviceProvider.GetRequiredService<TelemetryClient>(); // just a dummy resolve
loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration);
})
.UseConsoleLifetime();
}
Option 2 is to create a custom TelemetryConverter which can update required context instead of using the Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter
. That would remove the reliance on initializer.
Option 3 is to switch to code based setup instead of configuration which would allow you to pass TelemetryClient
or TelemetryConfiguration
instance like https://github.com/serilog/serilog-sinks-applicationinsights#configuring. But that may not be as declarative as configuration based approach.