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 初始化日志
}
}