diff --git a/SlnMesnac.Serilog/SerilogExtensions.cs b/SlnMesnac.Serilog/SerilogExtensions.cs index c73c411..180da7e 100644 --- a/SlnMesnac.Serilog/SerilogExtensions.cs +++ b/SlnMesnac.Serilog/SerilogExtensions.cs @@ -37,25 +37,102 @@ namespace SlnMesnac.Serilog { public static void UseSerilogExtensions(this IServiceProvider service) { - - #region 通过配置文件读取日志存放位置 var appConfig = service.GetService(); - var logPath = $"{appConfig.logPath}/Logs/"; + var logPath = appConfig?.logPath ?? "Logs"; + + // 如果是多实例,使用实例专用的日志路径 + // 注意:App类中的InstanceDataPath已经是实例专用的路径 + if (appConfig?.InstanceDataPath != null) + { + // 使用实例数据路径下的Logs目录 + logPath = Path.Combine(appConfig.InstanceDataPath, "Logs"); + } + + // 确保日志目录存在 + EnsureLogDirectories(logPath); #endregion - Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console() - .WriteTo.Logger(lc => lc - .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Info")) - .WriteTo.File(Path.Combine($"{logPath}/Info/", "Info.log"), rollingInterval: RollingInterval.Day)) - .WriteTo.Logger(lc => lc - .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("data")) - .WriteTo.File(Path.Combine($"{logPath}/data/", "data.log"), rollingInterval: RollingInterval.Day)) - .WriteTo.Logger(lc => lc - .Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Error")) - .WriteTo.File(Path.Combine($"{logPath}/Error/", "Error.log"), rollingInterval: RollingInterval.Day)) - .CreateLogger(); + // 构建日志配置,包含实例ID作为上下文 + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .Enrich.WithProperty("InstanceId", appConfig?.InstanceId ?? "Unknown") + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} {InstanceId}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(logEvent => + logEvent.Properties.ContainsKey("Module") && + logEvent.Properties["Module"].ToString().Contains("Info")) + .WriteTo.File( + Path.Combine($"{logPath}/Info/", "Info.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3} {InstanceId}] {Message:lj}{NewLine}{Exception}", + retainedFileCountLimit: 7)) // 保留最近7天的日志文件 + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(logEvent => + logEvent.Properties.ContainsKey("Module") && + logEvent.Properties["Module"].ToString().Contains("Data")) + .WriteTo.File( + Path.Combine($"{logPath}/Data/", "Data.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3} {InstanceId}] {Message:lj}{NewLine}{Exception}", + retainedFileCountLimit: 7)) + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(logEvent => + logEvent.Properties.ContainsKey("Module") && + logEvent.Properties["Module"].ToString().Contains("Error")) + .WriteTo.File( + Path.Combine($"{logPath}/Error/", "Error.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u3} {InstanceId}] {Message:lj}{NewLine}{Exception}", + retainedFileCountLimit: 30)) // 错误日志保留更长时间 + .CreateLogger(); + } + + /// + /// 确保日志目录存在 + /// + private static void EnsureLogDirectories(string baseLogPath) + { + try + { + // 确保基础日志目录存在 + if (!Directory.Exists(baseLogPath)) + { + Directory.CreateDirectory(baseLogPath); + } + + // 确保各模块日志子目录存在 + string[] subDirectories = { "Info", "Data", "Error", "Debug", "Audit" }; + foreach (var subDir in subDirectories) + { + string dirPath = Path.Combine(baseLogPath, subDir); + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + } + + // 创建当前实例的启动日志文件 + string startupLog = Path.Combine(baseLogPath, "Startup.log"); + File.AppendAllText(startupLog, + $"=== Instance Startup ===\r\n" + + $"Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}\r\n" + + $"Base Log Path: {baseLogPath}\r\n" + + $"=================================================================\r\n"); + } + catch (Exception ex) + { + // 如果无法创建目录,使用临时目录 + Console.WriteLine($"无法创建日志目录 {baseLogPath}: {ex.Message}"); + + // 尝试使用临时目录作为后备 + string tempLogPath = Path.Combine(Path.GetTempPath(), "SlnMesnac", "Logs"); + if (!Directory.Exists(tempLogPath)) + { + Directory.CreateDirectory(tempLogPath); + } + } } } } diff --git a/SlnMesnac.Serilog/SerilogHelper.cs b/SlnMesnac.Serilog/SerilogHelper.cs new file mode 100644 index 0000000..25cd1cb --- /dev/null +++ b/SlnMesnac.Serilog/SerilogHelper.cs @@ -0,0 +1,95 @@ +using Serilog; +using System; +using System.Collections.Generic; +using System.Text; + +#region << 版 本 注 释 >> +/*-------------------------------------------------------------------- +* 版权所有 (c) 2024 WenJY 保留所有权利。 +* CLR版本:4.0.30319.42000 +* 机器名称:T14-GEN3-7895 +* 命名空间:SlnMesnac.Serilog +* 唯一标识:fae9d68d-1992-4a03-b299-19edd9fc786d +* +* 创建者:WenJY +* 电子邮箱: +* 创建时间:2024-12-26 8:46:21 +* 版本:V1.0.0 +* 描述: +* +*-------------------------------------------------------------------- +* 修改人: +* 时间: +* 修改说明: +* +* 版本:V1.0.0 +*--------------------------------------------------------------------*/ +#endregion << 版 本 注 释 >> +namespace SlnMesnac.Serilog +{ + /// + /// Serilog日志类 + /// + public class SerilogHelper + { + private readonly ILogger? Info_logger = Log.ForContext("Module", "Info"); + private readonly ILogger? Data_logger = Log.ForContext("Module", "Data"); + private readonly ILogger? Error_logger = Log.ForContext("Module", "Error"); + + /// + /// Info日志 + /// + /// + public void Info(string msg) + { + if (Info_logger != null) + { + this.Info_logger.Information(msg); + } + } + + /// + /// Data日志 + /// + /// + public void Data(string msg) + { + if (Data_logger != null) + { + this.Data_logger.Information(msg); + } + } + + /// + /// Error日志 + /// + /// + /// + public void Error(string msg, Exception ex = null) + { + if (!string.IsNullOrEmpty(msg) && ex == null) + { + this.Error_logger.Information("【附加信息】 : {0}
", new object[] { msg }); + } + else if (!string.IsNullOrEmpty(msg) && ex != null) + { + string errorMsg = BeautyErrorMsg(ex); + this.Error_logger.Information("【附加信息】 : {0}
{1}", new object[] { msg, errorMsg }); + } + else if (string.IsNullOrEmpty(msg) && ex != null) + { + string errorMsg = BeautyErrorMsg(ex); + this.Error_logger.Information(errorMsg); + } + } + + private string BeautyErrorMsg(Exception ex) + { + string errorMsg = string.Format("【异常类型】:{0}
【异常信息】:{1}
【堆栈调用】:{2}", new object[] { ex.GetType().Name, ex.Message, ex.StackTrace }); + errorMsg = errorMsg.Replace("\r\n", "
"); + errorMsg = errorMsg.Replace("位置", "位置"); + return errorMsg; + } + + } +} \ No newline at end of file diff --git a/SlnMesnac.WPF/App.xaml.cs b/SlnMesnac.WPF/App.xaml.cs index ee740ce..e363547 100644 --- a/SlnMesnac.WPF/App.xaml.cs +++ b/SlnMesnac.WPF/App.xaml.cs @@ -102,11 +102,6 @@ namespace SlnMesnac.WPF // 根据实例ID调整数据库连接(如果需要) // AdjustDatabaseConnectionForInstance(appConfig); } - - Log.Information($"应用程序启动 - 实例ID: {_instanceId}, 进程ID: {Process.GetCurrentProcess().Id}"); - Log.Information($"日志目录: {appConfig?.logPath}"); - Log.Information($"数据目录: {_instanceDataPath}"); - // 显示主窗口 var loginWindow = ServiceProvider.GetRequiredService(); loginWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen; @@ -114,6 +109,13 @@ namespace SlnMesnac.WPF // 在窗口标题显示实例信息 loginWindow.Title = $"{loginWindow.Title} [{_instanceId}]"; loginWindow.Show(); + + var serilog = ServiceProvider.GetRequiredService(); + + serilog.Info($"应用程序启动 - 实例ID: {_instanceId}, 进程ID: {Process.GetCurrentProcess().Id}"); + serilog.Info($"日志目录: {appConfig?.logPath}"); + serilog.Data($"数据目录: {_instanceDataPath}"); + serilog.Error($"日志目录: {appConfig?.logPath}"); } catch (Exception exception) { @@ -169,11 +171,11 @@ namespace SlnMesnac.WPF _instanceDataPath = Path.Combine(baseDataRoot, appName, "Instances", _instanceId); Directory.CreateDirectory(_instanceDataPath); - Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Data")); + //Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Data")); Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Logs")); - Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Config")); - Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Cache")); - Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Temp")); + //Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Config")); + //Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Cache")); + //Directory.CreateDirectory(Path.Combine(_instanceDataPath, "Temp")); SaveInstanceInfo(); @@ -183,13 +185,42 @@ namespace SlnMesnac.WPF /// /// 获取数据存储根路径 /// + //private string GetDataRootPath() + //{ + // string envPath = Environment.GetEnvironmentVariable("SLNMESNAC_DATA_ROOT"); + // if (!string.IsNullOrEmpty(envPath) && Directory.Exists(envPath)) + // return envPath; + + // return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + //} + private string GetDataRootPath() { - string envPath = Environment.GetEnvironmentVariable("SLNMESNAC_DATA_ROOT"); - if (!string.IsNullOrEmpty(envPath) && Directory.Exists(envPath)) - return envPath; + // 使用当前系统目录 + InstanceData 路径 + string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; + string instanceDataPath = Path.Combine(currentDirectory, "InstanceData"); - return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + // 如果无法访问当前目录,使用应用程序数据目录作为后备 + try + { + // 尝试访问当前目录,确保我们有权限 + if (!Directory.Exists(instanceDataPath)) + { + Directory.CreateDirectory(instanceDataPath); + } + return instanceDataPath; + } + catch + { + // 如果没有权限,使用用户应用程序数据目录 + string fallbackPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + Assembly.GetExecutingAssembly().GetName().Name, + "InstanceData" + ); + Directory.CreateDirectory(fallbackPath); + return fallbackPath; + } } /// @@ -325,7 +356,7 @@ namespace SlnMesnac.WPF return appConfig; }); - //services.AddSingleton(); + services.AddSingleton(); // 加载程序集 Assembly[] assemblies = { diff --git a/SlnMesnac.WPF/appsettings.json b/SlnMesnac.WPF/appsettings.json index 1bcbb01..48637a6 100644 --- a/SlnMesnac.WPF/appsettings.json +++ b/SlnMesnac.WPF/appsettings.json @@ -9,6 +9,7 @@ "AllowedHosts": "*", "AppConfig": { "logPath": "D:\\net6.0-windows\\log", + "InstanceDataPath": "D:\\net6.0-windows\\InstanceData", "SqlConfig": [ { "configId": "mes",