C# 使用Log4net记录日志

1.Log4net.config详解

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
  </configSections>
  <log4net>
    <!-- ConversionPattern 解释
%m(message):输出的日志消息,如ILog.Debug(…)输出的一条消息
%n(new line):換行
%d(datetime):输出当前语句运行的时刻 
%r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数 
%t(thread id):当前语句所在的线程ID
%p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等
%c(class):当前日志对象的名称
%L:输出语句所在的行号
%F:输出语句所在的文件名
%-数字:表示该项的最小长度,如果不够,则用空格填充
-->

    <root>
      <!--控制级别,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
      <!--比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录-->
      <!--如果没有定义LEVEL的值,则缺省为DEBUG-->
      <level value="ALL"/>
      <appender-ref ref="AppLogAppender"/>
    </root>
    <!--定义输出到控制台命令行中-->
    <logger name="SysLogger">
      <level value="ALL"/>
      <appender-ref ref="TextAppender" />
    </logger>
    
    <logger name="AppLogger">
      <level value="ALL"/>
      <appender-ref ref="AppLogAppender" />
    </logger>
    
    <!--定义输出到控制台命令行中-->
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="RMFramework.Common.Logger.MyLayout">
        <param name="ConversionPattern" value="日志时间:%d %n日志级别:%-5p %n用 户 ID:%Property{UserID} %n用户姓名:%Property{UserName} %n日志信息:%Property{Message} %n异常信息:%exception %n%n" />
      </layout>
    </appender>

    <!--定义输出到windows事件中-->
    <appender name="WindowsAppender" type="log4net.Appender.EventLogAppender">
      <layout type="RMFramework.Common.Logger.MyLayout">
        <param name="ConversionPattern" value="日志时间:%d %n日志级别:%-5p %n用 户 ID:%Property{UserID} %n用户姓名:%Property{UserName} %n日志信息:%Property{Message} %n异常信息:%exception %n%n" />
      </layout>
    </appender>

    <!--定义输出到文件中 用于记录系统级-->
    <appender name="TextAppender" type="log4net.Appender.RollingFileAppender">
      <param name="File" value="Logs\SysLog\" />
      <param name="AppendToFile" value="true" />
      <param name="MaxFileSize" value="10240" />
      <param name="MaxSizeRollBackups" value="100" />
      <param name="StaticLogFileName" value="false" />
      <param name="DatePattern" value="yyyyMMdd&quot;.log&quot;" />
      <param name="RollingStyle" value="Date" />
      <layout type="RMFramework.Common.Logger.MyLayout">
        <param name="ConversionPattern" value="日志时间:%d %n日志级别:%-5p %n用 户 ID:%Property{UserID} %n用户姓名:%Property{UserName} %n日志信息:%Property{Message} %n异常信息:%exception %n%n" />
      </layout>
    </appender>
    <!--定义输出文本,用于记录业务级-->
    <appender name="AppLogAppender" type="log4net.Appender.RollingFileAppender">
      <file value="Logs/AppLog/" />
      <appendToFile value="true" />
      <rollingStyle value="Date" />
      <staticLogFileName value="false" />
      <datePattern value="yyyyMMdd&quot;.log&quot;" />
      <layout type="log4net.Layout.PatternLayout">
        <!--输出格式-->
        <conversionPattern value="%date %-5level %message%newline" />
      </layout>
      <!--<filter type="log4net.Filter.LoggerMatchFilter">
        <loggerToMatch value="SystemLog" />
      </filter>-->
    </appender>
    
    <!--定义输出到数据库-->
    <appender name="DataBaseAppender" type="log4net.Appender.AdoNetAppender">
      <!--日志缓存写入条数-->
      <bufferSize value="1" />
      <!--日志数据库连接串-->
      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <connectionString value="data source=.SQL2008;initial catalog=Demo;integrated security=false;persist security info=True;User ID=sa;Password=1qaz" />
      <!--日志数据库脚本-->
      <commandText value="INSERT INTO LogInfo ([LogDate],[LogLevel],[UserId],[UserName],[Message],[Exception]) VALUES (@LogDate, @LogLevel,@UserId,@UserName, @Message, @Exception)" />
      <!--日志时间LogDate -->
      <parameter>
        <parameterName value="@LogDate" />
        <dbType value="String" />
        <size value="30" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%date{yyyy-MM-dd HH:mm:ss}" />
        </layout>
      </parameter>
      <!--日志类型LogLevel -->
      <parameter>
        <parameterName value="@LogLevel" />
        <dbType value="String" />
        <size value="10" />
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%level" />
        </layout>
      </parameter>
      <!--自定义UserId -->
      <parameter>
        <parameterName value="@UserId" />
        <dbType value="String" />
        <size value="20" />
        <layout type="RMFramework.Common.Logger.MyLayout">
          <conversionPattern value="%Property{UserID}" />
        </layout>
      </parameter>
      <!--自定义UserName -->
      <parameter>
        <parameterName value="@UserName" />
        <dbType value="String" />
        <size value="50" />
        <layout type="RMFramework.Common.Logger.MyLayout">
          <conversionPattern value="%Property{UserName}" />
        </layout>
      </parameter>
      <!--自定义Message -->
      <parameter>
        <parameterName value="@Message" />
        <dbType value="String" />
        <size value="200" />
        <layout type="RMFramework.Common.Logger.MyLayout">
          <conversionPattern value="%Property{Message}" />
        </layout>
      </parameter>
      <!--异常信息Exception -->
      <parameter>
        <parameterName value="@Exception" />
        <dbType value="String" />
        <size value="4000" />
        <layout type="log4net.Layout.ExceptionLayout" />
      </parameter>
    </appender>
  </log4net>
</configuration>
View Code

 2.使用前准备工作

  使用nuget引入Log4net; 

  在引入类库中的AssemblyInfo.cs加入

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]

3.调用方法

  3.1 原始调用

public static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("AppLogger");

  实例化ILog后,即可进行调用。LogManager.GetLogger传入参数与log4net.config内的Logger要匹配。

  当只有一个配置时,也可以使用如下代码。若多个logger配置时,仅读取第一个输出配置。

LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  完整代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;

namespace RMFramework.Common.Logger
{
    public class Log4System
    {
        public static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("AppLogger");

        /// <summary>
        /// 业务日志记录
        /// </summary>
        /// <param name="info"></param>
        public void WriteLog(string info)
        {
            if (loginfo.IsInfoEnabled)
            {
                loginfo.Info(info);
            }
        }

        /// <summary>
        /// 报错日志记录
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ex"></param>
        public void WriteLog(string info, Exception ex)
        {
            if (loginfo.IsErrorEnabled)
            {
                loginfo.Error(info, ex);
            }
        }

        /// <summary>
        /// 调试日志记录
        /// </summary>
        /// <param name="info"></param>
        /// <param name="values"></param>
        public void WriteLog(string info, params object[] values)
        {
            if (loginfo.IsDebugEnabled)
            {
                loginfo.Debug(string.Format(info, values));
            }

        }
    }
}
View Code

   3.2 自定义输出

  自定义输出对象,映射到配置文件内的layout标签

  支持输出到控制台,支持输出到文本,支持输出到数据库。

using log4net;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using log4net.Layout.Pattern;
using System;
using System.IO;
using System.Reflection;

namespace RMFramework.Common.Logger
{
    public class LogHelper
    {
        /// <summary>
        /// LoggerName
        /// </summary>
        public static string LoggerName = string.Empty;
        /// <summary>
        /// 用户ID
        /// </summary>
        public static string UserID = string.Empty;
        /// <summary>
        /// 用户名称
        /// </summary>
        public static string UserName = string.Empty;

        private static ILog iLog;
        private static LogEntity logEntity;

        /// <summary>
        /// 接口
        /// </summary>
        private static ILog log
        {
            get
            {
                var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"));
                if (!configFile.Exists)
                {
                    throw new Exception("未配置log4net配置文件!");
                }

                // 设置日志配置文件路径
                XmlConfigurator.Configure(configFile);

                //string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"Log4Net.config";//AppDomain.CurrentDomain.BaseDirectory
                //XmlConfigurator.Configure(new FileInfo(path));

                if (iLog == null)
                {
                    iLog = LogManager.GetLogger(LoggerName);
                }
                else
                {
                    if (iLog.Logger.Name != LoggerName)
                    {
                        iLog = LogManager.GetLogger(LoggerName);
                    }
                }

                return iLog;
            }
        }

        /// <summary>
        /// 构造消息实体
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private static LogEntity BuildMessageMode(string message)
        {
            if (logEntity == null)
            {
                logEntity = new LogEntity();
                logEntity.UserID = UserID;
                logEntity.UserName = UserName;
                logEntity.Message = message;
            }
            else
                logEntity.Message = message;

            return logEntity;
        }

        /// <summary>
        /// 调试
        /// </summary>
        /// <param name="message">消息</param>
        public static void Debug(string message)
        {
            if (log.IsDebugEnabled)
                log.Debug(BuildMessageMode(message));
        }

        /// <summary>
        /// 调试
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exception">异常</param>
        public static void Debug(string message, Exception ex)
        {
            if (log.IsDebugEnabled)
                log.Debug(BuildMessageMode(message), ex);
        }

        /// <summary>
        /// 信息
        /// </summary>
        /// <param name="message">消息</param>
        public static void Info(string message)
        {
            if (log.IsInfoEnabled)
                log.Info(BuildMessageMode(message));
        }

        /// <summary>
        /// 信息
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exception">异常</param>
        public static void Info(string message, Exception ex)
        {
            if (log.IsInfoEnabled)
                log.Info(BuildMessageMode(message), ex);
        }

        /// <summary>
        /// 一般错误
        /// </summary>
        /// <param name="message">消息</param>
        public static void Error(string message)
        {
            if (log.IsErrorEnabled)
                log.Error(BuildMessageMode(message));
        }

        /// <summary>
        /// 一般错误
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exception">异常</param>
        public static void Error(string message, Exception exception)
        {
            if (log.IsErrorEnabled)
                log.Error(BuildMessageMode(message), exception);
        }

        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="message">消息</param>
        public static void Warn(string message)
        {
            if (log.IsWarnEnabled)
                log.Warn(BuildMessageMode(message));
        }

        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exception">异常</param>
        public static void Warn(string message, Exception ex)
        {
            if (log.IsWarnEnabled)
                log.Warn(BuildMessageMode(message), ex);
        }

        /// <summary>
        /// 严重
        /// </summary>
        /// <param name="message">消息</param>
        public static void Fatal(string message)
        {
            if (log.IsFatalEnabled)
                log.Fatal(BuildMessageMode(message));
        }

        /// <summary>
        /// 严重
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exception">异常</param>
        public static void Fatal(string message, Exception ex)
        {
            if (log.IsFatalEnabled)
                log.Fatal(BuildMessageMode(message), ex);
        }
    }

    public class MyLayout : PatternLayout
    {
        public MyLayout()
        {
            this.AddConverter("Property", typeof(MyPatternConverter));
        }
    }

    public class LogEntity
    {
        public string UserID { get; set; }

        public string UserName { get; set; }

        public string Message { get; set; }

        public Exception Exception { get; set; }

    }

    public class MyPatternConverter : PatternLayoutConverter
    {
        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
        {
            if (Option != null)
            {
                WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
            }
            else
            {
                WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
            }
        }

        /// <summary>
        /// 通过反射获取传入的日志对象的某个属性的值
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private object LookupProperty(string property, log4net.Core.LoggingEvent loggingEvent)
        {
            object propertyValue = string.Empty;

            PropertyInfo propertyInfo = loggingEvent.MessageObject.GetType().GetProperty(property);
            if (propertyInfo != null)
                propertyValue = propertyInfo.GetValue(loggingEvent.MessageObject, null);

            return propertyValue;
        }
    }
}
View Code

   3.3 队列输出

   将日志添加到队列,队列输出到磁盘,效率高。

using log4net;
using log4net.Config;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;

namespace RMFramework.Common.Logger
{
    public sealed class FlashLogger
    {
        /// <summary>
        /// 记录消息Queue
        /// </summary>
        private readonly ConcurrentQueue<FlashLogMessage> _que;

        /// <summary>
        /// 信号
        /// </summary>
        private readonly ManualResetEvent _mre;

        /// <summary>
        /// 日志
        /// </summary>
        private readonly ILog _log;

        /// <summary>
        /// 日志
        /// </summary>
        private static FlashLogger _flashLog = new FlashLogger();


        private FlashLogger()
        {
            var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"));
            if (!configFile.Exists)
            {
                throw new Exception("未配置log4net配置文件!");
            }

            // 设置日志配置文件路径
            XmlConfigurator.Configure(configFile);

            _que = new ConcurrentQueue<FlashLogMessage>();
            _mre = new ManualResetEvent(false);
            _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        }

        /// <summary>
        /// 实现单例
        /// </summary>
        /// <returns></returns>
        public static FlashLogger Instance()
        {
            return _flashLog;
        }

        /// <summary>
        /// 另一个线程记录日志,只在程序初始化时调用一次
        /// </summary>
        public void Register()
        {
            Thread t = new Thread(new ThreadStart(WriteLog));
            t.IsBackground = false;
            t.Start();
        }

        /// <summary>
        /// 从队列中写日志至磁盘
        /// </summary>
        private void WriteLog()
        {
            while (true)
            {
                // 等待信号通知
                _mre.WaitOne();

                FlashLogMessage msg;
                // 判断是否有内容需要如磁盘 从列队中获取内容,并删除列队中的内容
                while (_que.Count > 0 && _que.TryDequeue(out msg))
                {
                    // 判断日志等级,然后写日志
                    switch (msg.Level)
                    {
                        case FlashLogLevel.Debug:
                            _log.Debug(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Info:
                            _log.Info(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Error:
                            _log.Error(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Warn:
                            _log.Warn(msg.Message, msg.Exception);
                            break;
                        case FlashLogLevel.Fatal:
                            _log.Fatal(msg.Message, msg.Exception);
                            break;
                    }
                }

                // 重新设置信号
                _mre.Reset();
                Thread.Sleep(1);
            }
        }


        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="message">日志文本</param>
        /// <param name="level">等级</param>
        /// <param name="ex">Exception</param>
        public void EnqueueMessage(string message, FlashLogLevel level, Exception ex = null)
        {
            if ((level == FlashLogLevel.Debug && _log.IsDebugEnabled)
             || (level == FlashLogLevel.Error && _log.IsErrorEnabled)
             || (level == FlashLogLevel.Fatal && _log.IsFatalEnabled)
             || (level == FlashLogLevel.Info && _log.IsInfoEnabled)
             || (level == FlashLogLevel.Warn && _log.IsWarnEnabled))
            {
                _que.Enqueue(new FlashLogMessage
                {
                    Message = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff") + "]
" + message,
                    Level = level,
                    Exception = ex
                });

                // 通知线程往磁盘中写日志
                _mre.Set();
            }
        }

        public static void Debug(string msg, Exception ex = null)
        {
            Instance().EnqueueMessage(msg, FlashLogLevel.Debug, ex);
        }

        public static void Error(string msg, Exception ex = null)
        {
            Instance().EnqueueMessage(msg, FlashLogLevel.Error, ex);
        }

        public static void Fatal(string msg, Exception ex = null)
        {
            Instance().EnqueueMessage(msg, FlashLogLevel.Fatal, ex);
        }

        public static void Info(string msg, Exception ex = null)
        {
            Instance().EnqueueMessage(msg, FlashLogLevel.Info, ex);
        }

        public static void Warn(string msg, Exception ex = null)
        {
            Instance().EnqueueMessage(msg, FlashLogLevel.Warn, ex);
        }

    }

    /// <summary>
    /// 日志等级
    /// </summary>
    public enum FlashLogLevel
    {
        Debug,
        Info,
        Error,
        Warn,
        Fatal
    }


    /// <summary>
    /// 日志内容
    /// </summary>
    public class FlashLogMessage
    {
        public string Message { get; set; }
        public FlashLogLevel Level { get; set; }
        public Exception Exception { get; set; }

    }

}
View Code

 4.需要注意的地方

  • 实例化后的日志是被独占的,日志类库应集中管理,否则会导致输出为空
原文地址:https://www.cnblogs.com/xuexian/p/10906261.html