ASP.NET Core Logging using Serilog and Azure

This article shows how to implement logging in an ASP.NET Core application using Serilog and Azure as a hosting environment.

Code: https://github.com/damienbod/aspnetcore-azure-logging

History

  • 2024-01-27 Updated log path as defined by Azure
  • 2024-01-14 Updated and remove AzureApp sink.

Priority logging use cases

Two types of default logging use cases need to be supported in most software solutions. The application requires near real time logs which can be easily viewed and persisted logs for a period of time which can be viewed after something goes wrong. This needs to work in development and also in production. We have many other logging use cases as well, I just want to cover the basic logging requirements here.

  • Real time logging development
  • Real time logging production
  • Persisted logging development
  • Persisted logging production

In this setup, I use Azure App Insights for persisted logging on the production server. This has the problem that the logs only appear after n-minutes. A near real view is also required.

Setup

The ASP.NET Core application is deployed to an Azure App Service (Linux hosted). I like to use this as it is simple and scales good with web applications. I use Serilog to add logging to my ASP.NET Core applications and add different sinks depending on the hosted environments of the dev, test, prod etc.

The following Nuget packages are used to setup the logging in this use case.

Serilog.AspNetCore
Serilog.Enrichers.Environment
Serilog.Enrichers.Thread
Serilog.Sinks.Async
Serilog.Sinks.ApplicationInsights

The program file of the ASP.NET Core application is used to initialize the logging. I use just one configuration for both dev and production in this example. I normally use different configurations pro hosted environment. The Serilog is setup using the CreateBootstrapLogger and the UseSerilog methods. The UseSerilog reads the sinks and logging setup from a configuration file, or files if separated per environment.

using Serilog;
using AspNetCoreAzureLogging;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

Log.Information("Starting AspNetCoreAzureLogging application");

try
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog((context, loggerConfiguration) => loggerConfiguration
        .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}")
        .ReadFrom.Configuration(context.Configuration));

    // ... Add here:
	// define your services and pipelines

    app.Run();
}
catch (Exception ex) when (ex.GetType().Name is not "StopTheHostException" && ex.GetType().Name is not "HostAbortedException")
{
    Log.Fatal(ex, "Unhandled exception");
}
finally
{
    Log.Information("Shut down complete");
    Log.CloseAndFlush();
}

The Serilog configuration adds the Azure and file based setup. The console or the file are used for local development, the Azure App Insights is used for the persisted logs on Azure. Depending on what services you deploy, the near real time logging is setup differently.

"Serilog": {
  "Using": [ "Serilog.Sinks.ApplicationInsights" ],
  "MinimumLevel": {
    "Default": "Debug",
    "Override": {
      "Microsoft": "Debug",
      "System": "Debug"
    }
  },
  "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
  "WriteTo": [
    {
      "Name": "ApplicationInsights",
      "Args": {
        "telemetryConverter": "Serilog.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
      }
    },
    {
      "Name": "File",
      "Args": {
        "path": "../../LogFiles/_logs-uifile.txt",
        "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] [{SourceContext}] [{EventId}] {Message}{NewLine}{Exception}",
        "rollOnFileSizeLimit": true,
        "fileSizeLimitBytes": 4194304,
        "retainedFileCountLimit": 5
      }
    }
  ]
},

Logging to App Insights

App Insights need to be enabled for the Azure App Service which uses a Linux hosting plan. This can be created with it enabled, or you can use the portal to enable this.

Note: this can all be setup using IaC like terraform or whatever tools you use.

The App Insights can be opened and the exceptions or the traces can be viewed in the Logs tab.

You can use KDL to view the traces or the exceptions from the application logs.

Near real time logging App Service

Azure App Insights logs do not appear for n-minutes. Due to this, sometimes you need to enable near real time logs to debug a bug on Azure deployments. This can be done using the Azure Service Logs blade. Enable the File System logs.

The log stream can be used to view thenear real time logs view.

On a development system, I use the log files or the console to debug the application. I do not use App Insights in the development environment if this can be avoided as it adds complexity and delays in development.

Notes

This just covers the two default logging requirements which can be used for development and production. There are many other logging features which can be implemented and monitored but these two basic ones should always be supported and working in all solutions. You can also use logging solutions like Seq server or Elasticsearch, main thing is to use one.

Links

https://github.com/rufer7/aspnetcore-serilog-azureappservice

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging

https://learn.microsoft.com/en-us/azure/azure-monitor/app/ilogger

https://learn.microsoft.com/en-us/azure/azure-monitor/app/tutorial-asp-net-core

https://serilog.net/

https://github.com/serilog/serilog

Learning the Kusto Query Language (KQL) with Application Insights Logs

10 comments

  1. […] ASP.NET Core Logging using Serilog and Azure (Damien Bowden) […]

  2. […] ASP.NET Core Logging using Serilog and Azure – Damien Bowden […]

  3. Tory's avatar

    Just a heads up Serilog.AspNetCore depends on many of those packages so you do not need to directly depend on them. https://github.com/serilog/serilog-aspnetcore/blob/dev/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj#L25-L35

    1. damienbod's avatar

      Thanks Tory

      I’ll update and fix

      Greetings Damien

  4. Pure Krome's avatar
    Pure Krome · · Reply

    Another easy ‘localhost’ trick I use here is that I add in the Serilog.Seq library for my localhost. I add this via the appsettingsfile:

    “Using”: [ “Serilog.Sinks.Seq” ],
    “WriteTo”: [
    {
    “Name”: “Console”
    },
    {
    “Name”: “Seq”,
    “Args”: {
    “serverUrl”: “http://localhost:5301”,
    “restrictedToMinimumLevel”: “Information”
    }
    }
    ],

    and finally -> what is this Seq thingy? I have that as part of my docker-compose.yml file .. but here’s a simple docker run command:

    docker run -rm -p 5300:80 -p 5301:5341 datalust/seq

    or something like that.

    gives some extra help if the console output lines are just not enough. seq is great for semantic logging and ‘searching’ for specific log data.

    1. Brandon Duncan's avatar
      Brandon Duncan · · Reply

      Pure Krome! Do you have an example of putting the serilog configuration settings into the Azure Function Environment variables such that .UserSerilog….ReadFrom.Configuration(hostingContext.Configuration) can read the configuration values from the Environment variables?

      1. purekrome's avatar
        purekrome · ·

        Does this link help: https://github.com/Azure/azure-functions-host/issues/4577#issuecomment-1028439830 ??

        basically, just add serilog normally like you’re sorta doing above. Except with Functions it doesn’t have (by default) the appsettings.ENV.json files. Instead of has the local.settings.json file. That link shows how to use that.

  5. D's avatar

    Just curious, where are you providing the connection string for the Serilog App Insights sink?

    1. damienbod's avatar

      This is in the App Service configuration itself, or through the terraform setup

  6. Brandon Duncan's avatar
    Brandon Duncan · · Reply

    Does anyone have an example of putting the Serilog Configuration settings into the Azure Function Environment variables?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.