Serilog組態設定紀錄-混合型
Serilog用的越來越專業組態設定就越來越複雜。在這複雜的組態中簡化的方案就是混合 appsettings.json 的靜態組態與程式碼的動態(參數)組態。
開發環境
Visual Studio 2022
.NET6
Blazor Server App
上一版文章參考
程式碼紀錄
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
},
"SystemID": "MONEY",
"LogFolder": "C:\\Log",
...
}
using Serilog;
using Serilog.Sinks.MSSqlServer;
try
{
var builder = WebApplication.CreateBuilder(args);
IConfiguration Configuration = builder.Configuration;
#region ## Prefix system initialization
// 取得系統名稱
string systemName = Assembly.GetAssembly(typeof(Program)).GetName().Name;
#endregion
#region ## Serilog configuration. ------------------------------------------
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Add(StandardColumn.LogEvent);
columnOptions.AdditionalColumns = new Collection<SqlColumn>
{
new SqlColumn{ColumnName = "SysName", DataType = SqlDbType.NVarChar, DataLength = 128},
new SqlColumn{ColumnName = "ClassName", DataType = SqlDbType.NVarChar, DataLength = 128},
new SqlColumn{ColumnName = "MethodName", DataType = SqlDbType.NVarChar, DataLength = 128},
new SqlColumn{ColumnName = "AuthUser", DataType = SqlDbType.NVarChar, DataLength = 128},
new SqlColumn{ColumnName = "ClientIP", DataType = SqlDbType.NVarChar, DataLength = 128}
};
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration) // 先取靜態組態,接下來再混合動態(參數)組態。
.Enrich.WithProperty("SysName", Configuration["SystemID"])
.Enrich.FromLogContext()
.WriteTo.Async(cfg =>
{
//# 文字檔
cfg.File($"{Configuration["LogFolder"] ?? "Log"}/{systemName}Log.txt",
rollingInterval: RollingInterval.Day);
//# JSON 檔
cfg.File(new Serilog.Formatting.Json.JsonFormatter(), $"{Configuration["LogFolder"] ?? "Log"}/{systemName}Log.json",
rollingInterval: RollingInterval.Day);
//# SQL Server
cfg.MSSqlServer(
connectionString: YourSecureModule["LOGDB"],
sinkOptions: new MSSqlServerSinkOptions() { TableName = "Serilog" },
columnOptions: columnOptions);
//# 自訂 LoggerSink : ActionLog WebApi
cfg.CustomSerilogSink(Configuration);
}).CreateLogger();
#endregion
builder.Host.UseSerilog();
Log.Information("Web host start.");
#region ## Add services to the container. ----------------------------------
...
#endregion
var app = builder.Build();
#region ## Configure the HTTP request pipeline. ----------------------------
...
// custom middleware:用於強化Serilog取得環境參數。
app.UseMiddleware<CustomMiddleware>();
//## Endpoints
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
#endregion
app.Run();
Log.Information("Web host exit.");
}
catch (Exception ex)
{
Log.Fatal(ex, "Web host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
using Microsoft.AspNetCore.Http;
using Serilog.Context;
namespace YourProject.Services;
/// <summary>
/// 中介軟體(ASP.NET Core middleware)。用於強化Serilog取得環境參數。
/// </summary>
/// <see cref="ASP.NET Core 中介軟體(https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1)"/>
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
[System.Diagnostics.DebuggerHidden]
public async Task Invoke(HttpContext context)
{
try
{
using (LogContext.PushProperty("AuthUser", context.User.Identity?.Name))
using (LogContext.PushProperty("ClientIP", context.Connection.RemoteIpAddress))
using (LogContext.PushProperty("UserAgent", context.Request.Headers["User-Agent"].FirstOrDefault()))
using (LogContext.PushProperty("DomainUserName", $"{Environment.UserDomainName}\\{Environment.UserName}"))
using (LogContext.PushProperty("MachineName", Environment.MachineName))
{
// Do work that doesn't write to the Response.
await _next.Invoke(context);
// Do logging or other work that doesn't write to the Response.
}
}
catch (Exception ex)
{
throw new ApplicationException("CustomMiddleware.Invoke 出現例外!", ex);
}
}
}
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Serilog](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[TimeStamp] [datetime2](7) NOT NULL,
[Level] [nvarchar](128) NULL,
[Message] [nvarchar](max) NULL,
[AuthUser] [nvarchar](128) NULL,
[SysName] [nvarchar](128) NULL,
[ClassName] [nvarchar](128) NULL,
[MethodName] [nvarchar](128) NULL,
[ClientIP] [nvarchar](128) NULL,
[Exception] [nvarchar](max) NULL,
[LogEvent] [nvarchar](max) NULL,
CONSTRAINT [PK_Serilog] PRIMARY KEY CLUSTERED
(
[Id] DESC
))
GO
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
public static class ILoggerClassExtensions
{
public static void LogEx(this ILogger logger, LogLevel logLevel, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.Log(logLevel, exception, message);
}
}
public static void CriticalEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogCritical(exception, message);
}
}
public static void DebugEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogDebug(exception, message);
}
}
public static void ErrorEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogError(exception, message);
}
}
public static void InfoEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogInformation(exception, message);
}
}
public static void TraceEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogTrace(exception, message);
}
}
public static void WarnEx(this ILogger logger, string message, Exception exception = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
using (LogContext.PushProperty("ClassName", logger.GetType().GenericTypeArguments[0].Name))
using (LogContext.PushProperty("MethodName", memberName))
using (LogContext.PushProperty("SourceFilePath", sourceFilePath))
using (LogContext.PushProperty("SourceLineNumber", sourceLineNumber))
{
logger.LogWarning(exception, message);
}
}
}
Last updated