Search code examples
c#loggingconfigurationazure-functionsserilog

How to write logs to a specific sink depending on the developement environment in Serilog?


I'm using Serilog with Azure Functions v4 and .NET 7. I am trying to write logs to both the local console and Application Insights. I already spent hours trying different solutions and the situation at the time of writing seems to be that this technology stack (Azure Functions + Application Insights) is not mature or too recent, and not yet very well documented.

Setting up appsettings.{env}.json files by environment and trying to add a different Serilog config by environment does not work with Azure Functions. Depending on the solution I tried, either Microsoft logger is used or there are no logs.

The code below is working for the console but I get duplicate logs on Application Insights. And if I remove Console sink, it works well in Application Insights but I get no more local logs:

.UseSerilog((hostBuilderContext, serviceProvider, loggerConfiguration) =>
    loggerConfiguration
        .MinimumLevel.Debug()
        .WriteTo.Console()
        .WriteTo.ApplicationInsights(
            serviceProvider.GetRequiredService<TelemetryConfiguration>(),
            TelemetryConverter.Traces)
)

I also tried other ways of setting up the logger, for example in .ConfigureServices(..., injecting it differently in the function code, but no luck. Many samples talk about a Startup method that I don't have. If it helps I can post the whole Program.cs and the function code here.

So I now added .Enrich.WithEnvironmentName() to get the environment name from an environment variable, and the question is: how can I filter the logs using ByIncludingOnly in the code above (not config file) to include logs depending on the environment name (Local or Development)? Same question for ByExcluding - I guess answer will be similar.


Solution

  • This is the way to do it. I ended up not using the Environment enricher because it is at the time of writing not supporting the Azure environment variable for Azure Functions (AZURE_FUNCTIONS_ENVIRONMENT). See this issue:

    https://github.com/serilog/serilog-enrichers-environment/issues/49

    This is how I got this working:

    First option enriching the log event:

    // Careful: Serilog is not able to enrich with the value of this variable and will default to 'Production'
    
    var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
    var predicate = $"EnvironmentName = 'Local'";
    
    Log.Logger = new LoggerConfiguration()
        .Enrich.WithProperty("EnvironmentName", value)
        .MinimumLevel.Debug()
    
        .WriteTo.Logger(lc => lc
            .Filter.ByIncludingOnly(predicate)
            .WriteTo.Console(outputTemplate: "{Properties} {Message}"))
    
        .WriteTo.Logger(lc => lc
            .WriteTo.File(path: Directory.GetCurrentDirectory() + @"\test.txt"))
        
        .CreateLogger();
    

    Second option without enriching the log event:

    var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
    var predicate = $"{value} = 'Local'";
    
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
    
        .WriteTo.Logger(lc => lc
            .Filter.ByIncludingOnly(predicate)
            .WriteTo.Console(outputTemplate: "{Properties} {Message}"))
    
        .WriteTo.Logger(lc => lc
            .WriteTo.File(path: Directory.GetCurrentDirectory() + @"\test.txt"))
        
        .CreateLogger();