NLogHub.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using NLog;
  5. using NLog.Config;
  6. using NLog.Targets;
  7. namespace TeamAAS.Logging
  8. {
  9. /// <summary>
  10. /// NLog 统一接入点:全应用只此一份 NLog 配置,挂两个文件目标——
  11. /// 公共日志(Common):路径由 CommonPattern 解析(年/日期滚动),AppLogger 与插件 1/2 级日志共用;
  12. /// 任务日志(Task):路径由 TaskPattern 解析,流程名/任务名由每条日志的事件属性带入。
  13. /// 单文件大小归档(ArchiveAboveSize)与保留天数自动删除(MaxArchiveDays)均由 NLog 原生完成。
  14. /// 行格式由调用侧拼好后以 ${message} 原样写入。
  15. /// </summary>
  16. internal static class NLogHub
  17. {
  18. /// <summary>公共日志器名(AppLogger + 插件 1/2 级共用)。</summary>
  19. public const string CommonLoggerName = "TeamAAS.Common";
  20. /// <summary>任务日志器名(插件 3/4 级用)。</summary>
  21. public const string TaskLoggerName = "TeamAAS.Task";
  22. /// <summary>流程名事件属性名(TaskPattern 中的 {flow})。</summary>
  23. public const string FlowPropertyName = "flow";
  24. /// <summary>任务名事件属性名(TaskPattern 中的 {task})。</summary>
  25. public const string TaskPropertyName = "task";
  26. private static readonly object Gate = new object();
  27. private static PluginLogConfig _config;
  28. private static string _appliedSignature;
  29. /// <summary>下发日志配置(启动加载/保存系统配置时调用),配置变化时重建 NLog 目标。</summary>
  30. public static void Configure(PluginLogConfig config)
  31. {
  32. lock (Gate)
  33. {
  34. _config = config ?? new PluginLogConfig();
  35. _appliedSignature = null; // 强制下次 Apply 重建
  36. }
  37. Apply();
  38. }
  39. /// <summary>确保 NLog 配置已就绪(未下发时用默认配置);配置未变则幂等返回。</summary>
  40. public static void Apply()
  41. {
  42. lock (Gate)
  43. {
  44. var cfg = _config ?? (_config = new PluginLogConfig());
  45. var signature = BuildSignature(cfg);
  46. if (signature == _appliedSignature && LogManager.Configuration != null)
  47. return;
  48. var rootDir = ResolveRoot(cfg.RootPath);
  49. var commonFileName = Path.Combine(rootDir, ToLayout(cfg.CommonPattern, @"{year}\{time}.log"));
  50. var taskFileName = Path.Combine(rootDir, ToLayout(cfg.TaskPattern, @"{flow}\{task}\{year}\{time}.log"));
  51. long maxBytes = cfg.MaxFileMB > 0 ? (long)cfg.MaxFileMB * 1024 * 1024 : 0;
  52. int maxDays = cfg.RetentionDays > 0 ? cfg.RetentionDays : 0;
  53. var nlogCfg = new LoggingConfiguration();
  54. var commonTarget = MakeTarget("common", commonFileName, maxBytes, maxDays);
  55. var taskTarget = MakeTarget("task", taskFileName, maxBytes, maxDays);
  56. nlogCfg.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, commonTarget, CommonLoggerName);
  57. nlogCfg.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, taskTarget, TaskLoggerName);
  58. LogManager.Configuration = nlogCfg;
  59. _appliedSignature = signature;
  60. }
  61. }
  62. /// <summary>构造一个文件目标:日期滚动 + 超大小归档 + 超保留天数自动删除。</summary>
  63. private static FileTarget MakeTarget(string name, string fileName, long maxBytes, int maxDays)
  64. => new FileTarget(name)
  65. {
  66. FileName = fileName,
  67. Layout = "${message}",
  68. Encoding = Encoding.UTF8,
  69. KeepFileOpen = false,
  70. ConcurrentWrites = true,
  71. ArchiveAboveSize = maxBytes, // 0=不按大小归档
  72. ArchiveNumbering = ArchiveNumberingMode.Rolling,
  73. MaxArchiveFiles = 0, // 不按份数限制
  74. MaxArchiveDays = maxDays // 0=不按天数删除
  75. };
  76. /// <summary>公共日志器(AppLogger + 插件 1/2 级)。</summary>
  77. public static Logger Common => LogManager.GetLogger(CommonLoggerName);
  78. /// <summary>任务日志器(插件 3/4 级)。</summary>
  79. public static Logger Task => LogManager.GetLogger(TaskLoggerName);
  80. /// <summary>解析日志根目录:绝对路径直接用,相对路径拼到程序目录下。</summary>
  81. private static string ResolveRoot(string root)
  82. => Path.IsPathRooted(root ?? "")
  83. ? root
  84. : Path.Combine(TeamAAS.PathHelper.Root, string.IsNullOrWhiteSpace(root) ? "Logs" : root);
  85. /// <summary>把路径公式里的占位符转为 NLog 布局渲染器(日期/事件属性),交由 NLog 自行滚动与归档。</summary>
  86. private static string ToLayout(string pattern, string fallback)
  87. {
  88. if (string.IsNullOrWhiteSpace(pattern)) pattern = fallback;
  89. return pattern
  90. .Replace("{year}", "${date:format=yyyy}")
  91. .Replace("{month}", "${date:format=MM}")
  92. .Replace("{day}", "${date:format=dd}")
  93. .Replace("{time}", "${date:format=yyyy-MM-dd}")
  94. .Replace("{timestamp}", "${date:format=yyyyMMdd_HHmmss}")
  95. .Replace("{flow}", "${event-properties:item=" + FlowPropertyName + ":whenEmpty=_}")
  96. .Replace("{task}", "${event-properties:item=" + TaskPropertyName + ":whenEmpty=_}");
  97. }
  98. /// <summary>配置指纹:任一关键字段变化则重建 NLog 配置。</summary>
  99. private static string BuildSignature(PluginLogConfig c)
  100. => string.Join("|", c.RootPath, c.CommonPattern, c.TaskPattern, c.MaxFileMB, c.RetentionDays);
  101. }
  102. }