I am writing a web service that is using Serilog. I was having problems getting files to write out (but console logging worked). I noticed that the setup changed when .net core 2.0 came out based on this and this pages' explanation.
However, now, I can't see any logging (perhaps in the past the default M$ loggers were actually what I was seeing).
Here's how program.cs
is set up:
public class Program
{
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddUserSecrets<Startup>()
.Build();
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration.GetSection("Serilog"))
.CreateLogger();
try
{
Log.Information("Starting webhost...");
BuildWebHost(args).Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseConfiguration(Configuration)
.UseSerilog()
.Build();
}
My appsettings.json has this section in the root:
"Serilog": {
"Using" : ["Serilog.Sinks.Console", "Serilog.Sinks.File"],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
},
"Enrich" : ["FromLogContext"],
"WriteTo": [
{"Name": "Console" },
{"Name": "Debug" },
{"Name": "File", "Args": {"path": "%LogDir%\\sampleapp\\log-{Date}.txt", "rollingInterval": "Day", "shared": true } }
]
},
"Properties": {
"Application": "sampleapp"
}
},
Note that %LogDir%
is an environment variable on my machine and resolves fine in other applications. The path is already created and the Logs folder has full RW permissions for the credentials this app uses.
I call logging like so...
private readonly ILogger<PartnerController> _logger;
private readonly IPartnerDao _partnerDao;
public PartnerController(ILogger<PartnerController> logger, IPartnerDao partnerDao)
{
_logger = logger;
_partnerDao = partnerDao;
}
[HttpGet]
[Route("{titleCode}")]
public async Task<IActionResult> Get(string titleCode)
{
_logger.LogInformation("Test logging");
}
Yet, somehow nothing shows in the ASP.NET Core Web Server
window and not file is created on my machine when running the service.
Am I missing something obvious?
Turns out I had copied some of the JSON from documentation incorrectly. It's hard to tell but in the original question I actually had Enrich
, WriteTo
, and Properties
sections embedded within the MinimumLevel
section.
Obviously this prevented Serilog from correctly knowing which Sinks to write to.
Here's my corrected settings JSON:
"Serilog": {
"Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": ["FromLogContext"],
"WriteTo": [
{ "Name": "Console" },
{ "Name": "Debug" },
{
"Name": "File",
"Args": {
"path": "%LogDir%\\sampleapp\\log-.txt",
"rollingInterval": "Day",
"shared": true
}
}
],
"Properties": {
"Application": "sampleapp"
}
},
Note that I also removed the {Date}
from the filename. Apparently it'll tack that on if you set the rolling interval to day....
appsettings.Development.json
overwrite settings in appsettings.json
I repeat again, configuration in appsettings.Development.json
will take precedence over appsettings.json
. I know that sounds obvious but I bet someone could overlook this in the future just like I did.
I spent nearly an hour scratching my head why no log was being written to the file, only to notice later I had only Console
sink in my appsettings.Development.json
(effectively removing my File
sink in appsettings.json
duh!).
Here is a sample of a proper configuration (modify according to your needs):
ASP.NET Core 3.1
Program.cs
using Microsoft.AspNetCore.Hosting;
using Serilog;
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog.Core;
namespace My.App
{
public class Program
{
private static bool IsDevelopment =>
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.AddEnvironmentVariables()
.Build();
public static Logger Logger { get; } = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.CreateLogger();
public static int Main(string[] args)
{
Log.Logger = Logger;
try
{
Log.Information("Starting...");
var host = CreateHostBuilder(args).Build();
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseSerilog()
.UseServiceProviderFactory(
new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
});
return host;
}
}
}
ASP.NET Core 2.2
Program.cs
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)
.Enrich.FromLogContext()
.CreateLogger();
try
{
Log.Information("Starting...");
CreateWebHostBuilder(args).Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
appsettings.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}
appsettings.Development.json
{
"Serilog": {
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "Console"
},
{
"Name": "Debug"
},
{
"Name": "DiagnosticTrace"
},
{
"Name": "File",
"Args": {
"path": "/home/log/api-log-.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"buffered": true
}
}
]
}
}
]
}
}
appsettings.Production.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "ApplicationInsights",
"Args": {
"restrictedToMinimumLevel": "Information",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
},
{
"Name": "Email",
"Args": {
"EmailConnectionInfo": {
"EmailSubject": "PRODUCTION error logs",
"FromEmail": "xxxxxxx",
"ToEmail": "xxxxxxx",
"MailServer": "xxxx",
"NetworkCredentials": {
"username": "xxxxxx",
"password": "xxxxxx",
"domain": "xxxxx"
},
"Port": 25
},
"restrictedToMinimumLevel": "Error"
}
},
{
"Name": "File",
"Args": {
"path": "/home/log/api-log-.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 15,
"buffered": true
}
}
]
}
}
]
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With