基于log4net日志组件的使用
一、前言
系统运行的时候,生产上需要使用文件来记录一些系统运行中的日志,比如系统的对接日志、错误信息、警告信息或者特定调试信息,通过对记录的信息回溯系统执行过程,查找系统问题,帮助开发者解决线上问题等。这个系统都要使用日志组件,一般都会有相应的开源组件,比如log4net。
二、使用
1、在项目引用中引入log4net组件,然后对日志配置文件进行配置,配置信息涉及到文件夹位置、文件名称格式、文件最大大小、文件的内容格式、日志级别等信息,比如Log4Net.config。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> configSections> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="[%p %t] [%date{yyyy-MM-dd HH:mm:ss,fff}] %-5l - %m%n"/> layout> appender> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <param name= "File" value= "logs\Info\log_"/> <param name= "AppendToFile" value= "true"/> <param name= "MaxSizeRollBackups" value= "10"/> <param name= "StaticLogFileName" value= "false"/> <param name= "DatePattern" value= "yyyy-MM-dd".log""/> <param name= "RollingStyle" value= "Date"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别: %-5level 类:%logger - 描述:%message %n"/> layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="Info" /> <param name="LevelMax" value="WARN" /> filter> appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <param name= "File" value= "logs\Error\Log_"/> <param name= "AppendToFile" value= "true"/> <param name= "MaxSizeRollBackups" value= "10"/> <param name= "StaticLogFileName" value= "false"/> <param name= "DatePattern" value= "yyyy-MM-dd".log""/> <param name= "RollingStyle" value= "Date"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别: %-5level 类:%logger - 描述:%message %n"/> layout> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="ERROR" /> <param name="LevelMax" value="FATAL" /> filter> appender> <root> <level value="ALL" /> <appender-ref ref="RollingLogFileAppender" /> <appender-ref ref="RollingFile" /> root> log4net> configuration>
通过这个日志文件定义基本信息,可以在打印日志生成想要的日志文件。
2、在项目代码中使用配置文件,并且提供写入日志的方法,定义一个日志接口(使用文件或者数据库的方式),通过log4net组件来实现文件形式记录日志信息。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TQF.Logger.Logger { ////// 定义日志接口类 /// public interface ILogger { /// /// 调试日志输出 /// /// 输出内容 void Debug(string msg); /// /// 调试日志输出 /// /// 输出内容 /// 输出异常 void Debug(string msg, Exception ex); /// /// 信息日志输出 /// /// 输出内容 void Info(string msg); /// /// 信息日志输出 /// /// 输出内容 /// 输出异常 void Info(string msg, Exception ex); /// /// 警告日志输出 /// /// 输出内容 void Warn(string msg); /// /// 警告日志输出 /// /// 输出内容 /// 输出异常 void Warn(string msg, Exception ex); /// /// 错误日志输出 /// /// 输出内容 void Error(string msg); /// /// 错误日志输出 /// /// 输出内容 /// 输出异常 void Error(string msg, Exception ex); /// /// 致命日志输出 /// /// 输出内容 void Fatal(string msg); /// /// 致命日志输出 /// /// 输出内容 /// 输出异常 void Fatal(string msg, Exception ex); } }
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using log4net; using log4net.Appender; using log4net.Config; namespace TQF.Logger.Logger { ////// 日志接口实现类 /// public class Log4Helper : ILogger { /// /// 考虑使用线程安全的字典 /// private Dictionary<string, ILog> LogDic = new Dictionary<string, ILog>(); /// /// 定义锁对象 /// private object _islock = new object(); /// /// 定义文件名 /// private string fileName = string.Empty; /// /// 使用构造函数日志调用初始化 /// /// 日志文件保存路径[若路径为空,则默认程序根目录Logger文件夹;] /// 日志文件名[若文件名为空,则默认文件名:Default] public Log4Helper(string fileSavePath, string fileName, string logSuffix = ".log") { try { Init(); if (string.IsNullOrEmpty(fileSavePath)) fileSavePath = "Logger"; if (string.IsNullOrEmpty(fileName)) fileName = "Default"; this.fileName = fileName; var repository = LogManager.GetRepository(); var appenders = repository.GetAppenders(); if (appenders.Length == 0) return; var targetApder = appenders.First(p => p.Name == "FileInfoAppender") as RollingFileAppender; targetApder.File = Path.Combine(fileSavePath, this.fileName + logSuffix); targetApder.ActivateOptions(); } catch (Exception ex) { } } /// /// 缓存日志对象(通过日志文件名缓存日志对象,减少日志对象的创建工作) /// /// /// private ILog GetLog(string name) { try { if (LogDic == null) { LogDic = new Dictionary<string, ILog>(); } lock (_islock) { if (!LogDic.ContainsKey(name)) { LogDic.Add(name, LogManager.GetLogger(name)); } } return LogDic[name]; } catch { return LogManager.GetLogger("Default"); } } /// /// 日志记录初始化 /// private void Init() { // 获取日志配置文件 var file = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log4net.config")); // 检测配置文件是否存在 if (file.Exists) { //加载配置文件 XmlConfigurator.Configure(file); } else { new Exception("找不到日志配置文件!"); } } /// /// 调试日志输出 /// /// 内容 public void Debug(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg); } /// /// 调试日志输出 /// /// 输出内容 /// 输出异常 public void Debug(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg, ex); } /// /// 错误日志输出 /// /// 输出内容 public void Error(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg); } /// /// 错误日志输出 /// /// 输出内容 /// 输出异常 public void Error(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg, ex); } /// /// 致命日志输出 /// /// 输出内容 public void Fatal(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg); } /// /// 致命日志输出 /// /// 输出内容 /// 输出异常 public void Fatal(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg, ex); } /// /// 信息日志输出 /// /// 输出内容 public void Info(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg); } /// /// 信息日志输出 /// /// 输出内容 /// 输出异常 public void Info(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg, ex); } /// /// 警告日志输出 /// /// 输出内容 public void Warn(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Warn(msg); } /// /// 警告日志输出 /// /// 输出内容 /// 输出异常 public void Warn(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Warn(msg, ex); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TQF.Logger.Logger; namespace TQF.Logger { ////// 配置文件如果不设置属性“复制到输出目录”不会在debug文件中存在 /// class Program { static void Main(string[] args) { var log4Helper = new Log4Helper(string.Empty, "program"); log4Helper.Info("info"); } } }
通过上述代码,定义日志接口(写入方法,不同层级的写入方法)、日志帮助类实现接口,使用组件读取配置文件、获取日志对象写入日志。
3、上述是按照配置文件格式定义好的文件夹、文件名称、文件内容格式,相对标准化,如果系统要特殊的记录单个日志信息,比如不确定文件夹名称、不确定的的文件名称等,就要开发者进行自定义。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.Concurrent; using System.Configuration; using log4net; using log4net.Appender; using log4net.Core; using log4net.Layout; using log4net.Repository; using log4net.Repository.Hierarchy; [assembly: log4net.Config.XmlConfigurator(Watch = true)] namespace DataPullAPI.Utility { ////// log4net 自定义日志类 /// public static class LogCustomHelper { /// /// 类型安全的字典对象 /// private static readonly ConcurrentDictionary<string, ILog> loggerContainer = new ConcurrentDictionary<string, ILog>(); //默认配置 private const int MAX_SIZE_ROLL_BACKUPS = 20; private const string LAYOUT_PATTERN = "%message"; private const string DATE_PATTERN = "yyyyMMdd"; private const string MAXIMUM_FILE_SIZE = "10MB"; private const string LEVEL = "ALL"; /// /// 获取日志对象 /// /// /// /// /// public static ILog GetCustomLogger(string loggerName, string category = null, bool additivity = false) { return loggerContainer.GetOrAdd(loggerName, delegate (string name) { RollingFileAppender newAppender = GetNewFileApender(loggerName, GetFile(category, loggerName), MAX_SIZE_ROLL_BACKUPS, true, true, MAXIMUM_FILE_SIZE, RollingFileAppender.RollingMode.Composite, DATE_PATTERN, LAYOUT_PATTERN); Hierarchy repository = (Hierarchy)LogManager.GetRepository(); Logger logger = repository.LoggerFactory.CreateLogger(repository, loggerName); logger.Hierarchy = repository; logger.Parent = repository.Root; logger.Level = GetLoggerLevel(LEVEL); logger.Additivity = additivity; logger.AddAppender(newAppender); logger.Repository.Configured = true; return new LogImpl(logger); }); } /// /// 自定日志文件夹和文件路径 /// /// /// /// private static string GetFile(string category, string loggerName) { if (string.IsNullOrEmpty(category)) { return string.Format(@"Logs\{0}\{1}.txt", DateTime.Now.ToString("yyyy-MM-dd"), loggerName); } else { return string.Format(@"Logs\{0}\{1}\{2}.txt", category,DateTime.Now.ToString("yyyy-MM-dd"), loggerName); } } /// /// 获取日志级别 /// /// /// private static Level GetLoggerLevel(string level) { if (!string.IsNullOrEmpty(level)) { switch (level.ToLower().Trim()) { case "debug": return Level.Debug; case "info": return Level.Info; case "warn": return Level.Warn; case "error": return Level.Error; case "fatal": return Level.Fatal; } } return Level.Debug; } /// /// 获取配置信息 /// /// /// /// /// /// /// /// /// /// /// private static RollingFileAppender GetNewFileApender(string appenderName, string file, int maxSizeRollBackups, bool appendToFile = true, bool staticLogFileName = false, string maximumFileSize = "2MB", RollingFileAppender.RollingMode rollingMode = RollingFileAppender.RollingMode.Size, string datePattern = "yyyyMMdd\".txt\"", string layoutPattern = "%d [%t] %-5p %c - %m%n") { RollingFileAppender appender = new RollingFileAppender { LockingModel = new FileAppender.MinimalLock(), Name = appenderName, File = file, AppendToFile = appendToFile, MaxSizeRollBackups = maxSizeRollBackups, MaximumFileSize = maximumFileSize, StaticLogFileName = staticLogFileName, RollingStyle = rollingMode, DatePattern = datePattern }; PatternLayout layout = new PatternLayout(layoutPattern); appender.Layout = layout; layout.ActivateOptions(); appender.ActivateOptions(); return appender; } /// /// 写入日志信息 /// /// 文件夹名称 /// 文件名称 /// 日志信息 public static void Info(string folderName, string fileName,string message) { ILog logger = GetCustomLogger(fileName, folderName); logger.Info(message); } } }
通过在写入日志的时候,传入文件夹和文件名称来自定义日志存储,对于特殊的日志信息可以通过该方法达到特定存储要求。
总结
1、在系统生产环境记录日志可以帮助用户定位系统问题,解决系统问题,对系统执行过程中可能出现异常错误进行捕获记录,对执行全过程进行回溯查看等。
2、系统日志不仅可以使用文件方式进行存储,还可以使用数据库的方式进行存储。