• 基于log4net日志组件的使用


    一、前言

      应用程序在生产环境运行中产生的错误信息、告警信息、特定调试信息或者接口对接信息如何让开发者进行查找?这时候可以使用日志来回溯系统执行的全过程查找系统问题,帮助开发者解决线上问题等。一般都会选择一个日志组件并且希望其是代码侵入少,使用便捷,写入性能高,比如开源的log4net。

    二、使用

      在项目引用中引入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"/>
          <!--log保留天数-->
          <param name= "MaxSizeRollBackups" value= "10"/>
          <!--日志文件名是否是固定不变的-->
          <param name= "StaticLogFileName" value= "false"/>
          <!--日志文件名格式为-->
          <param name= "DatePattern" value= "yyyy-MM-dd&quot;.log&quot;"/>
          <!--日志根据日期滚动-->
          <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&quot;.log&quot;"/>
          <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>
          <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->
          <level value="ALL" />
          <appender-ref ref="RollingLogFileAppender" />
          <appender-ref ref="RollingFile" />
        </root>
      </log4net>
    </configuration>

      定义一个日志接口(主要是不同层级的写入方法),实现接口通过log4net组件来实现文件形式记录日志信息,使用组件读取配置文件、获取日志对象写入文件日志,还可以使用数据库方式实现接口写入日志。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TQF.Logger.Logger
    {
        /// <summary>
        /// 定义日志接口类
        /// </summary>
        public interface ILogger
        {
            /// <summary>
            /// 调试日志输出
            /// </summary>
            /// <param name="msg">输出内容</param>
            void Debug(string msg);
    
            /// <summary>
            /// 调试日志输出
            /// </summary>
            /// <param name="msg">输出内容</param>
            /// <param name="ex">输出异常</param>
            void Debug(string msg, Exception ex);
    
            /// <summary>
            /// 信息日志输出
            /// </summary>
            /// <param name="msg">输出内容</param>
            void Info(string msg);
    
            /// <summary>
            /// 信息日志输出
            /// </summary>
            /// <param name="msg">输出内容</param>
            /// <param name="ex">输出异常</param>
            void Info(string msg, Exception ex);
    
            /// <summary>
            /// 警告日志输出
            /// </summary>
            /// <param name="msg">输出内容</param>
            void Warn(string msg);
    /// <summary> /// 警告日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> void Warn(string msg, Exception ex); /// <summary> /// 错误日志输出 /// </summary> /// <param name="msg">输出内容</param> void Error(string msg); /// <summary> /// 错误日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> void Error(string msg, Exception ex); /// <summary> /// 致命日志输出 /// </summary> /// <param name="msg">输出内容</param> void Fatal(string msg); /// <summary> /// 致命日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> 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
    {
        /// <summary>
        /// 日志接口实现类
        /// </summary>
        public class Log4Helper : ILogger
        {
            /// <summary>
            /// 考虑使用线程安全的字典
            /// </summary>
            private Dictionary<string, ILog> LogDic = new Dictionary<string, ILog>();
            
            /// <summary>
            /// 定义锁对象
            /// </summary>
            private object _islock = new object();
    
            /// <summary>
            /// 定义文件名
            /// </summary>
            private string fileName = string.Empty;
    /// <summary> /// 使用构造函数日志调用初始化 /// </summary> /// <param name="fileSavePath">日志文件保存路径[若路径为空,则默认程序根目录Logger文件夹;]</param> /// <param name="fileName">日志文件名[若文件名为空,则默认文件名:Default]</param> 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) { } } /// <summary> /// 缓存日志对象(通过日志文件名缓存日志对象,减少日志对象的创建工作) /// </summary> /// <param name="name"></param> /// <returns></returns> 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"); } } /// <summary> /// 日志记录初始化 /// </summary> private void Init() { // 获取日志配置文件 var file = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log4net.config")); // 检测配置文件是否存在 if (file.Exists) { //加载配置文件 XmlConfigurator.Configure(file); } else { new Exception("找不到日志配置文件!"); } } /// <summary> /// 调试日志输出 /// </summary> /// <param name="msg">内容</param> public void Debug(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg); } /// <summary> /// 调试日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> public void Debug(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Debug(msg, ex); } /// <summary> /// 错误日志输出 /// </summary> /// <param name="msg">输出内容</param> public void Error(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg); } /// <summary> /// 错误日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> public void Error(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Error(msg, ex); } /// <summary> /// 致命日志输出 /// </summary> /// <param name="msg">输出内容</param> public void Fatal(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg); } /// <summary> /// 致命日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> public void Fatal(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Fatal(msg, ex); } /// <summary> /// 信息日志输出 /// </summary> /// <param name="msg">输出内容</param> public void Info(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg); } /// <summary> /// 信息日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> public void Info(string msg, Exception ex) { var log = GetLog(this.fileName); if (log == null) { return; } log.Info(msg, ex); } /// <summary> /// 警告日志输出 /// </summary> /// <param name="msg">输出内容</param> public void Warn(string msg) { var log = GetLog(this.fileName); if (log == null) { return; } log.Warn(msg); } /// <summary> /// 警告日志输出 /// </summary> /// <param name="msg">输出内容</param> /// <param name="ex">输出异常</param> 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
    {
        /// <summary>
        /// 配置文件如果不设置属性“复制到输出目录”不会在debug文件中存在
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                var log4Helper = new Log4Helper(string.Empty, "program");
                log4Helper.Info("info");
            }
        }
    }

      按照配置文件格式定义的文件夹、文件名称、文件内容格式,相对标准化,但是要特殊的记录日志信息比如不确定文件夹名称、不确定的文件名称等,就要开发者自定义,通过在写入日志的时候,传入文件夹和文件名称形式来存储,对于特殊的日志信息可以通过该方法达到特定要求,如下所示。

    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
    {
        /// <summary>
        /// log4net 自定义日志类
        /// </summary>
        public static class LogCustomHelper
        {
            /// <summary>
            /// 类型安全的字典对象
            /// </summary>
            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";
    
            /// <summary>
            /// 获取日志对象
            /// </summary>
            /// <param name="loggerName"></param>
            /// <param name="category"></param>
            /// <param name="additivity"></param>
            /// <returns></returns>
            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);
                });
            }
    
            /// <summary>
            /// 自定日志文件夹和文件路径
            /// </summary>
            /// <param name="category"></param>
            /// <param name="loggerName"></param>
            /// <returns></returns>
            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);
                }
            }
    
            /// <summary>
            /// 获取日志级别
            /// </summary>
            /// <param name="level"></param>
            /// <returns></returns>
            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;
            }
    
            /// <summary>
            /// 获取配置信息
            /// </summary>
            /// <param name="appenderName"></param>
            /// <param name="file"></param>
            /// <param name="maxSizeRollBackups"></param>
            /// <param name="appendToFile"></param>
            /// <param name="staticLogFileName"></param>
            /// <param name="maximumFileSize"></param>
            /// <param name="rollingMode"></param>
            /// <param name="datePattern"></param>
            /// <param name="layoutPattern"></param>
            /// <returns></returns>
            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;
            }
    
            /// <summary>
            /// 写入日志信息
            /// </summary>
            /// <param name="folderName">文件夹名称</param>
            /// <param name="fileName">文件名称</param>
            /// <param name="message">日志信息</param>
            public static void Info(string folderName, string fileName,string message)
            {
                ILog logger = GetCustomLogger(fileName, folderName);
                logger.Info(message);
            }
        }
    }

    三、总结

      在生产环境记录日志可以帮助用户定位问题,解决问题,对系统执行过程中可能出现异常错误进行捕获,对执行全过程进行回溯查看等;在存储方式上不仅可以使用文本文件方式保存,还可以使用数据库保存(关系型数据,非关系型数据库mongodb);在分布式系统中日志的聚集,整合,可视化,统一管理的考量与实践。

  • 相关阅读:
    选择排序——Java实现
    冒泡排序——Python实现
    基数排序——Java实现
    Sqoop迁移Hadoop与RDBMS间的数据
    Hive Join
    Hadoop 完全分布式部署(三节点)
    springboot自定义异常处理
    linux下安装redis
    windows 下tomcat重启脚本
    解决rabbin首次请求慢的问题
  • 原文地址:https://www.cnblogs.com/tuqunfu/p/15594455.html
Copyright © 2020-2023  润新知