1

I'm trying to replace Microsoft.Extensions.Logging with Serilog in an Azure Function v4 project.

Serilog is working using the configuration below, but it looks like all log entries are "piped through" Microsoft.Extensions.Logging instead of written to the console by Serilog. See the output:

[2024-10-23T13:31:26.843Z] Executing 'Functions.TestTrigger' (Reason='This function was programmatically called via the host APIs.', Id=efe14143-428e-4ac8-a498-1abeae4aa53d)
[2024-10-23T13:31:26.917Z] [15:31:26 INF] Retrieved request

Line 1 is logged by the framework, and line 2 is logged by our code using Serilog. The second timestamp on line 2 is written by Serilog.

Is it possible to have all log entries written directly to the console by Serilog?

Also, if I change the default log level in host.json to anything but Debug, no entries from Serilog is written.

What am I missing?

Program.cs:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService(x => { x.EnableAdaptiveSampling = false; });
        services.ConfigureFunctionsApplicationInsights();
    })
    .ConfigureLogging(loggerBuilder =>
    {
        Log.Logger = new LoggerConfiguration()
            .Enrich.FromLogContext()
            .WriteTo.Console(LogEventLevel.Debug)
            .CreateBootstrapLogger();

        loggerBuilder.AddSerilog(Log.Logger, true);
    })
    .UseSerilog((_, sp, loggerCfg) => loggerCfg
            .Enrich.FromLogContext()
            .WriteTo.Console(LogEventLevel.Debug)
            .WriteTo.ApplicationInsights(sp.GetRequiredService<TelemetryClient>(), TelemetryConverter.Traces))
    .Build();

host.Run();

host.json:

{
  "version": "2.0",
  "logging": {
    "logLevel": {
      "default": "Debug"
    }
  }
}

1 Answer 1

0

We use the Serilog bootstrap logger found in the NuGet package Serilog.AspNetCore. This allows us to see messages while debugging locally. It later allows us to renew the Logger. You can read more about bootstrap logger on Nicholas Blumhardt's blog. We also setup Serilog in the appsettings.json. You should be able to pull out Microsoft.Extensions.Logging from your packages. Hopefully you can use our configuration to help you.

NuGet Packages used:

  • Serilog.AspNetCore
  • Serilog.Enrichers.Environment
  • Serilog.Enrichers.Thread
  • Serilog.Sinks.Console
  • Serilog.Sinks.MSSqlServer
  • Serilog.Sinks.PeriodicBatching
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .CreateBootstrapLogger(); //https://nblumhardt.com/2020/10/bootstrap-logger/
try
{
    Log.Debug("Starting web application.");
    var builder = WebApplication.CreateBuilder(args);

    IConfigurationRoot configuration;
    var configurationBuilder = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
    configuration = configurationBuilder.Build();
    var env = configuration.GetValue(typeof(string), "environmentVariables:ASPNETCORE_ENVIRONMENT")!.ToString();

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
    {
        Args = args,
        EnvironmentName = env,
    });

    builder.Host.UseSerilog((context, services, configuration) => configuration
    .ReadFrom.Configuration(context.Configuration)
    .ReadFrom.Services(services)
    .Enrich.FromLogContext()
    );

    var app = builder.Build();
    /* Setup App i.e. app.UseAuthentication(); */

    app.UseSerilogRequestLogging(options =>
    {
        // Customize the message template
        options.MessageTemplate = "{RequestPath} responded {StatusCode} in {Elapsed}";

        // Emit debug-level events instead of the defaults
        options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;

        // Attach additional properties to the request completion event
        options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
        {
            diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
            diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
            //diagnosticContext.Set("StatusCode", httpContext.Response.StatusCode);
        };
    });

    app.Run();
{
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.MSSqlServer" ],
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft.AspNetCore": "Warning",
        "Microsoft": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "MSSqlServer",
        "Args": {
          "connectionString": "*****",
          "sinkOptionsSection": {
            "tableName": "logTableName",
            "schemaName": "user",
            "autoCreateSqlTable": false,
            "batchPostingLimit": 1000,
            "period": "0.00:00:30"
          },
          "restrictedToMinimumLevel": "Debug",
          "columnOptionsSection": {
            "disableTriggers": true,
            "clusteredColumnstoreIndex": false,
            "primaryKeyColumnName": "Id",
            "addStandardColumns": [ "LogEvent" ],
            "additionalColumns": [
              {
                "ColumnName": "EnvironmentUserName",
                "DataType": "nvarchar",
                "DataLength": 120,
                "AllowNull": true
              },
              {
                "ColumnName": "MachineName",
                "DataType": "nvarchar",
                "DataLength": 120,
                "AllowNull": true
              },
              {
                "ColumnName": "ProcessId",
                "DataType": "int",
                "AllowNull": true
              },
              {
                "ColumnName": "ThreadId",
                "DataType": "int",
                "AllowNull": true
              },
              {
                "ColumnName": "BatchId",
                "DataType": "nvarchar",
                "DataLength": 120,
                "AllowNull": true
              },
              {
                "ColumnName": "ApplicationName",
                "DataType": "nvarchar",
                "DataLength": 120,
                "AllowNull": true
              },
              {
                "ColumnName": "CorrelationId",
                "PropertyName": "ConnectionId",
                "DataType": "nvarchar",
                "DataLength": 120,
                "AllowNull": true
              }
            ],
            "id": {
              "columnName": "Id",
              "nonClusteredIndex": true
            },
            "message": { "columnName": "Message" },
            "messageTemplate": { "columnName": "MessageTemplate" },
            "level": {
              "columnName": "Level",
              "storeAsEnum": false
            },
            "timeStamp": {
              "columnName": "TimeStamp",
              "convertToUtc": true
            },
            "exception": { "columnName": "Exception" },
            "properties": {
              "columnName": "Properties",
              "excludeAdditionalProperties": true,
              "dictionaryElementName": "dict",
              "itemElementName": "item",
              "omitDictionaryContainerElement": false,
              "omitSequenceContainerElement": false,
              "omitStructureContainerElement": false,
              "omitElementIfEmpty": true,
              "propertyElementName": "prop",
              "rootElementName": "root",
              "sequenceElementName": "seq",
              "structureElementName": "struct",
              "usePropertyKeyAsElementName": false
            },
            "logEvent": {
              "columnName": "LogEvent",
              "excludeAdditionalProperties": true,
              "excludeStandardColumns": true
            }
          }
        }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "WithEnvironmentUserName" ],
    "Properties": {
      "ApplicationName": "My Cool Website"
    }
  },
  "AllowedHosts": "*"
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks J Man. I'm also using the bootstrap logger. I fail to see how this is related to Azure Functions?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.