Fix Serilog email sink: configure in code, not JSON config
Serilog.Settings.Configuration cannot deserialize NetworkCredential or MailKit's SecureSocketOptions from JSON, causing an InvalidOperationException in the binder and preventing containers from starting. Fix: remove Email from the WriteTo JSON array entirely and wire it in code inside ConfigureJsonSerilog using a dedicated SerilogEmail:* config section. The sink is skipped when From/To/Host are absent, so local dev is unaffected. Also renames the docker-compose env vars from the verbose Serilog__WriteTo__2__Args__* prefix to the clean SerilogEmail__* prefix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using Azure.Identity;
|
||||
using MailKit.Security;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -9,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
@@ -41,6 +44,8 @@ public static class StartupExtensions
|
||||
.Enrich.WithProperty("Service", serviceName)
|
||||
.Enrich.WithProperty("AppVersion", appVersion)
|
||||
.WriteTo.Console(new Serilog.Formatting.Json.JsonFormatter());
|
||||
|
||||
AddEmailSinkIfConfigured(configuration, context.Configuration, serviceName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,9 +62,40 @@ public static class StartupExtensions
|
||||
.Enrich.WithProperty("Service", serviceName)
|
||||
.Enrich.WithProperty("AppVersion", appVersion)
|
||||
.WriteTo.Console(new Serilog.Formatting.Json.JsonFormatter());
|
||||
|
||||
AddEmailSinkIfConfigured(configuration, builder.Configuration, serviceName);
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddEmailSinkIfConfigured(LoggerConfiguration loggerConfig, IConfiguration appConfig, string serviceName)
|
||||
{
|
||||
var from = appConfig["SerilogEmail:From"];
|
||||
var to = appConfig["SerilogEmail:To"];
|
||||
var host = appConfig["SerilogEmail:Host"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(from) || string.IsNullOrWhiteSpace(to) || string.IsNullOrWhiteSpace(host))
|
||||
return;
|
||||
|
||||
var port = appConfig.GetValue("SerilogEmail:Port", 587);
|
||||
var userName = appConfig["SerilogEmail:UserName"];
|
||||
var password = appConfig["SerilogEmail:Password"];
|
||||
|
||||
NetworkCredential? credentials = null;
|
||||
if (!string.IsNullOrWhiteSpace(userName))
|
||||
credentials = new NetworkCredential(userName, password);
|
||||
|
||||
loggerConfig.WriteTo.Email(
|
||||
from: from,
|
||||
to: to,
|
||||
host: host,
|
||||
port: port,
|
||||
connectionSecurity: SecureSocketOptions.StartTls,
|
||||
credentials: credentials,
|
||||
subject: $"[myAi {serviceName}] Error Alert",
|
||||
body: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}",
|
||||
restrictedToMinimumLevel: LogEventLevel.Error);
|
||||
}
|
||||
|
||||
public static void AddAzureKeyVaultIfConfigured(this WebApplicationBuilder builder)
|
||||
{
|
||||
var keyVaultUri = builder.Configuration["KeyVault:VaultUri"];
|
||||
|
||||
Reference in New Issue
Block a user