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
};
}
}