Reputation: 1899
Im trying to configure serilog to write to multiple files, with no luck whatsoever. With this configuration it just writes to the second file?
{
"AllowedHosts": "*",
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "c:\\temp\\audit-.log",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Information"
}
},
{
"Name": "File",
"Args": {
"path": "c:\\temp\\error-.log",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Error"
}
}
]
}
}
Or is there any way to load many loggers to the software with different configurations from appsettings.json. Something like this?
var errorLogConfiguration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
_log = new LoggerConfiguration()
.ReadFrom
.Configuration(errorLogConfiguration)
.CreateLogger();
Upvotes: 11
Views: 18020
Reputation: 408
I have got a cool solution from a blog i have found. It surely did perform like a charm. if any problem occurs pls let me know. I am also sharing the blog with you guys https://www.techrepository.in/blog/posts/writing-logs-to-different-files-serilog-asp-net-core. Also for filtering the logs you need to use Serilog.Eexpressions nuget package. Hope you get what you are looking for from here
"Serilog": {
"Using": [ "Serilog.Sinks.File", "Serilog.Expressions" ],
"MinimumLevel": {
"Default": "Debug"
},
"WriteTo": [
{
"Name": "Logger",
"Args": {
"configureLogger": {
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "@l = 'Error' or @l = 'Fatal' or @l = 'Warning'"
}
}
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "../logs/error_.log",
"outputTemplate": "{Timestamp:o} [{Level:u3}] ({SourceContext}) {Message}{NewLine}{Exception}",
"rollingInterval": "Day"
}
}
]
}
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "@l = 'Information' or @l = 'Debug'"
}
}
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "../logs/debug_.log",
"outputTemplate": "{Timestamp:o} [{Level:u3}] ({SourceContext}) {Message}{NewLine}{Exception}",
"rollingInterval": "Day"
}
}
]
}
}
}
]},
Step 1: Install Required Packages
Ensure you have the necessary Serilog packages installed. You will need:
Serilog
Serilog.Sinks.File
Serilog.Expressions
Explanation:
Serilog
Section: Configures Serilog's sinks and settings.Using
: Lists the Serilog packages in use.MinimumLevel
: Sets the default log level.WriteTo
: Defines where logs should be written and the conditions for writing them.
Error
, Fatal
, or Warning
messages to error_.log
.Information
or Debug
messages to debug_.log
.Filter
: Uses Serilog's filtering capabilities to include only specific log levels based on the expression.WriteTo
File Sink: Specifies the file path and output format for the logs.Upvotes: 3
Reputation: 441
My findings after a while of errors, retry and nearly giving up based on lack of documentation about Serilog. They have a tread on GitHub: https://github.com/serilog/serilog-filters-expressions/issues/27. Nearly every thread goes to the same conclusion that you have to create a SUBLOGGER. This is my implementation. For this implementation you need the following plugins:
Serilog Async
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo:Information": { //this name here can be changed
"Name": "Logger", //this name here is essential
"Args": {
"configureLogger": {
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "@Level = 'Information'"
}
}
],
"WriteTo": [
{
"Name": "Async", //i use async plugin from serilog
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "Logs/Log_.txt",
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"rollingInterval": "Day",
"retainedFileCountLimit": 7
}
}
]
}
}
]
}
}
},
Upvotes: 5
Reputation: 81
After struggling for days due to the simple documentation for Serilog and any of the sinks, this is what finally worked for me without creating any separate logger:
"Serilog": {
"Using": [ "Serilog.Sinks.Async" ],
"MinimumLevel": "Verbose",
"Enrich": [ "FromLogContext", "WithDemystifiedStackTraces" ],
"WriteTo:Information": {
"Name": "Async",
"Args": {
"Configure": [
{
"Name": "RollingFile",
"Args": {
"RestrictedToMinimumLevel": "Information",
"Formatter": "FOOINC.API.Configuration.Logging.CustomRenderedCompactJsonFormatter, FOOINC.API.Configuration",
"PathFormat": "_logs\\info\\info-log.json"
}
}
]
}
},
"WriteTo:Error": {
"Name": "Async",
"Args": {
"Configure": [
{
"Name": "RollingFile",
"Args": {
"RestrictedToMinimumLevel": "Error",
"Formatter": "FOOINC.API.Configuration.Logging.CustomRenderedCompactJsonFormatter, FOOINC.API.Configuration",
"PathFormat": "_logs\\errors\\error-log.json"
}
}
]
}
}
}
Hope this helps anyone out there!!
Upvotes: 7
Reputation: 984
There is little documentation on logger settings through the configuration file. Can anyone help - just an example of using the settings of the logger. In the example - all logs are written in one sample.txt. Logs on a call of a certain API / api/health - in a separate file and are not included in sample.txt. And an example ad - IMyLogger-writes to a separate SampleMy.txt. You can add many sections, and divide the logs by different criteria. It is better to set local logging levels as minimal, they will be overwritten by the global level. The global filter will exclude logs from all sub-loggers (I don't use it). PS sorry for the bad English)
"Serilog": {
"MinimumLevel": "Information", //<- global error level. Ovveride all local error level
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"MinimumLevel": "Debug", // <- local error level.
//Only records with Information logging level will be written to the log file
//but if ovveride global level to Debug, and dont override local error level -> it will still be global
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "log\\SampleHealthCheck-.txt", //write health-check log in different file
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [CorrId:{CorrelationId}] [Op:{OperationId}] [U:{UserName}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "RequestPath like '%/api/health'"
}
}
]
}
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "log\\SampleMy-.txt", //Write some log in different file. Control through code
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [CorrId:{CorrelationId}] [Op:{OperationId}] [U:{UserName}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "SourceContext = 'MyProject.IMyLogger'"
}
}
]
}
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "log\\Sample-.txt", //all logs, without health-check
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [CorrId:{CorrelationId}] [Op:{OperationId}] [U:{UserName}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Filter": [
{
"Name": "ByExcluding",
"Args": {
"expression": "RequestPath like '%/api/health'"
}
}
]
}
}
}
],
"Enrich": [
"WithProcessName"
],
"Properties": {
"Application": "Sample",
"Environment": "Test"
}
}
public class MyCommandHandler : IRequestHandler<MyCommand, Unit>
{
private readonly ILogger _myLogger;
private static int _count;
public MyCommandHandler()
{
_myLogger = Log.ForContext<IMyLogger>();
}
public async Task<Unit> Handle(MyCommand request, CancellationToken cancellationToken)
{
_count++;
Log.Debug("MyCommandHandler Count call = {count}",_count ); //write sample.txt
Log.Information("MyCommandHandler Count call = {count}",_count ); //write in sample.txt
Log.Error("MyCommandHandler Count call = {count}",_count); //write in sample.txt
_myLogger.Information("Log from IMyLogger", _count); //write in sample.txt and in sampleMy.txt
return Unit.Value;
}
}
Upvotes: 3
Reputation: 1899
I found the solution. Created separate sections to appsettings.json, ErrorLog and AuditLog.
"ErrorLog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "c:\\temp\\error-.log",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Error"
}
}
]
},
"AuditLog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "c:\\temp\\audit-.log",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Information"
}
}
]
}
Now I can create 2 separate loggers:
var errorLogConfiguration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
var errorSection = errorLogConfiguration.GetSection("ErrorLog");
var auditSection = errorLogConfiguration.GetSection("AuditLog");
_log = new LoggerConfiguration()
.ReadFrom
.ConfigurationSection(errorSection)
.CreateLogger();
_auditLog = new LoggerConfiguration()
.ReadFrom
.ConfigurationSection(auditSection)
.CreateLogger();
Which suits my need better.
Upvotes: 8
Reputation: 1126
I made some tests on this issue. The good news is your configuration works. Your error files probably isn't created, because no error is logged.
Serilog will create the log file, when the first message is logged into that file. You can confirm this if you run this simple program and (un)comment the logging of an error.
class Program
{
static void Main(string[] args)
{
var errorLogConfiguration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("settings.json", optional: false, reloadOnChange: true)
.Build();
var log = new LoggerConfiguration()
.ReadFrom
.Configuration(errorLogConfiguration)
.CreateLogger();
log.Warning("Warning");
log.Error("Error");
}
}
Upvotes: -1