What does actually Using
do in Serilog JSON configuration (e.g. in AppSettings.json file in .Net Core environment)?
Let us take this configuration for example:
"Serilog": {
"Using": [ "Serilog.Sinks.Console" ], <=======***HERE***=========
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "RollingFile",
"Args": {
"pathFormat": "logs\\log-{Date}.txt",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "My Application"
}
}
In the example above we used File
sink WITHOUT adding it to Using
attribute. However, everything seems to be working ok.
So I cannot understand exactly why we basically need this Using
. Could someone explain it to me please?
This is covered in the documentation for Serilog.Settings.Configuration
:
(This package implements a convention using
DependencyContext
to find any package withSerilog
anywhere in the name and pulls configuration methods from it, so theUsing
example above is redundant.)
This means that it's used to define which packages are used for locating Serilog sinks, but it's redundant when using the Serilog.Settings.Configuration
package.
More Information
I've had a look at the Serilog source code in order to be able to provide more information on exactly what Using
does and why it might be needed in the first place. I hope the following explanation is helpful.
Consider the following code-based setup:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
In this example, Console
is an extension method for LoggerSinkConfiguration
(and so it takes, as its first parameter, an instance of LoggerSinkConfiguration
). When using this code-based approach, the code will only compile if this extension method can be found within a referenced assembly.
Next, consider the following approach, which uses the IConfiguration
-based approach:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(someConfiguration)
.CreateLogger();
{
"Serilog": {
"Using": ["Serilog.Sinks.Console"], // Redundant.
"WriteTo": ["Console"]
}
}
In this example, the compilation process has no knowledge of what the JSON string value "Console"
refers to and so there's a need for a process that can go from the string "Console"
to the Console()
extension method mentioned above. In order to do that, Serilog needs to first find the extension method at runtime (which, in this example, lives in the Serilog.Sinks.Console
assembly).
This finding process is done using reflection, which does a bit of assembly scanning to find public static
methods that take as their first parameter a LoggerSinkConfiguration
. The Using
directive you've asked about in your question is a mechanism for helping determine exactly which assemblies should be scanned when looking for these extension methods.
As the documentation states, the IConfiguration
-based approach uses DependencyContext
in order to automatically scan assemblies that have Serilog
in their name. Because Serilog.Sinks.Console
does have Serilog
in its name, there's no need to add this to the Using
directive. You also have the option to provide your own DependencyContext
instance when using this approach and so you might then need to be explicit about which assemblies to scan when looking for sinks.
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