OperationLogOptions.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Collections.Concurrent;
  3. namespace Aivfo.OperationLog
  4. {
  5. /// <summary>
  6. /// 组件配置(14§10 可配置)。集中持有,支持运行时改(热生效简化为:直接改属性即下次记日志生效)。
  7. /// 模块级开关:默认全开,可按模块名设 开/关 + 最低级别。
  8. /// </summary>
  9. public sealed class OperationLogOptions
  10. {
  11. /// <summary>端/服务名(project 字段),如 "operate" / "front"。</summary>
  12. public string Project { get; set; } = "operate";
  13. /// <summary>Kafka 地址,如 "localhost:9092"。</summary>
  14. public string KafkaBootstrapServers { get; set; } = "localhost:9092";
  15. /// <summary>Kafka topic。</summary>
  16. public string Topic { get; set; } = "tl-oplog";
  17. /// <summary>全局总开关。关掉则完全不记(连入队都跳过)。</summary>
  18. public bool Enabled { get; set; } = true;
  19. /// <summary>
  20. /// 全局最低级别。低于此级别的日志不发 Kafka。
  21. /// 默认 Info:只发操作级;调试级(Debug)默认走本地文件、不入 Kafka。
  22. /// </summary>
  23. public OpLogLevel GlobalLevel { get; set; } = OpLogLevel.Info;
  24. /// <summary>内存队列上限。满了走降级(丢弃 Debug / 落本地兜底)。</summary>
  25. public int QueueCapacity { get; set; } = 10000;
  26. /// <summary>后台发送批量大小。</summary>
  27. public int BatchSize { get; set; } = 100;
  28. /// <summary>本地文件目录(调试级 + 兜底)。</summary>
  29. public string LocalLogDir { get; set; } = "oplog_local";
  30. /// <summary>设备上下文:培养箱 SN(可选,作为全局默认)。</summary>
  31. public string TlSn { get; set; }
  32. /// <summary>本机 host(默认取机器名)。</summary>
  33. public string Host { get; set; } = SafeMachineName();
  34. // ===== G3-3 / 14§10 配置热生效(集中下发的本地落地) =====
  35. /// <summary>
  36. /// 配置文件路径(JSON)。非空则组件后台轮询该文件,改文件即热生效(不重编)。
  37. /// "集中下发"落地方式:日志微服务/运维把该文件下发/覆盖到各端此路径即可。
  38. /// 为空则关闭配置热加载(仅用代码 Init 时的设置)。
  39. /// </summary>
  40. public string ConfigFilePath { get; set; }
  41. /// <summary>配置文件轮询间隔(秒)。默认 15。</summary>
  42. public int ConfigReloadSeconds { get; set; } = 15;
  43. // ===== G3-3 / 14§11 本地兜底补送 =====
  44. /// <summary>是否启用 Kafka 恢复后补送本地兜底文件。默认开。</summary>
  45. public bool EnableFallbackResend { get; set; } = true;
  46. /// <summary>兜底补送轮询间隔(秒)。默认 60。</summary>
  47. public int FallbackResendSeconds { get; set; } = 60;
  48. /// <summary>每轮补送最多处理的兜底文件数(限流,避免一次扫太多)。默认 5。</summary>
  49. public int ResendFilesPerCycle { get; set; } = 5;
  50. // 模块级开关:moduleName -> (enabled, minLevel)。缺省即全开、按 GlobalLevel。
  51. private readonly ConcurrentDictionary<string, ModuleSetting> _modules =
  52. new ConcurrentDictionary<string, ModuleSetting>();
  53. /// <summary>设置某模块的开关与最低级别(运行时可调,热生效)。</summary>
  54. public void SetModule(string module, bool enabled, OpLogLevel minLevel = OpLogLevel.Info)
  55. {
  56. if (string.IsNullOrEmpty(module)) return;
  57. _modules[module] = new ModuleSetting { Enabled = enabled, MinLevel = minLevel };
  58. }
  59. /// <summary>判断某模块是否启用(总开关 + 模块开关)。关闭则连本地调试文件也不写。</summary>
  60. public bool IsModuleEnabled(string module)
  61. {
  62. if (!Enabled) return false;
  63. if (!string.IsNullOrEmpty(module) && _modules.TryGetValue(module, out var s))
  64. return s.Enabled;
  65. return true;
  66. }
  67. /// <summary>
  68. /// 判断某模块某级别是否应**发 Kafka 入库**(操作级门槛)。
  69. /// 调试级(Debug)天然低于 Info,故默认不发 Kafka、走本地文件——这是 14§4 两级设计。
  70. /// 要让某模块的调试级也入库(很少用),把该模块 MinLevel 设为 Debug。
  71. /// </summary>
  72. public bool ShouldSendKafka(string module, OpLogLevel level)
  73. {
  74. if (!IsModuleEnabled(module)) return false;
  75. if (!string.IsNullOrEmpty(module) && _modules.TryGetValue(module, out var s))
  76. return level >= s.MinLevel;
  77. return level >= GlobalLevel;
  78. }
  79. private static string SafeMachineName()
  80. {
  81. try { return Environment.MachineName; }
  82. catch { return "unknown"; }
  83. }
  84. /// <summary>
  85. /// 应用一份 JSON 配置(热生效)。容错:任意字段缺失/格式错都忽略该字段,不抛。
  86. /// 形如:{ "enabled":true, "globalLevel":"Info",
  87. /// "modules":{ "串口":{"enabled":true,"minLevel":"Debug"}, "对焦调试":{"enabled":true} } }
  88. /// </summary>
  89. public void ApplyConfigJson(string json)
  90. {
  91. if (string.IsNullOrWhiteSpace(json)) return;
  92. try
  93. {
  94. var root = Newtonsoft.Json.Linq.JObject.Parse(json);
  95. var en = root["enabled"];
  96. if (en != null && en.Type == Newtonsoft.Json.Linq.JTokenType.Boolean)
  97. Enabled = (bool)en;
  98. var gl = root["globalLevel"];
  99. if (gl != null && TryParseLevel(gl.ToString(), out var glLevel))
  100. GlobalLevel = glLevel;
  101. var modules = root["modules"] as Newtonsoft.Json.Linq.JObject;
  102. if (modules != null)
  103. {
  104. foreach (var prop in modules.Properties())
  105. {
  106. var name = prop.Name;
  107. if (string.IsNullOrEmpty(name)) continue;
  108. var m = prop.Value as Newtonsoft.Json.Linq.JObject;
  109. if (m == null) continue;
  110. // 模块缺省:enabled=true、minLevel 跟随全局。
  111. bool mEnabled = true;
  112. var me = m["enabled"];
  113. if (me != null && me.Type == Newtonsoft.Json.Linq.JTokenType.Boolean)
  114. mEnabled = (bool)me;
  115. var minLevel = GlobalLevel;
  116. var ml = m["minLevel"];
  117. if (ml != null) TryParseLevel(ml.ToString(), out minLevel);
  118. SetModule(name, mEnabled, minLevel);
  119. }
  120. }
  121. }
  122. catch { /* 配置解析失败不影响业务,沿用旧配置 */ }
  123. }
  124. /// <summary>解析级别字符串(Debug/Info,大小写不敏感)。</summary>
  125. private static bool TryParseLevel(string s, out OpLogLevel level)
  126. {
  127. level = OpLogLevel.Info;
  128. if (string.IsNullOrWhiteSpace(s)) return false;
  129. switch (s.Trim().ToLowerInvariant())
  130. {
  131. case "debug": case "0": level = OpLogLevel.Debug; return true;
  132. case "info": case "1": level = OpLogLevel.Info; return true;
  133. default: return false;
  134. }
  135. }
  136. private sealed class ModuleSetting
  137. {
  138. public bool Enabled;
  139. public OpLogLevel MinLevel;
  140. }
  141. }
  142. }