using log4net; using log4net.Config; using log4net.Repository; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Timers; using System.Xml; namespace Logs { /// /// 日志帮助类 /// public static class LogHelper { /// /// 日志仓库 /// public static ILoggerRepository? LoggerRepository { get; set; } #region 写入日志 private static ILog? GetLog(ILogType type, string flagKey) { if (LoggerRepository == null) return null; if (type.SubLogNames.ContainsKey(flagKey)) return LogManager.GetLogger(LoggerRepository.Name, type.SubLogNames[flagKey]); string defaultKey = string.Empty; foreach (var key in type.SubLogNames.Keys) { defaultKey = key; break; } return LogManager.GetLogger(LoggerRepository.Name, type.SubLogNames[defaultKey]); } #region Info /// /// 运行记录 /// /// 日志类型 /// 消息 /// 异常 /// 日志子项索引Key public static void Info(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Info(GetLogMessage(message, ex)); /// /// 运行记录 /// /// 日志类型 /// 消息 /// 日志子项索引Key public static void Info(this ILogType type, string message, string flagKey) => type.Info(message, null, flagKey); /// /// 运行记录 /// /// 日志类型 /// 异常 /// 日志子项索引Key public static void Info(this ILogType type, Exception ex, string flagKey) => type.Info(null, ex, flagKey); #endregion Info #region Error /// /// 错误记录 /// /// 日志类型 /// 消息 /// 异常 /// 日志子项索引Key public static void Error(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Error(GetLogMessage(message, ex)); /// /// 错误记录 /// /// 日志类型 /// 消息 /// 日志子项索引Key public static void Error(this ILogType type, string message, string flagKey) => type.Error(message, null, flagKey); /// /// 错误记录 /// /// 日志类型 /// 异常 /// 日志子项索引Key public static void Error(this ILogType type, Exception ex, string flagKey) => type.Error(null, ex, flagKey); #endregion Error #region Warn /// /// 警告 /// /// 日志类型 /// 消息 /// 异常 /// 日志子项索引Key public static void Warn(this ILogType type, string message, Exception ex, string flagKey) => GetLog(type, flagKey.ToUpper())?.Warn(GetLogMessage(message, ex)); /// /// 警告 /// /// 日志类型 /// 消息 /// 日志子项索引Key public static void Warn(this ILogType type, string message, string flagKey) => type.Warn(message, null, flagKey); /// /// 警告 /// /// 日志类型 /// 异常 /// 日志子项索引Key public static void Warn(this ILogType type, Exception ex, string flagKey) => type.Warn(null, ex, flagKey); #endregion Warn /// /// 获取详细日志信息 /// /// 信息 /// 异常 /// 详细信息 private static string GetLogMessage(string message, Exception ex) { if (string.IsNullOrEmpty(message) && ex == null) return "Unknown error."; if (string.IsNullOrEmpty(message) && ex != null) return $"[Type:{ex.GetType().Name}][StackTrace:{ex.StackTrace}][Message:{ex.Message.Replace("\r\n", " ")}]"; if (!string.IsNullOrEmpty(message) && ex == null) return message; return $"[Message:{message}][Type:{ex.GetType().Name}][StackTrace:{ex.StackTrace}][Ex Message:{ex.Message.Replace("\r\n", " ")}]"; } #endregion 写入日志 #region 初始化日志 private static string? _configPath = "Log"; private static int _fileSize = 500; private static readonly List _initList = new List(); private static readonly LogConfig _configModel = new LogConfig(); private static bool _cleanSet = false; private static string _previousCleanDay = null; private static int _cleanDays = 30; private static Timer _logCleanTimer; /// /// 设置配置信息 /// /// 配置信息 public static void SetConfigInfo(LogConfig logConfig) { SetConfigPath(logConfig.LogPath); SetLogFileSize(logConfig.LogSize); SetLogCleanDays(logConfig.LogDays); SetConfigInfo(logConfig.Logs); } /// /// 日志配置 /// /// public static void SetConfigPath(string path) { _configPath = path; } /// /// 设置日志层级关系 /// /// 配置信息 public static void SetConfigInfo(List logConfigs) { _configModel.Logs = logConfigs; } /// /// 设置日志项目文件数量和大小 /// /// 单文件大小(单位MB,最小10) public static void SetLogFileSize(int size) { if (size < 10) return; _fileSize = size; } /// /// 设置日志清理周期 /// /// 清理周期 public static void SetLogCleanDays(int days) { if (days < 7) return; _cleanDays = days; } /// /// 初始化Log组件 /// /// 日志类型数组 public static void Init(params ILogType[] types) { var addedTypes = types.Where(p => !_initList.Contains(p)).ToList(); if (addedTypes.Count == 0) return; if (_configModel.Logs == null || _configModel.Logs.Count == 0) throw new Exception("Log init, config is null."); _configPath = _configModel.Logs.FirstOrDefault(p => !string.IsNullOrEmpty(p.FileName)).FileName; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null)); XmlElement configuration = xmlDoc.CreateElement("configuration"); XmlElement root = xmlDoc.CreateElement("log4net"); foreach (var item in addedTypes) { _initList.Add(item); LogConfigModel config = _configModel.Logs.FirstOrDefault(p => p.Name == item.LogName); if (config is null) { config = new LogConfigModel { Name = item.LogName, FileName = _configPath, SubLogNames = item.SubLogNames }; } else { if (config.SubLogNames is null) config.SubLogNames = new Dictionary(); foreach (var key in config.SubLogNames.Keys) { if (item.SubLogNames != null && item.SubLogNames.ContainsKey(key)) continue; item.SubLogNames?.Add(key, config.SubLogNames[key]); } foreach (var key in item.SubLogNames.Keys) { if (config.SubLogNames.ContainsKey(key)) continue; config.SubLogNames.Add(key, item.SubLogNames[key]); } } if (item.SubLogNames == null || item.SubLogNames.Count == 0) continue; CreateXml(xmlDoc, root, config); } configuration.AppendChild(root); xmlDoc.AppendChild(configuration); //InitRepository.LoggerRepository ??= LogManager.CreateRepository("NETCoreRepository"); if (LoggerRepository == null) LoggerRepository = LogManager.CreateRepository("NETCoreRepository"); XmlConfigurator.Configure(LoggerRepository, new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(xmlDoc.OuterXml))).BaseStream); xmlDoc.Save("./log4net.config"); if (!_cleanSet) { _cleanSet = true; _previousCleanDay = DateTime.Now.ToString("yyyyMMdd"); // clean log _logCleanTimer = new Timer(1000 * 60); _logCleanTimer.Elapsed += LogCleanTimer_Elapsed; _logCleanTimer.Enabled = true; } } /// /// 创建XML节点 /// /// XML文档 /// /// private static void CreateXml(XmlDocument xmlDoc, XmlElement root, LogConfigModel config) { foreach (var key in config.SubLogNames.Keys) { // 创建 Logger XmlElement logger = xmlDoc.CreateElement("logger"); logger.SetAttribute("name", config.SubLogNames[key]); XmlElement level = xmlDoc.CreateElement("level"); level.SetAttribute("value", "ALL"); XmlElement appender_ref = xmlDoc.CreateElement("appender-ref"); appender_ref.SetAttribute("ref", $"{config.SubLogNames[key]}Appender"); logger.AppendChild(level); logger.AppendChild(appender_ref); // 创建 Appender XmlElement appender = xmlDoc.CreateElement("appender"); appender.SetAttribute("name", $"{config.SubLogNames[key]}Appender"); appender.SetAttribute("type", "log4net.Appender.RollingFileAppender"); XmlElement param = xmlDoc.CreateElement("param"); param.SetAttribute("name", "Encoding"); param.SetAttribute("value", "utf-8"); appender.AppendChild(param); XmlElement file = xmlDoc.CreateElement("file"); file.SetAttribute("value", Path.Combine(config.FileName, config.Name)); appender.AppendChild(file); XmlElement appendToFile = xmlDoc.CreateElement("appendToFile"); appendToFile.SetAttribute("value", "true"); appender.AppendChild(appendToFile); XmlElement rollingStyle = xmlDoc.CreateElement("rollingStyle"); rollingStyle.SetAttribute("value", "Composite"); appender.AppendChild(rollingStyle); XmlElement maxSizeRollBackups = xmlDoc.CreateElement("maxSizeRollBackups"); maxSizeRollBackups.SetAttribute("value", "-1"); appender.AppendChild(maxSizeRollBackups); XmlElement maximumFileSize = xmlDoc.CreateElement("maximumFileSize"); maximumFileSize.SetAttribute("value", $"{_fileSize}MB"); appender.AppendChild(maximumFileSize); XmlElement lockingModel = xmlDoc.CreateElement("lockingModel"); lockingModel.SetAttribute("type", "log4net.Appender.FileAppender+MinimalLock"); appender.AppendChild(lockingModel); XmlElement staticLogFileName = xmlDoc.CreateElement("staticLogFileName"); staticLogFileName.SetAttribute("value", "false"); appender.AppendChild(staticLogFileName); XmlElement DatePattern = xmlDoc.CreateElement("DatePattern"); DatePattern.SetAttribute("value", $"/yyyyMMdd/'{config.SubLogNames[key]}.log'"); appender.AppendChild(DatePattern); XmlElement layout = xmlDoc.CreateElement("layout"); layout.SetAttribute("type", "log4net.Layout.PatternLayout"); XmlElement conversionPattern = xmlDoc.CreateElement("conversionPattern"); conversionPattern.SetAttribute("value", "%date || %5level || %logger || %message || %exception || end %newline"); layout.AppendChild(conversionPattern); appender.AppendChild(layout); root.AppendChild(appender); root.AppendChild(logger); } } private static void LogCleanTimer_Elapsed(object sender, ElapsedEventArgs e) { try { if (_previousCleanDay.Equals(DateTime.Now.ToString("yyyyMMdd"))) return; _previousCleanDay = DateTime.Now.ToString("yyyyMMdd"); if (!Directory.Exists(_configPath)) return; string[] typeDirs = Directory.GetDirectories(_configPath); for (int i = 0; i < typeDirs.Length; i++) { string[] dateDirs = Directory.GetDirectories(typeDirs[i]); for (int j = 0; j < dateDirs.Length; j++) { string dirName = dateDirs[j].Substring(dateDirs[j].LastIndexOf('\\') + 1); if (Convert.ToInt32(dirName) < Convert.ToInt32(DateTime.Now.AddDays(_cleanDays * -1).ToString("yyyyMMdd"))) Directory.Delete(dateDirs[j], true); } } } catch (Exception ex) { //LogInfo.Log.Info(ex.Message, ex, "FATAL"); } } #endregion 初始化日志 } }