专栏首页明丰随笔log4net使用解析

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,并添加配置表Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\MY-AWESOME-APP,设置里面Key-value为EventMessageFile: C:\Windows\Microsoft.NET\Framework\v4.0.30319\EventLogMessages.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到底如何使用,以及它强大的配置能力。

本文分享自微信公众号 - 明丰随笔(liumingfengwx2)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python apscheduler 打印日志

    logging.basicConfig() logging.getLogger('apscheduler').setLevel(logging.DEBUG)

    用户5760343
  • 聊聊dubbo的LogbackContainer

    dubbo-2.7.2/dubbo-container/dubbo-container-logback/src/main/java/org/apache/dub...

    codecraft
  • vscode源码分析【四】程序启动的逻辑,最初创建的服务

    在第一节中提到的startup函数里(src\vs\code\electron-main\main.ts) 有一个createServices的调用:

    liulun
  • Python 的 2018 年终总结:发展状况回顾

    这个月早些时候我在加拿大PyCon的演讲让我兴奋不已,在会议期间,我与许多聪明人交谈,似乎每个人都在谈论着同样的希望和痛苦。 这是一个试图将社区中微弱的耳语合...

    昱良
  • 【Rust日报】 2019-06-01:知乎开源了Rust实现的搜索引擎 rucene

    据@Chaos了解,目前开源的部分只包括 lucene library 部分,搜索引擎的部分太多业务内容而且跟内部的一个分布式框架有强绑定就没开源,等后续弄好了...

    MikeLoveRust
  • Linux配置日志服务器

      本文主要介绍的是关于Linux配置日志服务器的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

    习惯说一说
  • Hyperledger Fabric Node.js开发中如何使用日志

    Hyperledger Fabric Node.js开发中如何使用日志?本教程就来演示下如何使用hyperledgefabric node.js客户端日志记录功...

    笔阁
  • 聊聊dubbo的LogbackContainer

    dubbo-2.7.2/dubbo-container/dubbo-container-logback/src/main/java/org/apache/dub...

    codecraft
  • Lumen 实现 SQL 监听

    之前 Lumen 框架从 5.6 升级到 5.7。发现 laravel-sql-logger 包不能正常纪录日志了。进行排查,发现是 Lumen 框架没有对 D...

    魔王卷子
  • 基于Egg框架的日志链路追踪实践

    实现全链路日志追踪,便于日志监控、问题排查、接口响应耗时数据统计等,首先 API 接口服务接收到调用方请求,根据调用方传的 traceId,在该次调用链中处理业...

    五月君

扫码关注云+社区

领取腾讯云代金券