本文是我在实际项目中应用log4net之后整理的,主要包括三部分:

  1. log4net基础配置
  2. log4net自定义配置
  3. log4net调试

下面我通过实际的例子(web项目),来说明这三部分。

log4net基础配置

  1. 打开vs2013,创建“ASP.NET Web应用程序”,命名为“Log4netDemo”;
  2. 通过NuGet安装log4net,如下图,在搜索框里输入“log4net”,在结果中安装第一个;
    log4net的实际应用和调试-程序旅途
  3. 在项目中新建log4net.config配置文件;
  4. 打开log4net.config文件,添加以下配置:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
      <log4net>
        <root>
          <level value="ALL" />
          <appender-ref ref="AdoNetAppender" />
        </root>
        <logger name="ErrorLogger">
          <level value="Error"/>
        </logger>
        <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
          <!--在实际项目建议设置为100-->
          <bufferSize value="1" />
          <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <!--<connectionString value="data source=.;initial catalog=Log4netDemoDb;integrated security=false;persist security info=True;User ID=sa;Password=sasa" />-->
          <connectionString value="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Log4netDemoDb.mdf;Integrated Security=True" />
          <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
          <parameter>
            <parameterName value="@log_date" />
            <dbType value="DateTime" />
            <layout type="log4net.Layout.RawTimeStampLayout" />
          </parameter>
          <parameter>
            <parameterName value="@thread" />
            <dbType value="String" />
            <size value="255" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%thread" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@log_level" />
            <dbType value="String" />
            <size value="50" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%level" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@logger" />
            <dbType value="String" />
            <size value="255" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%logger" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@message" />
            <dbType value="String" />
            <size value="4000" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%message" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@exception" />
            <dbType value="String" />
            <size value="2000" />
            <layout type="log4net.Layout.ExceptionLayout" />
          </parameter>
        </appender>
      </log4net>
    </configuration>
    
  5. 在项目中右击,选择“添加”->“SQL Server 数据库”,命名为“Log4netDemoDb”
  6. 执行以下语句,创建Log表
    CREATE TABLE [dbo].[Log] (
        [Id] [int] IDENTITY (1, 1) NOT NULL,
        [Date] [datetime] NOT NULL,
        [Thread] [nvarchar] (255) NOT NULL,
        [Level] [nvarchar] (50) NOT NULL,
        [Logger] [nvarchar] (255) NOT NULL,
        [Message] [nvarchar] (4000) NOT NULL,
        [Exception] [nvarchar] (2000) NULL
    )
    
  7. 在Global.asax的Application_Start()中添加以下代码,关联log4net.config配置文件
    log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/log4net.config")));
    
  8. 在项目中添加一个ILogger接口,代码如下:
        public interface ILogger
        {
            void Write(object message);
        }
    
  9. 应用适配器模式,添加一个适配器类Log4netErrorLogger,代码如下:
        public class Log4netErrorLogger : ILogger
        {
            private log4net.ILog _log;
    
            public Log4netErrorLogger()
            {
                _log = log4net.LogManager.GetLogger("ErrorLogger");
            }
    
            public void Write(object message)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message);
                }
            }
        }
    
  10. 在Global.asax中实例化一个Log4netErrorLogger变量,然后在Application_Error()中添加以下代码,记录错误的日志信息:
            ILogger _logger = new Log4netErrorLogger();
    
            protected void Application_Error()
            {
                var errors = HttpContext.Current.AllErrors;
    
                foreach (var exception in errors)
                {
                    _logger.Write(exception.Message);
                }
            }
    
  11. 下面添加一个TestController,对应的Action Index不添加View就会出现错误,再到Log表里看一下是否已经成功记录了错误信息吧
    log4net的实际应用和调试-程序旅途

上面基础配置部分还是很简单的,但是记录的信息也有限。e.g.,我的程序里有公司信息,有用户信息,当程序出现Bug的时候,我需要记录下是哪个公司的哪个用户执行什么操作的时候出现了什么问题,当然这还需要记录StackTrace信息。还有些情况下,需要记录客户端用户使用的是什么系统以及浏览器。

 log4net自定义配置

  1. 首先,添加一个自定义类LogMessage,这个类包含要记录的自定义信息
        public class LogMessage
        {
            public LogMessage(string message, string platform, string browser, string company, string user)
            {
                Message = message;
                Platform = platform;
                Browser = browser;
                Company= company;
                User = user;
            }
    
            public string Message { get; set; }
    
            public string Platform { get; set; }
    
            public string Browser { get; set; }
    
            public string Company{ get; set; }
    
            public string User { get; set; }
    
            public override string ToString()
            {
                return Message;
            }
        }
    
  2. 接着添加两个类,它们的作用是将我们自定义的属性转换成功log4net能够识别的属性
        public class CustomPatternLayout:PatternLayout
        {
            public CustomPatternLayout()
            {
                AddConverter("property", typeof(CustomPatternLayoutConverter));
            }
        }
    
        public class CustomPatternLayoutConverter : PatternLayoutConverter
        {
            protected override void Convert(System.IO.TextWriter writer, log4net.Core.LoggingEvent loggingEvent)
            {
                if (Option != null)
                {
                    // Write the value for the specified key
                    WriteObject(writer, loggingEvent.Repository, LookupProperty(Option, loggingEvent));
                }
                else
                {
                    // Write all the key value pairs
                    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;
            }
        }
    
  3. 修改log4net.config的配置,下面代码是修改的部分:
          <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception],[Platform],[Browser],[Company],[User]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception,@platform,@browser,@company,@user)" />
    
          <parameter>
            <parameterName value="@platform"/>
            <dbType value="String"/>
            <size value="50"/>
            <layout type="Log4netDemo.CustomPatternLayout, Log4netDemo">
              <param name="ConversionPattern" value="%property{Platform}"/>
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@browser"/>
            <dbType value="String"/>
            <size value="50"/>
            <layout type="Log4netDemo.CustomPatternLayout, Log4netDemo">
              <param name="ConversionPattern" value="%property{Browser}"/>
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@company"/>
            <dbType value="String"/>
            <size value="50"/>
            <layout type="Log4netDemo.CustomPatternLayout, Log4netDemo">
              <param name="ConversionPattern" value="%property{Company}"/>
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@user"/>
            <dbType value="String"/>
            <size value="50"/>
            <layout type="Log4netDemo.CustomPatternLayout, Log4netDemo">
              <param name="ConversionPattern" value="%property{User}"/>
            </layout>
          </parameter>
    
  4. 在Log表中,加入新增的4列,如下图所示:
    log4net的实际应用和调试-程序旅途
  5. 修改Application_Error代码,如下所示:
            protected void Application_Error()
            {
                var request = HttpContext.Current.Request;
                var platform = request.Browser.Platform;
                var browser = request.Browser.Browser;
                var company = "Company";
                var user = "User";
    
                var errors = HttpContext.Current.AllErrors;
                foreach (var exception in errors)
                {
                    //_logger.Write(exception.Message);
                    _logger.Write(new LogMessage(exception.Message, platform, browser, company, user));
                }
            }
    
  6. 再次运行程序,看看表里的数据:
    log4net的实际应用和调试-程序旅途

log4net调试

往往我们都会遇到记录不了的问题,遇到这种问题应该怎么办呢?

假如我将log4net.config里面配置连接数据库的字符串修改了,将数据库的名字改成一个不存在的数据库,也许是因为不仔细,怎么都没有发现这个改变,这样就导致日志信息怎么都记录不到数据库,因为我们连得就是一个不存在的数据库。下面看看应该怎么快速的发现问题出现在哪里。

log4net提供了内部的调试,关键是如何启动它,下面是步骤:

  1. 打开web.config文件,在appSettings中加入以下代码:
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <appSettings>
            <add key="log4net.Internal.Debug" value="true"/>
        </appSettings>
    </configuration>
    
  2. 因为log4net内部debug信息写入到System.Diagnostics.Trace系统,所以需要加入以下配置将小心写入到本地文件:
      <system.diagnostics>
        <trace autoflush="true">
          <listeners>
            <add
                name="textWriterTraceListener"
                type="System.Diagnostics.TextWriterTraceListener"
                initializeData="log4net.txt" />
          </listeners>
        </trace>
      </system.diagnostics>
    

再次运行程序,会在应用程序的根目录下生成一个log4net.txt,打开可以找到如下图所示的错误信息:

log4net的实际应用和调试-程序旅途

这样我们很快就知道是哪里出现了错误从而导致不能将日志信息记录到数据库了。

总结

本文只展示了将日志记录到数据库的情况,其他配置都是类似的,网上也有很多的教程,我在这里只是将应用log4net的步骤记录了下来,一是作为学习的总结,二是希望能够帮到其他需要的朋友。

我学习log4net的过程中参考了网上的很多资源,下面推荐以下对我帮助比较大的:

  1. 官方的配置例子:http://logging.apache.org/log4net/release/config-examples.html
  2. 官方常见问题:http://logging.apache.org/log4net/release/faq.html
  3. 比较详细的中文文档:http://www.cnblogs.com/longshizhong/archive/2009/11/25/1610452.html
  4. 自定义配置:http://acually.blog.51cto.com/1060340/376146

本文所有的代码托管在github上,访问地址:https://github.com/nmtreeblog/Log4netDemo