.net Core and Serilog Email sink - json config .net Core and Serilog Email sink - json config json json

.net Core and Serilog Email sink - json config


The settings system (ReadFrom.Configuration()) really only does try to call methods and extension methods that it can discover and pass arguments provided from the configuration file.

Unfortunately, it only supports basic types for the time being (convertible to/from string and a few more specific cases) and therefore, parameters of type EmailConnectionInfo cannot be provided.

As a workaround, though, if you only need to pass in a few parameters, you can create your own extension method that accepts the parameters that you need and call it from the configuration system.

In your case, you would need to do the following :

First, define an extension method EmailCustom(...) that can be plugged on WriteTo (which is of type Serilog.Configuration.LoggerSinkConfiguration) and returns a LoggerConfiguration.

This would look something like (not tested, no usings etc :P) :

namespace Serilog{    public static class MyCustomExtensions    {        public static LoggerConfiguration EmailCustom(this LoggerSinkConfiguration sinkConfiguration, string param1, int param2, LogEventLevel restrictedToMinimumLevel){            // the actual call to configure the Email sink, passing in complex parameters            return sinkConfiguration.Email(... ... , restrictedToMinimumLevel , EmailConnectionInfo(){            Foo = "bar",            Baz = param1,            Qux = param2,            }            );        }    }}

From that point on, you should be able to write C# code like :

new LoggerConfiguration()    .WriteTo.EmailCustom(param1: "my param1", param2: 42)   // ...    .CreateLogger();

Once you have that working, you can actually define that method call in json thanks to Serilog.Settings.Configurationin that case, that would look like

{"Serilog": {    "Using" : ["TheNameOfTheAssemblyThatContainsEmailCustom"],    "MinimumLevel": "Debug",    "WriteTo": [      {        "Name": "EmailCustom",        "Args": {          "param1": "my param1",          "param2": 42,          "restrictedToMinimumLevel": "Verbose"        }      }]    }}

This strategy can be applied for other sinks and other configuration parts of Serilog as well.


You can find a bit more about the configuration system here :


For others Like me that have trouble piecing things between the lines here is a complete answer using the framework presented by tsimbalar for a solution that sends email out using SendGrid.

I added the following class to the root of my project ("MyApp"). This gets called automatically from the ReadFrom.Configuration(configuration).CreateLogger(); due to the WriteTo EmailCustom in the appsettings.

using System;using System.Net;using Serilog;using Serilog.Configuration;using Serilog.Events;using Serilog.Sinks.Email;namespace TrackumApi{    public static class SerilogEmailExtension    {        public static LoggerConfiguration EmailCustom(this LoggerSinkConfiguration sinkConfiguration,            string fromEmail,            string toEmail,            string enableSsl,            string mailSubject,            string isBodyHtml,            string mailServer,            string networkCredentialuserName,            string networkCredentialpassword,            string smtpPort,            string outputTemplate,            string batchPostingLimit,            string periodMinutes,            string restrictedToMinimumLevel)        {            return sinkConfiguration.Email(new EmailConnectionInfo            {                FromEmail = fromEmail,                ToEmail = toEmail,                EnableSsl = GetBoolean(enableSsl),                EmailSubject = mailSubject,                IsBodyHtml = GetBoolean(isBodyHtml),                MailServer = mailServer,                NetworkCredentials = new NetworkCredential(networkCredentialuserName, networkCredentialpassword),                Port = GetInt(smtpPort)            }, outputTemplate, GetLevel(restrictedToMinimumLevel),                 GetInt(batchPostingLimit), TimeSpan.FromMinutes(GetInt(periodMinutes))            );        }      //The system hated converting the string inputs inline so I added the conversion methods:        private static int GetInt(string instring)        {            return int.TryParse(instring, out var result) ? result : 0;        }        private static bool GetBoolean(string instring)        {            return bool.TryParse(instring, out var result) && result;        }        private static LogEventLevel GetLevel(string restrictedtominimumlevel)        {            return Enum.TryParse(restrictedtominimumlevel, true,                out LogEventLevel level) ? level : LogEventLevel.Warning;        }    }}

In my origianl post I modified my Program.cs but it turns out that is not needed. However the addition of the Serilog.Debugging.SelfLog before any other code is still priceless:

        Serilog.Debugging.SelfLog.Enable(Console.Out);        var configuration = new ConfigurationBuilder()            .AddJsonFile("appsettings.json", true, true)            .Build();        Log.Logger = new LoggerConfiguration()            .ReadFrom.Configuration(configuration)            .CreateLogger();

Finally I modified the appsettings.json as follows (forgive the extra, but I think that might also help somebody):

{  "Logging": {    "LogLevel": {      "Default": "Information",      "Microsoft": "Warning",      "Microsoft.Hosting.Lifetime": "Information"    }  },  "AllowedHosts": "*",  "Serilog": {    "Using": [ "Serilog", "Serilog.Sinks.Console", "Serilog.Sinks.File", "MyApp" ],    "MinimumLevel": {      "Default": "Verbose",      "Override": {        "Microsoft": "Warning",        "System": "Warning",        "Microsoft.AspNetCore.Authentication": "Information"      }    },    "WriteTo": [      {        "Name": "Console",        "Args": {          "outputTemplate": "[{Timestamp:HH:mm:ss.fff} [{Level}] {SourceContext} {Message}{NewLine}{Exception}",          "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"        }      },      {        "Name": "File",        "Args": {          "path": "C:\\Temp\\Logs\\MyApp.log",          "fileSizeLimitBytes": 1000000,          "rollOnFileSizeLimit": "true",          "shared": "true",          "flushToDiskInterval": 3,          "outputTemplate": "[{Timestamp:MM/dd/yy HH:mm:ss} [{Level}] {SourceContext} {Message}{NewLine}{Exception}",          "restrictedToMinimumLevel": "Verbose"        }      },      {        "Name": "EmailCustom",        "Args": {          "fromEmail": "no-reply@mydomain.com",          "toEmail": "me@mydomain.com",          "enableSsl": false,          "mailSubject": "MyApp Message",          "isBodyHtml": true,          "mailServer": "smtp.sendgrid.net",          "networkCredentialuserName": "mysendgridusername",          "networkCredentialpassword": "mysendgridpassword",          "smtpPort": 587,          "outputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}",          "batchPostingLimit": 10,          "periodMinutes": 5,          "restrictedToMinimumLevel": "Verbose"        }      }    ],    "Enrich": [ "FromLogContext" ],    "Properties": {      "Application": "MyApp"    }  }}

HTH!