log4net使用解析

时间:2022-06-25
本文章向大家介绍log4net使用解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

这边篇文章的目的是训练我们在项目中使用log4net,为了更加全面的使用log4net的功能,我们假设在app里面定义:

一个repository: 作为log4net的顶级容器。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- 定义log4net的section,作为log4net的顶级容器,对于log4net里面的repository -->
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>

  <!-- 默认的Repository -->
  <log4net>
    ...
  </log4net>
</configuration>

两个logger:一个默认的logger,即root,给全局使用。一个特殊命名的logger,可以只给特殊的类型使用。

<!-- 默认的Repository -->
<log4net>
  <!-- 默认的Root Logger -->
  <root>
    <!-- level:All,Debug,Info,Warn,Error,Fatal,Off -->
    <!-- 如果不需要记录日志设置为Off -->
    <!-- 如果要记录Error和Fatal设置为Error -->
    <!-- 如果要记录Warn,Error和Fatal设置为Warn,以此类推 -->
    <level value="All" />
    <!-- 引用Appender -->
    <appender-ref ref="RollingFileAppender" />
  </root>
  <!-- 定义新Logger,自动继承Root Logger,并多两个Appender -->
  <logger name="Log4NetLib.MyLib">
    <appender-ref ref="ConsoleAppender" />
    <appender-ref ref="EventLogAppender" />
  </logger>
  ...
</log4net>

多个logger之间的继承关系?

logger使用继承体系,继承规则类似于.NET中的名字空间。如果有两个logger,分别命名为a.b和a.b.c,那么我们说a.b是a.b.c的祖先,a.b.c是a.b的孩子。每一个logger都继承了祖先的属性。root是默认的logger,所有其他的logger都会继承它的属性。

在<root>标签里,可以定义level级别值。如果没有定义level的值,默认值为DEBUG。可以通过<appender-ref>标签定义日志对象使用的Appender对象。<appender-ref>引用了在其他地方定义的Appender对象。在一个logger对象中的设置会覆盖根日志的设置。而对Appender属性来说,子日志对象则会继承父日志对象的Appender列表。可以显式设置logger的additivity属性为false,这样就不会继承祖先的属性了。logger的additivity属性的默认值为true。

一个自定义的ObjectRenderer类型,可以控制专有类型输出。

ObjectRenderer默认输出是如何实现的?

1. string类型的数据。原样输出。

2. IEnumerable类型的数据,输出{a, b, c}。

3. Array类型的数据,输出int[] {1, 2, 3}。

4. DictionaryEntry类型的数据,输出key=value。

5. 其他输出为Object.ToString()的返回值。

如果还不能满足需求,自定义一个CustomExcpetionRenderer类型,并配置:

<log4net>
  ...
  <renderer renderingClass="Log4NetLib.CustomExcpetionRenderer" renderedClass="Log4NetLib.MyException" />
</log4net>

CustomExcpetionRenderer类型实现IObjectRenderer接口,代码为:

public class CustomExcpetionRenderer : IObjectRenderer
{
  public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
  {
    MyException myException = obj as MyException;
    writer.WriteLine(string.Format("Transaction ID  :  {0}", myException.TransID));
    writer.WriteLine(string.Format("Username        :  {0}", myException.Username));

    Exception exception = obj as Exception;
    while (exception != null)
    {
      WriteException(exception, writer);
      exception = exception.InnerException;
    }
  }
  private void WriteException(Exception ex, TextWriter writer)
  {
    writer.WriteLine(string.Format("Type: {0}", ex.GetType().FullName));
    writer.WriteLine(string.Format("Message: {0}", ex.Message));
    writer.WriteLine(string.Format("Source: {0}", ex.Source));
    writer.WriteLine(string.Format("TargetSite: {0}", ex.TargetSite));
    WriteExceptionData(ex, writer);
    writer.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
  }
  private void WriteExceptionData(Exception ex, TextWriter writer)
  {
    foreach (DictionaryEntry entry in ex.Data)
    {
      writer.WriteLine(string.Format("{0}: {1}", entry.Key, entry.Value));
    }
  }
}

MyException的代码如下:

public class MyException : ApplicationException
{
  public string TransID;
  public string Username;

  public MyException(string message) : base(message)
  {
  }
}

这样在代码中打印MyException对象的时候,会输出自定义的格式。

三个Appenders: RollingFileAppender、EventLogAppender和ConsoleAppender。

RollingFileAppender配置为:

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <!-- 文件路径 -->
  <file value="logs\" />
  <datePattern value="yyyy\yyyyMM\yyyyMMdd'.log'" />
  <appendToFile value="true" />
  <rollingStyle value="Composite" />
  <staticLogFileName value="false"/>
  <maxSizeRollBackups value="100" />
  <maximumFileSize value="10MB" />
  <!-- 定义layout -->
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%property{host} %appdomain %date %level %logger %user %message %stacktracedetail%newline" />
  </layout>
  <!-- 定义filter -->
  <filter type="log4net.Filter.LevelRangeFilter">
    <param name="LevelMin" value="Info"/>
    <param name="LevelMax" value="Error"/>
  </filter>
</appender>

ConsoleAppender配置为:

<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <ConversionPattern value="%property{host} %appdomain %date %level %logger %user %message %stacktracedetail%newline" />
  </layout>
  <!-- 定义filter -->
  <filter type="log4net.Filter.LevelRangeFilter">
    <param name="LevelMin" value="Info"/>
    <param name="LevelMax" value="Error"/>
  </filter>
</appender>

EventLogAppender配置为:

<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
  <ApplicationName value="MY-AWESOME-APP" />
  <layout type="log4net.Layout.PatternLayout">
    <ConversionPattern value="%property{host} %appdomain %date %level %logger %user %message %stacktracedetail%newline" />
  </layout>
  <filter type="log4net.Filter.LevelRangeFilter">
    <param name="LevelMin" value="Error"/>
    <param name="LevelMax" value="Error"/>
  </filter>
</appender>

注意配置EventLogAppender的时候,需要定义ApplicationName为MY-AWESOME-APP,并添加配置表ComputerHKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesEventLogApplicationMY-AWESOME-APP,设置里面Key-value为EventMessageFile: C:WindowsMicrosoft.NETFrameworkv4.0.30319EventLogMessages.dll 不然会无法记录日志。

我们在appender中选择LevelRangeFilter类型来定义Filter。

Appender不设置Filter,默认filter是什么?

默认会记录所有的日志。

其实Appender可以设置多个filter。

1. 设置LevelMatchFilter和StringMatchFilter两个Filter

<filter type="log4net.Filter.LevelMatchFilter">
   <param name="LevelToMatch" value="Debug" />       
</filter>
<filter type="log4net.Filter.StringMatchFilter">
   <param name="StringToMatch" value="Debug" />       
</filter>

这样设置两个Filter,到底记不记日志,取决于两个Filter的并集,有一个条件匹配就会记录。

2. 为多个Filter设置AcceptOnMatch属性

<filter type="log4net.Filter.LevelMatchFilter">
   <param name="LevelToMatch" value="Debug" />   
   <AcceptOnMatch value="false"></AcceptOnMatch>    
</filter>
<filter type="log4net.Filter.StringMatchFilter">
   <param name="StringToMatch" value="Debug" />       
</filter>

如果这样写的话,即使第一个Filter符合了条件,也会询问第二个Filter是否符合条件。

选择PatternLayout排版进行输出。PatternLayout因为实在太好使用,基本上能完成我们的所有需求。

完成这些配置文件之后,我们需要在项目中写代码对log4net进行调用,首先加载配置文件:

log4net.Config.XmlConfigurator.Configure();

把机器名称保存在GlobalContext.Properties中:

log4net.GlobalContext.Properties["host"] = Environment.MachineName;

获取一个ILog:

var _log = LogFactory.GetLogger(typeof(Program));//root
var _log = LogFactory.GetLogger("Log4NetLib.MyLib");//logger name="Log4NetLib.MyLib"

public static class LogFactory
{
  public static ILog GetLogger(Type type)
  {
    return LogManager.GetLogger(type);
  }

  public static ILog GetLogger(string str)
  {
    return LogManager.GetLogger(str);
  }
}

写日志:

_log.Info("MyException Info");
//会输出自定义的格式。
_log.Info(ex);
//会输出自定义的格式。
_log.Error("MyLib MyException", ex);

通过这篇文章我们很好的学习了log4net到底如何使用,以及它强大的配置能力。