Filter Serilog logs to different sinks depending on context source?
I completed this work today, and thought that I'd provide a proper answer since it took me quite a few posts, issues and other pages to work through to get this sorted out.
It's useful to have all the logs, but I also wanted to log only my API code separately, and omit the Microsoft.
namespace logs. The JSON config to do that looks like this:
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "/var/logs/system.log",
... //other unrelated file config
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "/var/logs/api.log",
... //other unrelated file config
}
}
],
"Filter": [
{
"Name": "ByExcluding",
"Args": {
"expression": "StartsWith(SourceContext, 'Microsoft.')"
}
}
]
}
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
... //Destructure and other config
}
The top-level WriteTo
is the first simple, global, sink. All log events write to this. If you add a Filter
section on the same level as this, it will affect all configured WriteTo
elements.
Then I configure another WriteTo
as a Logger
(not File
), but the Args
for this looks different and has a configureLogger
element which serves the same purpose as Serilog
on the top level, that is to say, it is the top level of the sub-logger. This means that you can easily split out the config for this into a separate file and add it additionally in the config builder (see bottom).
From here, this sub-logger works the same way: You can configure multiple WriteTo
s, and the Filter
element on this level will affect only this sub-logger.
Simply add more "Name": "Logger"
elements to the top level WriteTo
section and setup filters for each one separately.
Note
It is also important to note that, even though you are doing all this in config and not referencing a single bit of the Serilog.Filters.Expressions
package in your code, you still have to add the NuGet reference to that package. It doesn't work without the package reference.
About splitting the config:
If I have to add more loggers, I would definitely split out the different loggers into separate files for clarity, e.g.
appsettings.json:
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Error",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "/var/logs/system.log",
...
}
},
{
"Name": "Logger",
"Args": {
"configureLogger": {} // leave this empty
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
...
apilogger.json:
{
"Serilog:WriteTo:1:Args:configureLogger": { //notice this key
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "/var/logs/api_separateFile.log",
...
}
}
],
"Filter": [
{
"Name": "ByExcluding",
"Args": {
"expression": "StartsWith(SourceContext, 'Microsoft.')"
}
}
]
}
}
And then adjust my IWebHost
builder to include the additional config:
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("apilogger.json", optional: false, reloadOnChange: false);
})
.UseStartup<Startup>();
This way it is easier to understand, read and maintain.
Writing only entityframework log to a file is done below but we need to install Serilog.Filters.Expressions nuget package
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"WriteTo": [
{
"Name": "Logger",
"Args": {
"configureLogger": {
"Filter": [
{
"Name": "ByIncludingOnly",
"Args": {
"expression": "StartsWith(SourceContext, 'Microsoft.EntityFrameworkCore')"
}
}
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "C:\\LOGS\\TestService_EF_.json",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
"rollingInterval": "Day",
"retainedFileCountLimit": 7
}
}
]
}
}
}
]
}