.netpowershellserilogserilog-exceptions

Hello World with SeriLog and PowerShell


I'm trying to understand how to use SeriLog in PS.

This is an example of code that works:

$SeriLog = Add-Type -Path "C:\Program Files\PackageManagement\Serilog.2.10.0\lib\net46\Serilog.dll" -PassThru 
$SeriLog_Sink_Console = Add-Type -Path "C:\Program Files\PackageManagement\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll" -PassThru

$SeriLog_Config = New-Object Serilog.LoggerConfiguration
$SeriLog_Config = [Serilog.Configuration.LoggerSinkConfiguration]$SeriLog_Config_Sink = $SeriLog_Config.WriteTo
[Serilog.Log]::Logger = [Serilog.ConsoleLoggerConfigurationExtensions]::Console($SeriLog_Config_Sink).CreateLogger()
[Serilog.Log]::Information("Logging started")

Based on examples listed on SeriLogs site for dotnet, what i don't understand is why are all my sinks missing.

Example:

$SeriLog = Add-Type -Path "C:\Program Files\PackageManagement\Serilog.2.10.0\lib\net46\Serilog.dll" -PassThru 
$SeriLog_Sink_Console = Add-Type -Path "C:\Program Files\PackageManagement\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll" -PassThru

$SeriLog_Config = New-Object Serilog.LoggerConfiguration
$SeriLog_Config.WriteTo.Console()

There error i get is:

InvalidOperation: Method invocation failed because [Serilog.Configuration.LoggerSinkConfiguration] does not contain a method named 'Console'.

This makes sense to a degree in that I can see it's missing, but I don't understand why? I've imported both DLL's. I guess for me, I'm very much a newbie when it comes to understanding how to work with DLL's in PS. I've had much better luck with and have a reliably working module with Log4Net, but I'm trying to migrate to something that's more modern and I'm pulling my hair out trying to understand how to integrate various sinks if the public examples for dotnet don't translate to PS.

JFYI, I'm using PS 7.1 incase that matters.


Solution

  • The sinks are not missing, and you're loading the assemblies correctly. The thing is that Serilog sinks follow a convention that leverage a feature of the C# language called "extension methods" that is not available in PowerShell.

    If you look in the source code of the Console sink, you'll see that the method to configure it is a static method that receives the Serilog configuration as the first parameter with the special keyword this:

    public static class ConsoleLoggerConfigurationExtensions
    {
        public static LoggerConfiguration Console(
            this LoggerSinkConfiguration sinkConfiguration, ...)
        {
            // ...
        }
    }
    

    The special keyword this before the type LoggerSinkConfiguration tells the C# compiler that this method can be called as if it were an instance method of LoggerSinkConfiguration.

    The property WriteTo of LoggerConfiguration is of type LoggerSinkConfiguration which is why you can write:

    var config = new LoggerConfiguration();
    config.WriteTo.Console();
    

    This is just syntactic sugar. The compiler will translate the call to something like this:

    var config = new LoggerConfiguration();
    ConsoleLoggerConfigurationExtensions.Console(config.WriteTo);
    

    Which is exactly what you need to do in PowerShell:

    $config = New-Object Serilog.LoggerConfiguration
    [Serilog.ConsoleLoggerConfigurationExtensions]::Console($config.WriteTo) | out-null
    

    Thus, in order to use Serilog sinks in PowerShell, you'll have to find out what is the name of the class that contains the configuration extension methods (vary for each sink), and call them as static methods.

    The pattern is the same as you have in your first block of code above:

    $serilog = Add-Type -Path "C:\path-to\Serilog.dll" -PassThru 
    $serilogSinksConsole = Add-Type -Path "C:\path-to\Serilog.Sinks.Console.dll" -PassThru
    # ... (add more sink assemblies)
    
    $config = New-Object Serilog.LoggerConfiguration
    [Serilog.ConsoleLoggerConfigurationExtensions]::Console($config.WriteTo) | out-null
    # ... (configure more sinks)
    
    # (create the logger with all the different sinks configured above)
    [Serilog.Log]::Logger = $config.CreateLogger()
    
    [Serilog.Log]::Information("Logging started") 
    

    The best way to find out the name of the class you need to use for configuring each sink, is to look at the source code of the sink you want to use. They usually follow a convention of <SinkName>LoggerConfigurationExtensions but that's not guaranteed.

    In the main Serilog organization you can find the source code of the official sinks, and for the other community-provided sinks you can visit the individual source code repository of each of them.