using Serilog; using Serilog.Events; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Http; using KH.WMS.Core.Logging.LogEnums; using System.Collections.Generic; using Serilog.Core; namespace KH.WMS.Core.Logging.Serilog; /// /// Serilog 配置 /// public static class SerilogSetup { /// /// 配置 Serilog(支持文件大小限制) /// public static LoggerConfiguration ConfigureSerilog( string appName, string logDirectory = "Logs", LogEventLevel minimumLevel = LogEventLevel.Information, int retentionDays = 30, int maxFileSizeMB = 100, string? logFileName = null, string? errorFileName = null, string? warningFileName = null) { // 确保日志目录存在 EnsureLogDirectoryExists(logDirectory); var fileSizeLimitBytes = maxFileSizeMB * 1024 * 1024L; // 使用自定义文件名或默认值 var logFile = string.IsNullOrEmpty(logFileName) ? "log-.txt" : $"{logFileName}-.txt"; var errorFile = string.IsNullOrEmpty(errorFileName) ? "error-.txt" : $"{errorFileName}-.txt"; var warningFile = string.IsNullOrEmpty(warningFileName) ? "warning-.txt" : $"{warningFileName}-.txt"; return new LoggerConfiguration() .MinimumLevel.Is(minimumLevel) .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .MinimumLevel.Override("System", LogEventLevel.Warning) .Enrich.FromLogContext() .Enrich.WithProperty("Application", appName) .WriteTo.Console(outputTemplate: GetConsoleOutputTemplate()) .WriteTo.File( path: $"{logDirectory}/{logFile}", rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), restrictedToMinimumLevel: LogEventLevel.Information, encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retentionDays, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.File( path: $"{logDirectory}/{errorFile}", rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), restrictedToMinimumLevel: LogEventLevel.Error, encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retentionDays, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.File( path: $"{logDirectory}/{warningFile}", rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), restrictedToMinimumLevel: LogEventLevel.Warning, encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retentionDays, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1)); } /// /// 配置 Serilog(使用 SerilogOptions) /// public static LoggerConfiguration ConfigureSerilog( string appName, SerilogOptions options) { return ConfigureSerilog( appName, options.LogDirectory, GetMinimumLogLevelFromString(options.MinimumLevel), options.RetentionDays, options.MaxFileSizeMB, options.CustomLogFileName, options.CustomErrorFileName, options.CustomWarningFileName); } /// /// 添加 Serilog 到主机(完整版,支持文件大小限制和自定义文件名) /// public static IHostBuilder AddSerilog( this IHostBuilder hostBuilder, string appName, string logDirectory = "Logs", int retentionDays = 30, int maxFileSizeMB = 100, string? logFileName = null, string? errorFileName = null, string? warningFileName = null) { return hostBuilder.UseSerilog((context, services, loggerConfiguration) => { var config = context.Configuration; var appNameValue = config["App:AppName"] ?? appName; // 从配置读取或使用默认值 var logPath = config["Serilog:LogPath"] ?? logDirectory; var retention = int.Parse(config["Serilog:RetentionDays"] ?? retentionDays.ToString()); var maxSize = int.Parse(config["Serilog:MaxFileSizeMB"] ?? maxFileSizeMB.ToString()); var fileSizeLimitBytes = maxSize * 1024 * 1024L; // 是否输出到控制台 var writeToConsole = bool.Parse(config["Serilog:WriteToConsole"] ?? "true"); // 从配置读取自定义文件名(支持通过配置文件设置) var customLogFileName = config["Serilog:FileNames:Log"] ?? logFileName; var customErrorFileName = config["Serilog:FileNames:Error"] ?? errorFileName; var customWarningFileName = config["Serilog:FileNames:Warning"] ?? warningFileName; // 使用自定义文件名或默认值 var logFile = string.IsNullOrEmpty(customLogFileName) ? "log-.txt" : $"{customLogFileName}-.txt"; var errorFile = string.IsNullOrEmpty(customErrorFileName) ? "error-.txt" : $"{customErrorFileName}-.txt"; var warningFile = string.IsNullOrEmpty(customWarningFileName) ? "warning-.txt" : $"{customWarningFileName}-.txt"; // 确保日志目录存在 EnsureLogDirectoryExists(logPath); loggerConfiguration .ReadFrom.Configuration(config) .ReadFrom.Services(services) .MinimumLevel.Is(GetMinimumLogLevel(config)) .MinimumLevel.Override("Microsoft", GetModuleLogLevel(config, "Microsoft")) .MinimumLevel.Override("Microsoft.EntityFrameworkCore", GetModuleLogLevel(config, "EntityFrameworkCore")) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", GetModuleLogLevel(config, "System")) .Enrich.FromLogContext() .Enrich.WithProperty("Application", appNameValue) .Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName); // 根据配置决定是否输出到控制台 if (writeToConsole) { loggerConfiguration.WriteTo.Console(outputTemplate: GetConsoleOutputTemplate()); } // 文件输出 loggerConfiguration // 普通日志 - 只记录 Information 级别 .WriteTo.File( path: Path.Combine(logPath, logFile), rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), restrictedToMinimumLevel: LogEventLevel.Information, encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retention, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1)) // 错误日志 - 只记录 Error 及 Fatal 级别 .WriteTo.Logger(lc => lc .Filter.ByIncludingOnly(e => e.Level >= LogEventLevel.Error) .WriteTo.File( path: Path.Combine(logPath, errorFile), rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retention, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1))) // 警告日志 - 只记录 Warning 级别(不包括 Error) .WriteTo.Logger(lc => lc .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning) .WriteTo.File( path: Path.Combine(logPath, warningFile), rollingInterval: RollingInterval.Day, outputTemplate: GetFileOutputTemplate(), encoding: System.Text.Encoding.UTF8, fileSizeLimitBytes: fileSizeLimitBytes, retainedFileCountLimit: retention, rollOnFileSizeLimit: true, flushToDiskInterval: TimeSpan.FromSeconds(1))); }); } /// /// 添加 Serilog 到主机(使用 SerilogOptions) /// public static IHostBuilder AddSerilog( this IHostBuilder hostBuilder, string appName, SerilogOptions options) { return AddSerilog( hostBuilder, appName, options.LogDirectory, options.RetentionDays, options.MaxFileSizeMB, options.CustomLogFileName, options.CustomErrorFileName, options.CustomWarningFileName); } /// /// 确保日志目录存在 /// private static void EnsureLogDirectoryExists(string logDirectory) { if (!Directory.Exists(logDirectory)) { Directory.CreateDirectory(logDirectory); } } /// /// 获取控制台输出模板 /// private static string GetConsoleOutputTemplate() { return "[{Timestamp:HH:mm:ss} {Level:u3}] [{ModuleCode}] {Message:lj}{NewLine}{Exception}"; } /// /// 获取文件输出模板 /// private static string GetFileOutputTemplate() { return "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{ModuleCode}] [LogType:{LogType}] [TenantId:{TenantId}] [UserId:{UserId}] [RequestId:{RequestId}] {Message:lj}{NewLine}{Exception}"; } /// /// 获取最低日志级别 /// private static LogEventLevel GetMinimumLogLevel(IConfiguration config) { var level = config["Serilog:MinimumLevel:Default"]; return level switch { "Verbose" => LogEventLevel.Verbose, "Debug" => LogEventLevel.Debug, "Information" => LogEventLevel.Information, "Warning" => LogEventLevel.Warning, "Error" => LogEventLevel.Error, "Fatal" => LogEventLevel.Fatal, _ => LogEventLevel.Information }; } /// /// 获取模块日志级别 /// private static LogEventLevel GetModuleLogLevel(IConfiguration config, string module) { var level = config[$"Serilog:MinimumLevel:Override:{module}"]; return level switch { "Verbose" => LogEventLevel.Verbose, "Debug" => LogEventLevel.Debug, "Information" => LogEventLevel.Information, "Warning" => LogEventLevel.Warning, "Error" => LogEventLevel.Error, "Fatal" => LogEventLevel.Fatal, _ => LogEventLevel.Warning }; } /// /// 从字符串获取日志级别 /// private static LogEventLevel GetMinimumLogLevelFromString(string level) { return level switch { "Verbose" => LogEventLevel.Verbose, "Debug" => LogEventLevel.Debug, "Information" => LogEventLevel.Information, "Warning" => LogEventLevel.Warning, "Error" => LogEventLevel.Error, "Fatal" => LogEventLevel.Fatal, _ => LogEventLevel.Information }; } }