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://github.com/serilog/serilog
Learning the Kusto Query Language (KQL) with Application Insights Logs

[…] ASP.NET Core Logging using Serilog and Azure (Damien Bowden) […]
[…] ASP.NET Core Logging using Serilog and Azure – Damien Bowden […]
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
Thanks Tory
I’ll update and fix
Greetings Damien
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.
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?
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.
Just curious, where are you providing the connection string for the Serilog App Insights sink?
This is in the App Service configuration itself, or through the terraform setup
Does anyone have an example of putting the Serilog Configuration settings into the Azure Function Environment variables?