How to setup Serilog with Azure Functions v4 correctly?

Viewed 3880

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

2 Answers

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.

For those that got here because they can't properly config their logging in an azure function with the app insights sink this is what works for me:

private static void ConfigureLogging(IServiceCollection services)
{
   Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Information()
        .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
        .MinimumLevel.Override("System", LogEventLevel.Warning)
        .MinimumLevel.Override("Worker", LogEventLevel.Warning)
        .MinimumLevel.Override("Host", LogEventLevel.Warning)
        .MinimumLevel.Override("Function", LogEventLevel.Warning)
        .MinimumLevel.Override("Azure", LogEventLevel.Warning)
        .MinimumLevel.Override("DurableTask", LogEventLevel.Warning)
        .Enrich.FromLogContext()
        .Enrich.WithExceptionDetails()
        .WriteTo.ApplicationInsights(
            TelemetryConfiguration.CreateDefault(),
            TelemetryConverter.Events,
            LogEventLevel.Information)
        .CreateLogger();
        services.AddLogging(configure => configure.AddSerilog(Log.Logger));
}

The example here at the time of writing doesn't seem to work. The logging scope doesn't get captured in the output.

Serilog Version: 2.11.0

Serilog.Sinks.ApplicationInsights Version: 4.0.0

Linked example for the future:

[assembly: FunctionsStartup(typeof(MyFunctions.Startup))]
namespace MyFunctions
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton<ILoggerProvider>((sp) => 
            {
                Log.Logger = new LoggerConfiguration()
                    .Enrich.FromLogContext()
                    .WriteTo.ApplicationInsights(sp.GetRequiredService<TelemetryClient>(), TelemetryConverter.Traces)
                    .CreateLogger();
                return new SerilogLoggerProvider(Log.Logger, true);
            });
        }
    }
}
Related