Author : MD TAREQ HASSAN | Updated : 2020/09/17
Logging using Serilog
Serilog / Logging in ASP.Net Core
Azure App Service Logging
- Application log can be seen in “Azure log streaming”
- Application logs can be stored in Filesystem or Blob Storage
- Links:
Enable App Service Logs
- Azure Portal > App Service > App Service logs
- Select On for either Application Logging (Filesystem) or Application Logging (Blob), or both.
File System Logging
- The Filesystem option is for temporary debugging purposes, and turns itself off in 12 hours. The Blob option is for long-term logging
- Access log files: https://docs.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#access-log-files
- Application logs in Kudu
- The default location for log files :
D:\home\LogFiles\Application
- the default file name is
diagnostics-yyyymmdd.txt
- The default location for log files :
Blog Storage Logging
- https://www.nuget.org/packages/Serilog.Sinks.AzureBlobStorage/
- https://github.com/chriswill/serilog-sinks-azureblobstorage
- https://stackoverflow.com/questions/51959089/logging-asp-net-core-on-azure-via-serilog
App Service Logging with Serilog
- In asp.net core, Serilog will override default logger (Serilog will become default logger)
- Serlog console sink output will be in App Service Log Stream
- Serilog file sink output will be in: App Service > path:
"%HOME%/LogFiles/Application/<log-file-name-here>"
%HOME%
: default folder of App Service, varies depending on machine i.e for Windows%HOME%
is “D:\home”- Serilog file path configuration in
appsetting.json
=>"path": "%HOME%/LogFiles/Application/FooApp.ndjson"
- Links:
appsettings.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Warning"
},
"Enrich": [
"FromLogContext"
],
"Properties": {
"Application": "HovermindApp"
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u10}] {Message:lj}",
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Literate, Serilog.Sinks.Console"
}
},
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
"path": "%HOME%/LogFiles/Application/FooApp.ndjson",
"shared": "true",
"fileSizeLimitBytes": "1000000",
"rollOnFileSizeLimit": true,
"flushToDiskInterval": "1"
}
}
]
}
}
]
}
}
Program.cs
using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace BlazorAppDefaultScaffoling
{
public class Program
{
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(Configuration).CreateLogger();
try
{
Log.Warning("Running Blazor App");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).UseSerilog();
}
}
Using Fluent API (instead of appsettings.json
)
loggerConfiguration.WriteTo.File(
@"D:\home\LogFiles\Application\trace.log",
fileSizeLimitBytes: 5_000_000,
rollOnFileSizeLimit: true,
shared: true,
flushToDiskInterval: TimeSpan.FromSeconds(1));
Log.Logger = loggerConfiguration
.Enrich.WithMachineName()
.Enrich.WithProcessId()
.Enrich.FromLogContext()
.CreateLogger();
Viewing App Service Logs
Console Logs
- Azure portal > App services
- Then select your app service i.e. Foo App Service > Log Steram
File logs
- Azure portal > App services
- Then select your app service i.e. Foo App Service > Advanced Tool > Click “GO ->”
- Debug Console > CMD > LogFIles > Apllication >
FooApp.ndjson
- Links:
Logging to Azure Blob Storage using Serilog
- See: Installing Serilog in AspNetCore
- Azure Blob Storage Sink
- Github Repository: https://github.com/chriswill/serilog-sinks-azureblobstorage
- List of other sinks: https://github.com/serilog/serilog/wiki/Provided-Sinks
- StackOverflow Question
- Serilog and Azure Blob Storage: https://stackoverflow.com/questions/51959089/logging-asp-net-core-on-azure-via-serilog
- Working answer: https://stackoverflow.com/a/51963534
Installation
Install-Package Serilog.Sinks.AzureBlobStorage
JSON Configuration: https://github.com/chriswill/serilog-sinks-azureblobstorage#json-configuration
{
"Serilog": {
"Using": [ "Serilog.Sinks.AzureBlobStorage" ],
"WriteTo": [
{
"Name": "AzureBlobStorage",
"Args": {
"connectionString": "",
"storageContainerName": "",
"storageFileName": "",
"outputTemplate": "",
"blobSizeLimitBytes": ""
}
}
]
}
}
Example JSON Configuration: appsettings.Production.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.AzureBlobStorage" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "AzureBlobStorage",
"Args": {
"connectionString": "DefaultEndpointsProtocol=https;AccountName=blobstorageloggingpoc;AccountKey=ceF/WJzzXoZYOWB/z22L5IZS2dtLHLFMwcJ/JhgoyYfHd8xn2eWn7p2nsNPus2VlvCPMYVf8BKDh++k1tIOlUQ==;EndpointSuffix=core.windows.net",
"storageContainerName": "foocontainer",
"storageFileName": "{dd-MM-yyyy}-test.log",
"outputTemplate": "[{Timestamp:yyyy/MM/dd HH:mm:ss} {Level:u10}] {Message:lj}{NewLine}{NewLine}{Exception}{NewLine}",
"blobSizeLimitBytes": "4000000"
}
}
]
}
}
Get Connection String
Logging to blob storage with batch posting
Serilog.Sinks.AzureBlobStorage
has dependency toSerilog.Sinks.PeriodicBatching
- No need to install extra package for batch posting (AzureBlobStorage sink uses PeriodicBatching for batch posting)
{
"Serilog": {
"Using": [ "Serilog.Sinks.AzureBlobStorage" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "AzureBlobStorage",
"Args": {
"connectionString": "",
"storageContainerName": "",
"storageFileName": "",
"outputTemplate": "",
"blobSizeLimitBytes": "4000000",
"writeInBatches": "true",
"period ": "15",
"batchPostingLimit": "50"
}
}
]
}
}
Working example: appsettings.Production.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.AzureBlobStorage" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "AzureBlobStorage",
"Args": {
"connectionString": "DefaultEndpointsProtocol=https;AccountName=blobstorageloggingpoc;AccountKey=ceF/WJzzXoZYOWB/z22L5IZS2dtLHLFMwcJ/JhgoyYfHd8xn2eWn7p2nsNPus2VlvCPMYVf8BKDh++k1tIOlUQ==;EndpointSuffix=core.windows.net",
"storageContainerName": "foocontainer",
"storageFileName": "{dd-MM-yyyy}-test.log",
"outputTemplate": "[{Timestamp:yyyy/MM/dd HH:mm:ss} {Level:u10}] {Message:lj}{NewLine}{NewLine}{Exception}{NewLine}",
"blobSizeLimitBytes": "4000000",
"writeInBatches": "true",
"period ": "15",
"batchPostingLimit": "50"
}
}
]
}
}
Logging to blob storage with connectionString in Azure KeyVault
- connectionString contains AccountKey and therefore should be in KeyVault
appsettings.json
- Will contain KeyVaultBaseUrl and BlobStorageContainerName (values will be empty)
- Both KeyVaultBaseUrl and BlobStorageContainerName will be overriden by DevOps pipeline (pipeline will provide actual values)
appsettings.Development.json
will contain development-time specific settings only (will be ignored in Production by convention)
appsettings.json
{
"KeyVaultBaseUrl": "",
"BlobStorageContainerName": ""
}
Constants.cs
public static class Azure
{
public const string KeyVaultIdentifier = "KeyVaultBaseUrl";
public const string BlobStorageConnectionStringKey = "BlobStorageAccountConnectionString";
public const string BlobStorageContainerNameIndentifier = "BlobStorageContainerName";
public const string BlobStorageFileNamePattern = @"{yyyyMMdd}_hoverapp.ndjson";
public const string BlobStorageLogFormat = @"[{Timestamp:yyyy/MM/dd HH:mm:ss} {Level:u10}] {LineNumber} {Method} {SourceContext:l} {Message:lj}{NewLine}{Exception}{NewLine}";
public const int BatchPeriodInMilliSeconds = 15;
public const int BatchEventLimit = 30;
}
Program.cs
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
using static Serilog.Events.LogEventLevel;
namespace Hovermind.HoverApp
{
public class Program
{
private const string MicrosoftLog = "Microsoft";
private const string SystemtLog = "System";
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, configBuilder) =>
{
var Configuration = configBuilder.Build();
var isProduction = context.HostingEnvironment.IsProduction();
if (isProduction)
{
var keyVaultUri = Configuration[Azure.KeyVaultIdentifier];
configBuilder.AddAzureKeyVault(keyVaultUri);
Configuration = configBuilder.Build();
var blogStorageConnectionString = Configuration[Azure.BlobStorageConnectionStringKey];
var blogStorageContainerName = Configuration[Azure.BlobStorageContainerNameIndentifier];
if (!string.IsNullOrEmpty(blogStorageConnectionString))
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override(MicrosoftLog, Warning)
.MinimumLevel.Override(SystemtLog, Warning)
.WriteTo.AzureBlobStorage(connectionString: blogStorageConnectionString,
restrictedToMinimumLevel: Information,
storageContainerName: blogStorageContainerName,
storageFileName: Azure.BlobStorageFileNamePattern,
outputTemplate: Azure.BlobStorageLogFormat,
writeInBatches: true,
period: TimeSpan.FromSeconds(Azure.BatchPeriodInMilliSeconds),
batchPostingLimit: Azure.BatchEventLimit)
.CreateLogger();
Log.Information("AzureBlobStorage logger created");
}
}
else
{
//
// DEV: create logger from appsettings.json
//
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(Configuration).CreateLogger();
Log.Information("Development logger created");
}
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
.UseSerilog();
}
}