LogHub.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using log4net;
  2. using log4net.Config;
  3. using log4net.Repository;
  4. using System.Text;
  5. using System.Timers;
  6. using System.Xml;
  7. namespace LogHelper
  8. {
  9. /// <summary>
  10. /// 日志帮助类
  11. /// </summary>
  12. public static class LogHub
  13. {
  14. /// <summary>
  15. /// 日志仓库
  16. /// </summary>
  17. public static ILoggerRepository? LoggerRepository { get; set; }
  18. #region 写入日志
  19. private static ILog? GetLog(ILogType type, string flagKey)
  20. {
  21. if (LoggerRepository == null)
  22. return null;
  23. if (type.SubLogNames.ContainsKey(flagKey))
  24. return LogManager.GetLogger(LoggerRepository.Name, type.SubLogNames[flagKey]);
  25. var defaultKey = string.Empty;
  26. foreach (var key in type.SubLogNames.Keys)
  27. {
  28. defaultKey = key;
  29. break;
  30. }
  31. return LogManager.GetLogger(LoggerRepository.Name, type.SubLogNames[defaultKey]);
  32. }
  33. #region Info
  34. /// <summary>
  35. /// 运行记录
  36. /// </summary>
  37. /// <param name="type">日志类型</param>
  38. /// <param name="message">消息</param>
  39. /// <param name="ex">异常</param>
  40. /// <param name="flagKey">日志子项索引Key</param>
  41. public static void Info(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Info(GetLogMessage(message, ex));
  42. /// <summary>
  43. /// 运行记录
  44. /// </summary>
  45. /// <param name="type">日志类型</param>
  46. /// <param name="message">消息</param>
  47. /// <param name="flagKey">日志子项索引Key</param>
  48. public static void Info(this ILogType type, string message, string flagKey) => type.Info(message, null!, flagKey);
  49. /// <summary>
  50. /// 运行记录
  51. /// </summary>
  52. /// <param name="type">日志类型</param>
  53. /// <param name="ex">异常</param>
  54. /// <param name="flagKey">日志子项索引Key</param>
  55. public static void Info(this ILogType type, Exception ex, string flagKey) => type.Info(null!, ex, flagKey);
  56. #endregion Info
  57. #region Error
  58. /// <summary>
  59. /// 错误记录
  60. /// </summary>
  61. /// <param name="type">日志类型</param>
  62. /// <param name="message">消息</param>
  63. /// <param name="ex">异常</param>
  64. /// <param name="flagKey">日志子项索引Key</param>
  65. public static void Error(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Error(GetLogMessage(message, ex));
  66. /// <summary>
  67. /// 错误记录
  68. /// </summary>
  69. /// <param name="type">日志类型</param>
  70. /// <param name="message">消息</param>
  71. /// <param name="flagKey">日志子项索引Key</param>
  72. public static void Error(this ILogType type, string message, string flagKey) => type.Error(message, null!, flagKey);
  73. /// <summary>
  74. /// 错误记录
  75. /// </summary>
  76. /// <param name="type">日志类型</param>
  77. /// <param name="ex">异常</param>
  78. /// <param name="flagKey">日志子项索引Key</param>
  79. public static void Error(this ILogType type, Exception ex, string flagKey) => type.Error(null!, ex, flagKey);
  80. #endregion Error
  81. #region Warn
  82. /// <summary>
  83. /// 警告
  84. /// </summary>
  85. /// <param name="type">日志类型</param>
  86. /// <param name="message">消息</param>
  87. /// <param name="ex">异常</param>
  88. /// <param name="flagKey">日志子项索引Key</param>
  89. public static void Warn(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Warn(GetLogMessage(message, ex));
  90. /// <summary>
  91. /// 警告
  92. /// </summary>
  93. /// <param name="type">日志类型</param>
  94. /// <param name="message">消息</param>
  95. /// <param name="flagKey">日志子项索引Key</param>
  96. public static void Warn(this ILogType type, string message, string flagKey) => type.Warn(message, null!, flagKey);
  97. /// <summary>
  98. /// 警告
  99. /// </summary>
  100. /// <param name="type">日志类型</param>
  101. /// <param name="ex">异常</param>
  102. /// <param name="flagKey">日志子项索引Key</param>
  103. public static void Warn(this ILogType type, Exception ex, string flagKey) => type.Warn(null!, ex, flagKey);
  104. #endregion Warn
  105. /// <summary>
  106. /// 获取详细日志信息
  107. /// </summary>
  108. /// <param name="message">信息</param>
  109. /// <param name="ex">异常</param>
  110. /// <returns>详细信息</returns>
  111. private static string GetLogMessage(string message, Exception ex)
  112. {
  113. if (string.IsNullOrEmpty(message) && ex == null)
  114. return "Unknown error.";
  115. if (string.IsNullOrEmpty(message) && ex != null)
  116. return $"[Type:{ex.GetType().Name}][StackTrace:{ex.StackTrace}][Message:{ex.Message.Replace("\r\n", " ")}]";
  117. if (!string.IsNullOrEmpty(message) && ex == null)
  118. return message;
  119. return $"[Message:{message}][Type:{ex!.GetType().Name}][StackTrace:{ex.StackTrace}][Ex Message:{ex.Message.Replace("\r\n", " ")}]";
  120. }
  121. #endregion 写入日志
  122. #region 初始化日志
  123. private static string? _configPath = "Log";
  124. private static int _fileSize = 500;
  125. private static readonly List<ILogType> InitList = new List<ILogType>();
  126. private static readonly LogConfig ConfigModel = new LogConfig();
  127. private static bool _cleanSet = false;
  128. private static string? _previousCleanDay = null;
  129. private static int _cleanDays = 30;
  130. private static System.Timers.Timer? _logCleanTimer;
  131. /// <summary>
  132. /// 设置配置信息
  133. /// </summary>
  134. /// <param name="logConfig"></param>
  135. public static void SetConfigInfo(LogConfig logConfig)
  136. {
  137. SetConfigPath(logConfig.LogPath);
  138. SetLogFileSize(logConfig.LogSize);
  139. SetLogCleanDays(logConfig.LogDays);
  140. SetConfigInfo(logConfig.Logs!);
  141. }
  142. /// <summary>
  143. /// 日志配置
  144. /// </summary>
  145. /// <param name="path"></param>
  146. public static void SetConfigPath(string path)
  147. {
  148. _configPath = path;
  149. }
  150. /// <summary>
  151. /// 设置日志层级关系
  152. /// </summary>
  153. /// <param name="logConfigs">配置信息</param>
  154. public static void SetConfigInfo(List<LogConfigModel> logConfigs)
  155. {
  156. ConfigModel.Logs = logConfigs;
  157. }
  158. /// <summary>
  159. /// 设置日志项目文件数量和大小
  160. /// </summary>
  161. /// <param name="size">单文件大小(单位MB,最小10)</param>
  162. public static void SetLogFileSize(int size)
  163. {
  164. if (size < 10)
  165. return;
  166. _fileSize = size;
  167. }
  168. /// <summary>
  169. /// 设置日志清理周期
  170. /// </summary>
  171. /// <param name="days">清理周期</param>
  172. public static void SetLogCleanDays(int days)
  173. {
  174. if (days < 7)
  175. return;
  176. _cleanDays = days;
  177. }
  178. /// <summary>
  179. /// 初始化Log组件
  180. /// </summary>
  181. /// <param name="types">日志类型数组</param>
  182. public static void Init(params ILogType[] types)
  183. {
  184. var addedTypes = types.Where(p => !InitList.Contains(p)).ToList();
  185. if (addedTypes.Count == 0)
  186. return;
  187. if (ConfigModel.Logs == null || ConfigModel.Logs.Count == 0)
  188. throw new Exception("Log init, config is null.");
  189. _configPath = ConfigModel.Logs.FirstOrDefault(p => !string.IsNullOrEmpty(p.FileName))!.FileName;
  190. var xmlDoc = new XmlDocument();
  191. xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null));
  192. var configuration = xmlDoc.CreateElement("configuration");
  193. var root = xmlDoc.CreateElement("log4net");
  194. foreach (var item in addedTypes)
  195. {
  196. InitList.Add(item);
  197. var config = ConfigModel.Logs.FirstOrDefault(p => p.Name == item.LogName);
  198. if (config is null)
  199. {
  200. config = new LogConfigModel
  201. {
  202. Name = item.LogName,
  203. FileName = _configPath,
  204. SubLogNames = item.SubLogNames
  205. };
  206. }
  207. else
  208. {
  209. config.SubLogNames ??= new Dictionary<string, string>();
  210. foreach (var key in config.SubLogNames.Keys.Where(key => item.SubLogNames == null || !item.SubLogNames.ContainsKey(key)))
  211. {
  212. item.SubLogNames?.Add(key, config.SubLogNames[key]);
  213. }
  214. foreach (var key in item.SubLogNames!.Keys.Where(key => !config.SubLogNames.ContainsKey(key)))
  215. {
  216. config.SubLogNames.Add(key, item.SubLogNames[key]);
  217. }
  218. }
  219. if (item.SubLogNames == null || item.SubLogNames.Count == 0)
  220. continue;
  221. CreateXml(xmlDoc, root, config);
  222. }
  223. configuration.AppendChild(root);
  224. xmlDoc.AppendChild(configuration);
  225. //InitRepository.LoggerRepository ??= LogManager.CreateRepository("NETCoreRepository");
  226. LoggerRepository ??= LogManager.CreateRepository("NETCoreRepository");
  227. XmlConfigurator.Configure(LoggerRepository, new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(xmlDoc.OuterXml))).BaseStream);
  228. xmlDoc.Save("./log4net.config");
  229. if (_cleanSet) return;
  230. _cleanSet = true;
  231. _previousCleanDay = DateTime.Now.ToString("yyyyMMdd");
  232. // clean log
  233. _logCleanTimer = new System.Timers.Timer(1000 * 60);
  234. _logCleanTimer.Elapsed += LogCleanTimer_Elapsed;
  235. _logCleanTimer.Enabled = true;
  236. }
  237. /// <summary>
  238. /// 创建XML节点
  239. /// </summary>
  240. /// <param name="xmlDoc">XML文档</param>
  241. /// <param name="root"></param>
  242. /// <param name="config"></param>
  243. private static void CreateXml(XmlDocument xmlDoc, XmlElement root, LogConfigModel config)
  244. {
  245. foreach (var key in config.SubLogNames!.Keys)
  246. {
  247. // 创建 Logger
  248. var logger = xmlDoc.CreateElement("logger");
  249. logger.SetAttribute("name", config.SubLogNames[key]);
  250. var level = xmlDoc.CreateElement("level");
  251. level.SetAttribute("value", "ALL");
  252. var appenderRef = xmlDoc.CreateElement("appender-ref");
  253. appenderRef.SetAttribute("ref", $"{config.SubLogNames[key]}Appender");
  254. logger.AppendChild(level);
  255. logger.AppendChild(appenderRef);
  256. // 创建 Appender
  257. var appender = xmlDoc.CreateElement("appender");
  258. appender.SetAttribute("name", $"{config.SubLogNames[key]}Appender");
  259. appender.SetAttribute("type", "log4net.Appender.RollingFileAppender");
  260. var param = xmlDoc.CreateElement("param");
  261. param.SetAttribute("name", "Encoding");
  262. param.SetAttribute("value", "utf-8");
  263. appender.AppendChild(param);
  264. var file = xmlDoc.CreateElement("file");
  265. file.SetAttribute("value", Path.Combine(config.FileName!, config.Name!));
  266. appender.AppendChild(file);
  267. var appendToFile = xmlDoc.CreateElement("appendToFile");
  268. appendToFile.SetAttribute("value", "true");
  269. appender.AppendChild(appendToFile);
  270. var rollingStyle = xmlDoc.CreateElement("rollingStyle");
  271. rollingStyle.SetAttribute("value", "Composite");
  272. appender.AppendChild(rollingStyle);
  273. var maxSizeRollBackups = xmlDoc.CreateElement("maxSizeRollBackups");
  274. maxSizeRollBackups.SetAttribute("value", "-1");
  275. appender.AppendChild(maxSizeRollBackups);
  276. var maximumFileSize = xmlDoc.CreateElement("maximumFileSize");
  277. maximumFileSize.SetAttribute("value", $"{_fileSize}MB");
  278. appender.AppendChild(maximumFileSize);
  279. var lockingModel = xmlDoc.CreateElement("lockingModel");
  280. lockingModel.SetAttribute("type", "log4net.Appender.FileAppender+MinimalLock");
  281. appender.AppendChild(lockingModel);
  282. var staticLogFileName = xmlDoc.CreateElement("staticLogFileName");
  283. staticLogFileName.SetAttribute("value", "false");
  284. appender.AppendChild(staticLogFileName);
  285. var datePattern = xmlDoc.CreateElement("DatePattern");
  286. datePattern.SetAttribute("value", $"/yyyyMMdd/'{config.SubLogNames[key]}.log'");
  287. appender.AppendChild(datePattern);
  288. var layout = xmlDoc.CreateElement("layout");
  289. layout.SetAttribute("type", "log4net.Layout.PatternLayout");
  290. var conversionPattern = xmlDoc.CreateElement("conversionPattern");
  291. conversionPattern.SetAttribute("value", "%date || %5level || %logger || %message || %exception || end %newline");
  292. layout.AppendChild(conversionPattern);
  293. appender.AppendChild(layout);
  294. root.AppendChild(appender);
  295. root.AppendChild(logger);
  296. }
  297. }
  298. private static void LogCleanTimer_Elapsed(object? sender, ElapsedEventArgs e)
  299. {
  300. try
  301. {
  302. if (_previousCleanDay!.Equals(DateTime.Now.ToString("yyyyMMdd")))
  303. return;
  304. _previousCleanDay = DateTime.Now.ToString("yyyyMMdd");
  305. if (!Directory.Exists(_configPath))
  306. return;
  307. string[] typeDirs = Directory.GetDirectories(_configPath);
  308. foreach (var t in typeDirs)
  309. {
  310. string[] dateDirs = Directory.GetDirectories(t);
  311. foreach (var t1 in dateDirs)
  312. {
  313. var dirName = t1[(t1.LastIndexOf('\\') + 1)..];
  314. if (Convert.ToInt32(dirName) < Convert.ToInt32(DateTime.Now.AddDays(_cleanDays * -1).ToString("yyyyMMdd")))
  315. Directory.Delete(t1, true);
  316. }
  317. }
  318. }
  319. catch (Exception)
  320. {
  321. //TODO:增加日志
  322. }
  323. }
  324. #endregion 初始化日志
  325. }
  326. }