I'm using .net Core 2.0 and Serilog Email sink. I have problem to configure email sink with appsettings.json. The same configuration from program.cs is working while one from appsetting.json isn't.


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": "[email protected]",
          "toEmail": "[email protected]",
          "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!
using System.Net;
using MailKit.Security;
using Serilog;
using Serilog.Configuration;
using Serilog.Events;
using Serilog.Formatting.Display;
using Serilog.Sinks.Email;
using Serilog.Sinks.PeriodicBatching;
namespace SerilogMail3Test
{
    /*
     * Important! The assembly name must be in the Serilog Usings in the appsettings.json
     * such as
     *   "Serilog": {
     * "Using": [ "Serilog", "Serilog.Sinks.Console", "Serilog.Sinks.File", "SerilogMail3Test" ],
     *
     */
    public static class SerilogEmailExtension
    {
        public static LoggerConfiguration EmailCustom(this LoggerSinkConfiguration sinkConfiguration,
            string fromEmail,
            string toEmail,
            string mailSubject,
            string isBodyHtml,
            string mailServer,
            string networkCredentialUserName,
            string networkCredentialPassword,
            string smtpPort,
            string outputTemplate,
            string batchPostingLimit,
            string periodMinutes,
            string restrictedToMinimumLevel)
        {
            return sinkConfiguration.Email(new EmailSinkOptions
            {
                From = fromEmail,
                To = [toEmail],
                Host = mailServer,
                Port = GetInt(smtpPort),
                Credentials = new NetworkCredential(networkCredentialUserName, networkCredentialPassword),
                Subject = new MessageTemplateTextFormatter(mailSubject),
                Body = new MessageTemplateTextFormatter(outputTemplate),
                IsBodyHtml = GetBoolean(isBodyHtml),
                ConnectionSecurity = SecureSocketOptions.Auto,
                ServerCertificateValidationCallback = null
            },
                new PeriodicBatchingSinkOptions
                {
                    EagerlyEmitFirstEvent = false,
                    BatchSizeLimit = GetInt(batchPostingLimit),
                    Period = TimeSpan.FromMinutes(GetInt(periodMinutes)),
                    QueueLimit = null
                },
                GetLevel(restrictedToMinimumLevel)
            );
        }
        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;
        }
    }
}
using System.Net;
using MailKit.Security;
using Serilog;
using Serilog.Configuration;
using Serilog.Events;
using Serilog.Formatting.Display;
using Serilog.Sinks.Email;
namespace SerilogMail3Test
{
    /*
     * Important! The assembly name must be in the Serilog Usings in the appsettings.json
     * such as
     *   "Serilog": {
     * "Using": [ "Serilog", "Serilog.Sinks.Console", "Serilog.Sinks.File", "SerilogMail3Test" ],
     *
     */
    public static class SerilogEmailExtension
    {
        public static LoggerConfiguration EmailCustom(this LoggerSinkConfiguration sinkConfiguration,
            string fromEmail,
            string toEmail,
            string mailSubject,
            string isBodyHtml,
            string mailServer,
            string networkCredentialUserName,
            string networkCredentialPassword,
            string smtpPort,
            string outputTemplate,
            string batchPostingLimit,
            string periodMinutes,
            string restrictedToMinimumLevel)
        {
            return sinkConfiguration.Email(new EmailSinkOptions
            {
                From = fromEmail,
                To = [toEmail],
                Host = mailServer,
                Port = GetInt(smtpPort),
                Credentials = new NetworkCredential(networkCredentialUserName, networkCredentialPassword),
                Subject = new MessageTemplateTextFormatter(mailSubject),
                Body = new MessageTemplateTextFormatter(outputTemplate),
                IsBodyHtml = GetBoolean(isBodyHtml),
                ConnectionSecurity = SecureSocketOptions.Auto,
                ServerCertificateValidationCallback = null
            },
            new BatchingOptions
            {
                EagerlyEmitFirstEvent = false,
                BatchSizeLimit = GetInt(batchPostingLimit),
                BufferingTimeLimit = TimeSpan.FromMinutes(GetInt(periodMinutes)),
                QueueLimit = null
            },
                GetLevel(restrictedToMinimumLevel)
            );
        }
        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;
        }
    }
}
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