12

For my ASP.NET 5 Web Project I want to log to Application Insights via Serilog. Now I finally got everything working, but I cannot belive this is they way to go. I hope you can help me to improve it.

The main problem I see, is that the Instrumentation Key of Application Insights is both configured in the Serilog and ApplicationInsights section of appsettings.json. I would expect Serilog to use the default/active Client (and configuration), but instead it seems to create a second Client based on its own configuration.

Documentation at https://github.com/serilog/serilog-sinks-applicationinsights says:

As mentioned above you can also pass an instrumentation key but it's actively discouraged

If I remove the instrumentationKey setting from the Serilog section in appsettings.json, it doesn't log any Serilog statements to Application Insights anymore. To solve this issue, I saw you can use .WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces), but I don't want hard-coded configuration and prefer to do it via appsettings.json. Also, I am affraid having two Clients/configurations, does not give the best performance.

Now, is there any way to do the configuration via appsettings.json and not have to duplicate the instrumentation key?

Program.cs

public static void Main(string[] args)
{
    LoggingExtensions.SetupLoggerConfiguration(AppName, AppVersion);

    try
    {
        Log.Information("Starting web host for {0}", AppName);
        CreateHostBuilder(args).Build().Run();
    }
    catch (Exception ex)
    {
        Log.Fatal(ex, "Host terminated unexpectedly");
    }
    finally
    {
        Log.CloseAndFlush();
    }
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog((hostBuilderContext, loggerConfiguration) =>
        {
            loggerConfiguration.ConfigureBaseLogging(
                hostBuilderContext.Configuration, AppName, AppVersion);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

public static string AppName => typeof(Program).Assembly.GetName().Name;
public static Version AppVersion => typeof(Program).Assembly.GetName().Version;

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddApplicationInsightsTelemetry(Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
    ...
}

LoggingExtensions.cs

internal static void SetupLoggerConfiguration(string appName, Version appVersion)
{
    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
        .Build();
    
    Log.Logger = new LoggerConfiguration()
        .ConfigureBaseLogging(configuration, appName, appVersion)
        .CreateLogger();
}

internal static LoggerConfiguration ConfigureBaseLogging(
    this LoggerConfiguration loggerConfiguration,
    IConfiguration configuration,
    string appName,
    Version appVersion
)
{
    loggerConfiguration
        .ReadFrom.Configuration(configuration)
        .Enrich.WithProperty("ApplicationVersion", appVersion.ToString())
        .Enrich.WithProperty("ApplicationName", appName);

    return loggerConfiguration;
}

HomeController.cs

private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
    _logger = logger;
}

public ActionResult Index()
{
    _logger.LogInformation("Home page visit");
    return View();
}

appsettings.json

{
  "Serilog": {
    "Using": [
      "Serilog.Sinks.Console",
      "Serilog.Sinks.ApplicationInsights"
    ],
    "MinimumLevel": {
      "Default": "Information"
    },
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
        }
      },
      {
        "Name": "ApplicationInsights",
        "Args": {
          "restrictedToMinimumLevel": "Information",
          "instrumentationKey": "xxx",
          "telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
        }
      }
    ],
    "Enrich": [
      "FromLogContext",
      "WithMachineName",
      "WithThreadId"
    ]
  },
  "ApplicationInsights": {
    "ConnectionString": "InstrumentationKey=xxx;IngestionEndpoint=https://xxx.in.applicationinsights.azure.com/"
  }
}

Update 1

Seems that the ApplicationInsights sink relies on TelemetryConfiguration.Active which is deprecated and probably not set in .NET 5. https://github.com/serilog/serilog-sinks-applicationinsights/issues/141

For now, I used the solution at https://github.com/serilog/serilog-sinks-applicationinsights/issues/121#issuecomment-798849183, but please if someone has a better solution... tell me!

Update 2

Found a better solution for now at https://github.com/serilog/serilog-sinks-applicationinsights/issues/156 The deprecated TelemetryConfiguration.Active can be used by setting this option in startup.cs:

services.AddApplicationInsightsTelemetry(opt => opt.EnableActiveTelemetryConfigurationSetup = true);
// No need to set InstrumentationKey here as you can do this by setting 
// { "ApplicationInsights": { "InstrumentationKey": "xxx" } }
// in appsettings.json

Also add the following on top of the Program.cs as described on https://nblumhardt.com/2020/10/bootstrap-logger/

Log.Logger = new LoggerConfiguration()
                .WriteTo.Console()
                .CreateBootstrapLogger();
Vincent Bitter
  • 1,000
  • 1
  • 6
  • 15

0 Answers0