Search code examples
c#azure-functionsserilog.net-6.0datadog

How to setup Serilog with Azure Functions v4 correctly?


I want to use Serilog in an Azure Function v4 (.net 6) (the logs should be sent to Datadog). For this I have installed the following nuget packages:

<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Datadog.Logs" Version="0.3.5" />

Below is the configuration in the Startup.cs class:

public override void Configure(IFunctionsHostBuilder builder)
{
  builder.Services.AddHttpClient();
  
  //... adding services etc.

  Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .MinimumLevel.Override("Worker", LogEventLevel.Warning)
    .MinimumLevel.Override("Host", LogEventLevel.Warning)
    .MinimumLevel.Override("System", LogEventLevel.Error)
    .MinimumLevel.Override("Function", LogEventLevel.Error)
    .MinimumLevel.Override("Azure.Storage.Blobs", LogEventLevel.Error)
    .MinimumLevel.Override("Azure.Core", LogEventLevel.Error)
    .Enrich.WithProperty("Application", "Comatic.KrediScan.AzureFunctions")
    .Enrich.FromLogContext()
    .WriteTo.DatadogLogs("XXXXXXXXXXX", configuration: new DatadogConfiguration() { Url = "https://http-intake.logs.datadoghq.eu" }, logLevel:   LogEventLevel.Debug)
    .WriteTo.Console()
    .CreateLogger();

  builder.Services.AddSingleton<ILoggerProvider>(sp => new SerilogLoggerProvider(Log.Logger, true));

  builder.Services.AddLogging(lb =>
  {
    //lb.ClearProviders(); //--> if used nothing works...
    lb.AddSerilog(Log.Logger, true);
  });

Basically logging works, but all log statements are written twice (with a few milliseconds difference, Datadog and Console).

enter image description here

Obviously I am doing something fundamentally wrong with the configuration. I don't use appsettings.json, the configuration of Serilog takes place exclusively in the code. I have scoured the entire internet and read just about every article on Serilog and Azure Functions. On Stackoverflow I also read virtually every question about it and tried all the answers. Unfortunately, so far without success.

SO-Questions for example: Use Serilog with Azure Log Stream
How do I use Serilog with Azure WebJobs?
Serilog enricher Dependency Injection with Azure Functions
https://github.com/hgmauri/sample-azure-functions/blob/main/src/Sample.AzureFunctions.DotNet31/Startup.cs

Is there any example for setting up Serilog with Azure Functions v4 / .net 6?

Thanks a lot for the help!
Michael Hachen


Solution

  • Got it! After replacing all ILogger with ILogger<T> and removing the line builder.Services.AddSingleton<ILoggerProvider>(sp => new SerilogLoggerProvider(Log.Logger, true)); everything worked as expected.

    Startup.cs

    Log.Logger = new LoggerConfiguration()
              .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
              .MinimumLevel.Override("Worker", LogEventLevel.Warning)
              .MinimumLevel.Override("Host", LogEventLevel.Warning)
              .MinimumLevel.Override("System", LogEventLevel.Error)
              .MinimumLevel.Override("Function", LogEventLevel.Error)
              .MinimumLevel.Override("Azure.Storage.Blobs", LogEventLevel.Error)
              .MinimumLevel.Override("Azure.Core", LogEventLevel.Error)
              .Enrich.WithProperty("Application", $"xxxxx.AzureFunctions.{builder.GetContext().EnvironmentName}")
              .Enrich.FromLogContext()
              .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                .WithDefaultDestructurers()
                .WithDestructurers(new[] { new SqlExceptionDestructurer() }))
              .WriteTo.Seq(builder.GetContext().EnvironmentName.Equals("Development", StringComparison.OrdinalIgnoreCase) ? "http://localhost:5341/" : "https://xxxxxx.xx:5341/", LogEventLevel.Verbose)
              .WriteTo.Console(theme: SystemConsoleTheme.Literate)
              .CreateLogger();
          
          builder.Services.AddLogging(lb =>
          {
            lb.AddSerilog(Log.Logger, true);
          });