则该包中的类由服务器的ClassLoader加载,本文介绍两种常用的日记记录工具

在软件开采、测试以及生产条件中,日志记录是万分关键的,接纳合适的日志记录工具、设计合理的日记输出格式,有利于支持开荒职员分明程序的举办流程以及错误的全速牢固和革新。日志记录作为软件开荒周期中的主要组成都部队分,本文介绍三种常用的日志记录工具: 

Commons
Logging+Log4J一直是Java日记的优异组合,以至于多数服务器都利用了接近的配置,像WebSphere、在此以前的汤姆cat都选取CommonsLogging作为日志输出框架,而听说JBoss则一向CommonsLogging和Log四J一同选用了(这一个测度是为着消除CommonsLogging中平日在那类服务器上蒙受的ClassLoader难题)。可是Log4J的付出集团对CommonsLogging貌似不称心(能够从Log4J
马努al中看出壹些头脑),由此Log四J团队支付了本人的日志门面框架SLF4J(Simple
Logging Façade For
Java),貌似他们对友好付出的Log4J的特性也不满足,然后又弄出了个LogBack,关键实行语句的品质要比Log四J快十倍以上(官方网站资料,笔者本人还未有仔细看过LogBack的代码,更未曾测试过,不晓得具体性质能增加多少),那是后话,等过几年看LogBack代码后再精心商量。

  • log4net
  • NLog
  • log4j
  • SLF4J

以自个儿个人知道,SLF四J的产出是为了消除Commons Logging存在的七个难点:

内部,log肆net 和 NLog 紧要用以 C#/.net 平台,log4j 和 SLF④J 首要用以
Java 开拓。

一.    Commons Logging存在的ClassLoader难点,即当服务器本人引进CommonsLogging时,若是在服务器中载入CommonsLogging包,则该包中的类由服务器的ClassLoader加载,从而在加载Log4J中的Logger类时会出现ClassNotFoundException,因为服务器中的ClassLoader没办法找到Web
App下的jar包。对于父ClassLoader优先的类加运载飞机制以来,方今的1个解决方案是利用commons-logging-api-一.一.一.jar包,该包的Log完毕类只含有Jdk1四Logger、SimpleLog、NoOpLog七个类,因此在加载那多少个类时会使用Web
App中的CommonsLogging包,从而消除ClassNotFoundException的题目,但是那种方法对这3个实现Child
ClassLoader
First的服务器来说,由WebClassLoader父ClassLoader加载的类在运用日志时会存在难点,因为它们的Log接口由加载自己类的ClassLoader加载,而Log四JLogger类却由WebClassLoader加载。具体有关CommonsLogging中设有的难题作者会在别的①篇小说中详细表明。

log4net

参考:Apache log4net
官网

无偿的开源日志记录组件,log四net 库是依照 Apache log肆j 框架在 Microsoft
.NET 平台的落成。

有关 log肆net 的着力介绍,能够参见:log4net Tutorial –
CodeProject

特点

  •  Distinct Goals: speed and flexibility
  •  各种路线输出:日志音讯输出到:[1]. 控制台;[2]. 文件;[3].
    数据库;
  •  协理三种 .Net 框架,援救各个日志新闻品级,帮衬三种格式输出
  •  XML Configuration + Dynamic Configuration

主导选择

log四net.dll 引用 + 配置文件 log四net.xml

当前,最新版本的 .dll 文件是:log4net
2.0.8

本有的首要商量 log4net 的安插文件:log4net.xml

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4Net" />
  </configSections>

  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingLogFileAppender" />
      <appender-ref ref="ColoredLogConsoleAppender" />
    </root>

    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender,log4net">
      <param name="File" value="Log\" />
      <param name= "AppendToFile" value= "true" />
      <param name= "DatePattern" value= "yyyyMMdd".log"" />
      <param name= "RollingStyle" value= "Date" />
      <param name="StaticLogFileName" value="false" />
      <lockingModel type="log4net.Appender.FileAppender.MinimalLock" />
      <layout type="log4net.Layout.XMLLayout,log4net">
        <param name="Header" value="" />
        <param name="Footer" value="" />
        <param name="ConversionPattern" value="[%d] [%t] %-5p %c-(%line)  %m%n" />
      </layout>
      <filter type="log4net.Filter.LevelRangeFilter">
         <param name="LevelMin" value="ALL" />
         <param name="LevelMax" value="OFF" />
      </filter>
    </appender>

    <appender name="ColoredLogConsoleAppender" type="log4net.Appender.ColoredConsoleAppender,log4net">      
      <mapping>
        <level value="INFO" />
        <foreColor value="Grey" />
      </mapping>
      <mapping>
        <level value="WARN" />
        <foreColor value="Green" />
      </mapping>
      <mapping>
        <level value="ERROR" />
        <foreColor value="Red, HighIntensity" />
      </mapping>
      <layout type="log4net.Layout.PatternLayout,log4net">
         <param name="ConversionPattern" value="[%d] [%t] %-5p %c-(%line) %m%n" />
      </layout>
    </appender>
  </log4net>
</configuration>

首先对 log四net 配置文件的格式作表明,以下三种艺术是等价的:

<param name="keyName" value="valueName" />
<keyName value="valueName" />

上边对 .xml 文件中多少个重大标签作表达:

[0.1].RollingFileAppender

RollingFileAppender 基于 FileAppender,FileAppender 和
RollingFileAppender 都是用来将日志写入到文本文件中,RollingFileAppender
提供越来越多的操纵选项

[0.2].ColoredConsoleAppender

ColoredConsoleAppender 基于
ConsoleAppender,ConsoleAppender
和 ColoredConsoleAppender 都以用来将日志写入到调控台,ColoredConsoleAppender 提供越多的操纵选项 

[1].level

日记新闻调控等第,由低到高 ALL|DEBUG|INFO|WA汉兰达N|E智跑RO冠道|FATAL|OFF,私下认可DEBUG,只记录 >=level 的日记音信

内部,ALL表示同意具有的日志请求,OFF代表拒绝全体的呼吁

[2].appender-ref

概念日志对象所使用的 Appender

[3].RollingStyle

日志文件滚动方式,援助 Date、Size、Composite 三种格局,具体地:

  •  Date:日期滚动,每日3个日志文件
  •  Size:文件大小滚动,相配如下 二 个参数:
    •  MaximumFileSize:日志文件大小限制,支持 KB|MB|GB
    •  MaxSizeRollBackups:日志文件个数限制,默认 ``-1 表示无限制
  •  Composite:Date + Size 的合体版,适于日志量比较大的情景

[4].layout

日志音信输出格式,支持 XMLLayout、PatternLayout,具体地:

  •  PatternLayout:优秀格式
  •  XMLLayout:XML格式

关于 Layout 的详细信息,参见上面包车型地铁 深入领会 – Layout 部分。

[5].lockingModel

文件锁类型,允许多少个进程能够写入同3个文件,RollingFileAppender
本人不是线程安全的,若在程序中从不开始展览线程安全范围,可在此间配置、确定保证写入安全。援助两种类型:

  •  ExclusiveLock:排它锁
  •  MinimalLock:最小锁定

别的,供给留意的多少个难题

// 如果是网站项目,须在项目中的 AssemblyInfo.cs 文件的最后一行中添加
[assembly: log4net.Config.DOMConfigurator(ConfigFile = "Web.config", Watch = true)] 

在 log4net 中,最常用的是 RollingFileAppender,各样常用的不等安插参见:Log4net中的RollingFileAppender解析

上边是 log四net 的宗旨调用方法:读取 log4net.xml 文件,日志音讯记录。

using log4net;

namespace log4net_sqh
{
    public class Program
    {
        static void Main(string[] args)
        {
            string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
            string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
            string configFilePath = assemblyDirPath + " \\log4net.xml";
            log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(configFilePath));

            ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

            Logger.Fatal("fatal, 严重错误");
            Logger.Error("error, 错误");
            Logger.Warn("warn, 警告");
            Logger.Info("info, 信息");
            Logger.Debug("debug, 调试");           

            Console.Read();
        }
    }
}

连锁内容可参见:C#行使Log四Net记录日志

中间,MethodBase.GetCurrentMethod().DeclaringType
用于获取注解该成员的类。ConfigureAndWatch 方法读取配置文件
log四net.xml,相比较 Configure
方法,当配置文件有转换时会重新加载。不一样等第的日志信息记录支持三种重载方法:

void Info(object message);
void Info(object message, Exception exception);
void InfoFormat(string format, params object[] args);

上面提供如下链接,能够参见:

除开独立选用 log肆net.xml 文件作为 log四net
的安排文件外,亦可径直利用项目自带的 app.config(CS程序)或
web.config(BS程序) 文件,只要将 <log四net> 结点放在 <configuration>
结点中,但 <log四net> 结点必须在 <configSections>
结点的下面,不然不会记日志。

style=”font-family: Consolas; font-size: 9pt;”> style=”color: #a31515;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一5;”> style=”color: #a315壹五;”> style=”color: #a315一伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一五;”> style=”color: #a315一五;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a31515;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a31515;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315壹伍;”> style=”color: #a315一5;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一5;”> style=”color: #a315壹伍;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a315一五;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a3151伍;”> style=”color: #a3151伍;”> style=”font-size: 1四px; color: #000000;”>直接用配备文件 app.Config 或
web.Config 配置使用系统的运营参数,比单独做三个 .xml
配置文件,简洁方便:

  • style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a315壹伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一5;”> style=”color: #a31515;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一5;”> style=”color: #a315①伍;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a315一伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一伍;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 九pt;”> style=”color: #a31515;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a31515;”> style=”color: #a31515;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一伍;”> style=”color: #a31515;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 九pt;”> style=”color: #a315壹5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a3151伍;”> style=”color: #a315一五;”> style=”font-size: 1四px; color: #000000;”>提供普及的
    connectionStrings 和 appSettings,给出 数据库连接 和
    常见的键/值表的概念访问方法
  • style=”font-family: Consolas; font-size: 九pt;”> style=”color: #a315一伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315一伍;”> style=”color: #a31515;”> style=”color: #a315一伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a31515;”> style=”color: #a315一5;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 玖pt;”> style=”color: #a31515;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315壹五;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 九pt;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a3151伍;”> style=”color: #a3151五;”> style=”color: #a315一伍;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a31515;”> style=”color: #a315一5;”> style=”color: blue;”> style=”font-family: Consolas; font-size: 九pt;”> style=”color: #a315一5;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: red;”> style=”color: black;”> style=”color: black;”> style=”color: #a315壹5;”> style=”color: #a315一5;”> style=”font-size: 1肆px; color: #000000;”>提供自定义段configSections,能够活动定义段成分,eg.
    log四net、assembly

上面给出一个 app.config 文件的模板代码:

图片 1

利用并读取 app.config 文件的陈设消息,项目应小心:

[1]. 添加引用程序集 System.configuration
[2]. 名称空间添加 using System.Configuration; 

关于加载 log4net.config
配置
文本,能够动用差异的办法,相关音讯参考:log四net
配置文件配置格局

(1)Configuration Attributes

该格局经过增多 Assembly
Attribute,在类型的 AssemblyInfo.cs 文件中增加如下代码,提示程序从 app.config
中读取配置

[assembly: log4net.Config.XmlConfigurator(Watch=true)]

(2)appSettings

该方法在布署文件中加多如下配置

<appSettings>
  <add key="log4net.Config" value="log4net.config"/>
  <add key="log4net.Config.Watch" value="True"/>
</appSettings>

(3)Configuration Files

该措施得以因而如下二种格局实现

  • Using the .NET System.Configuration API
  • Reading the file contents directly

引入第3种办法,在程序入口选取如下代码

log4net.Config.XmlConfigurator.Configure(new FileInfo("app.config")); 或 
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("app.config"));

其次种办法正是眼下说的单身选择一个 log4net.xml 文件作为配置文件的方法

Only one log4net element can be specified in the xml file, but it may/can be located anywhere in the XML hierarchy. 

那二种办法,Configuration Files 具备相对调控权,appSettings
次之,assembly-level attributes 最弱。

2个小小的恢宏知识

app.configexe.configvshost.exe.config

  • app.config是自定义的配置文件,vshost.exe.config和exe.config是程序运转时自动成立的剧情跟app.config一样的文件
  • vshost.exe.config是程序运营时选用的布局文件,exe.config是程序运维后会复制到vshost.exe.config
  • 从app.config复制到exe.config再复制到vshost.exe.config
  • 对 .config 文件的读写操作,实际是针对性 exe.config,该公文至关重要

关于 app.config
文件配置难题,可参照:关于自定义配置结点

深深掌握

最新版本的源码文件:log4net-2.0.8-src.zip

log四net 有七个重点组件,分别是:

  • Logger(记录器)
  • Appender(附着器)
  • Layout(布局)
  • Repository(库)

上面分别实际介绍各种零部件:

1
Logger

应用程序交互的首要组件,产生日志新闻,日志音讯经过 Layout
的格式化处理才会输出。

Log四net 框架定义一个 ILog 接口,全体的 logger
类(包罗自定义的logger类)都不可能不贯彻这一个接口。

public interface ILog
{
   // 基本方法
   void Debug(object message);
   void Info(object message);
   void Warn(object message);
   void Error(object message);
   void Fatal(object message);

   // 以上的每一个方法都有多个重载的方法,用来支持异常处理及格式化处理
   void Debug(object message, Exception ex);
   void DebugFormat(...);
        ...

   //属性用来检查Logger的日志级别
   bool isDebugEnabled;
        ...
}

Log肆net 框架定义一个 LogManager 类,管理全部的 logger 对象。该类的
GetLogger() 静态方法,用大家提供的名字创办 Logger 对象或索求已经存在的
Logger 对象

log4net.ILog log = log4net.LogManager.GetLogger("logger-name");

措施 GetLogger() 的入参能够行使自定义的日志名字
“logger-name”,也得以动用如下参数作为入参

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType

在应用程序中能够创设四个 Logger,每种实例化的 Logger 对象都被 log四net
框架当作命名实体(named entity)来分别维护。 

2
Appender

概念输出介质,log肆net 协助多样情势的日志音讯输出。

叁个 Appender 对象缺省级地区级将兼具的日记音讯传递到输出流,能够运用 Appender Filters 根据不一样的正规过滤日志事件。

3Layout

用来调节 Appender 的输出格式,能够是线性的或XML,可是一个 Appender
只能有一个 Layout。log肆net 帮忙各类 layout:

  • PatternLayout:精彩格式,最常用
  • SimpleLayout:轻易格式,只输出日志等级与音信内容
  • XMLLayout:XML格式
  • RawTimeStampLayout:可格式化时间,常用于出口到数据库

有关 layout 结点的配备表达:

  • %d:datetime,当前讲话的日鸡时间,%d{HH:mm:ss,fff} 能够只呈现时间

  • %p:priority,日志等第

  • %m:message,输出的日志音信
  • %n:newline,换行
  • %t:threadid,当前说话所在线程ID
  • %c:class,超过天记对象的称谓
  • %L:输出语句所在的行号
  • %F:输出语句所在的文书名
  • %-数字:表示该项的蝇头长度,要是不够,则用空格填充

4
Repository

承担日志对象协会结构的保卫安全。

庞大应用

在上学 log四net 的末段,实现 log肆net 输出 xml 格式的日记:

  • 使用 log4net 自带的 XmlLayout
  • 自定义 XmlLayout

应用自定义 XmlLayout 格式,务必如下:

  • write a class deriving from XmlLayoutBase,override the FormatXml method
  • instruct your appender with yourXmlLayout

首先付诸配置文件 app.config,基本同上,layout 使用自定义的 XmlLayout

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
  </configSections>

  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingLogFileAppender" />
    </root>

    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender" >
      <param name="File" value="log\" />
      <param name="AppendToFile" value="true" />
      <param name="RollingStyle" value="Date" />
      <param name="datePattern" value="yyyyMMdd'.txt'" />
      <param name="staticLogFileName" value="false" />
      <layout type="MyNamespace.MyXmlLayout">
        <param name="Header" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&#13;&#10;&lt;Root&gt;&#13;&#10;" />
        <param name="Footer" value="&lt;/Root&gt;&#13;&#10;" />
      </layout>
    </appender>    
  </log4net>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

其中, 表示 \r\n
换行。

下一场,自定义类 MyXmlLayout 承接自 XmlLayoutBase,重写 FormatXml 方法:

namespace MyNamespace {
    public class MyXmlLayout : XmlLayoutBase
    {
        protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
        {
            // 自定义 XML 文档格式
            ... ...
        }
    }
}

该方式中能够自定义突显的 xml 文件内容。在那之中,类 XmlLayoutBase 定义如下

public abstract class XmlLayoutBase : LayoutSkeleton
{
    protected XmlLayoutBase();

    public override void ActivateOptions() { }
    public override void Format(TextWriter writer, LoggingEvent loggingEvent);
    {
        XmlTextWriter xmlTextWriter = new XmlTextWriter(new ProtectCloseTextWriter(writer));
        xmlTextWriter.Formatting = Formatting.None;
        xmlTextWriter.Namespaces = false;
        this.FormatXml(xmlTextWriter, loggingEvent);
        xmlTextWriter.WriteWhitespace(SystemInfo.NewLine); // 换行
        xmlTextWriter.Close(); // 关闭流
    }

    protected abstract void FormatXml(XmlWriter writer, LoggingEvent loggingEvent);
}

而外,主函数调用情势保险不变。

至此,xml
格式日志文件中央可正常输出。不过该公文或然存在缩进、中文格式、xml读取的标题,需做如下校对:

[1].
缩进 XML

在 log4net 的源码 src\Layout\XMLLayoutbase.cs 中,在
Format 方法中加多如下代码

// 注意是使用 XmlTextWriter,而不是 XmlWriter
xmlTextWriter.Formatting = Formatting.Indented;
xmlTextWriter.Indentation = 2;

为了打响编写翻译,还索要修改 src\AssemblyInfo.cs 文件,加多 false 关闭
log肆net 的强具名

#if fasle && STRONG && (CLI_1_0 || NET_1_0 || NET_1_1 || NETCF_1_0 || SSCLI)
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(@"..\..\..\log4net.snk")]
#endif

将转变的 log四net.dll 文件替换项目中的 .dll 文件就能够。

参考:修改 log四net,生成缩进
XML

[2].
普通话格式

在 log4net 的源码 src\Util\Transform.cs 中,更新如下代码

private static Regex INVALIDCHARS = 
    new Regex(@"[^\x09\x0A\x0D\x20-\xFF\u00FF-\u07FF\uE000-\uFFFD\u4e00-\u9fa5]",RegexOptions.Compiled); 

[3].
XML 读取小标题

在 log4net 的源码 src\Layout\XMLLayout.cs 中,方法 ActivateOptions 立异如下

将 ":" 全部改成 "_" 

参考:修改
log四net,扶助粤语格式

[4].
<Root> 结点难点

脚下转换的 xml 文件,根结点唯有 <Root>,未有 </Root>

关于怎么解决,有待于进一步商讨。

日前,在接纳的时候要特别处理一下,为 xml 文书档案增添 </Root>

二.    在采纳Commons Logging时,大家常常会看到以下办法的写法:

NLog

免费的 .NET 开源日志框架,NLog
允许自定义从追踪音讯的源点(source)到记录跟踪新闻的靶子(target)的条条框框。

有关 NLog
的详细音讯,参考官方网址:NLogCodeProject –
Introduction to
NLog

天性特点

  • Easy-to-configure:configuration file or programmatically
  • Templatable:layout renders
  • Extensible:write custom targets or pass custom values
  • 字符串延迟加载
  • 支撑异步日志记录

NLog 与 log四net 比较,具体细节可参见:[日志框架相比较 NLog VS

if (logger.isDebugEnabled()) {

Log4net](http://www.cnblogs.com/qinjin/p/DonetLogCompare.html);[两者比较

逗比版](http://www.cnblogs.com/wanglee/archive/2013/05/22/3092474.html);

配备文件

NLog 同 log四net 同样,可是比 log四net 更简约,NLog 使用路由表(routing
table)实行安顿,log肆net 使用层次性的 appender
配置,一样支撑二种情势的配备方式

  • 独自的 NLog.Config 配置文件,直接以 <nlog> 作为根结点
  • 在 App.config/Web.config
    文件中增添 NLog 的配备结点

不论是选择哪一种配备方式,配置内容和使用情势是一模同样的。配置音信结点包涵两有些

  • targets:输出指标,扶助 File、Mail、Console、DataBase等,每种target 代表贰个出口目的,包括1个特性
    • name:输出模板名称,在 <rules> 中应用
    • type:输出类型,NLog can dynamically write to one of multiple
      targets for each log message
  • rules:路由规则,将日志和出口目的匹配起来
    • name:日志记录者的名字 (允许行使通配符*)
    • minlevel/maxlevel:日志范围的最低/高端别
    • level/levels:单11日志等第/一名目诸多日志等级,逗号分隔
    • writeTo:规则相称时日志信息应该被写入的一层层目的,由逗号分隔

率先种办法,特别注意不要包括中文!

其次种办法,尤其注意在 App.config/Web.config 中必须保险 <configSections>
结点存在并且是根结点 <configuration>
的率先个结点,不然程序会报错:配置体系不能开首化,文件结构情势如下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, nLog"/>
  </configSections>

  <nlog>
    <targets>
      <target />
    </targets>

    <rules>
      <logger />
    </rules>  
  </nlog>
</configuration>

宗旨采取

NLog.dll 引用 + 配置文件

脚下 NLog 的源码最新版本是:NLog 4.4.10 –
Source

1)加多引用

添加对 NLog 的引用,有 2 种方法

  • 透过 Nuget,加多 NLog 和 NLog Configuration 五个文本
  • 工具-Nuget程序包管理器-程序包管理器调节台,输入 Install-Package NLog
    和 Install-Package NLog.config 

也许直接在先后中加多 NLog.dll 引用,并 using NLog; 

2)配置结点 <nlog>

安插文件 <nlog>
结点协助三个路由规则和出口指标,卓殊灵活,下边直接付出结点配置消息

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="nlog" type="NLog.Config.ConfigSectionHandler, nLog"/>
  </configSections>

  <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        autoReload="true" 
      throwExceptions="false">
    <targets>
      <!-- 控制台 -->
      <target name="myLogConsole" xsi:type="ColoredConsole"
              layout="${date:format=HH\:mm\:ss}> ${level:padding=-5} ${message}"/>
      <!-- 文件(每天一个日志文件) -->
      <target name="myRollingLogFile" xsi:type="File"
              fileName="${basedir}/Log/${date:format=yyyyMMdd}.txt" keepFileOpen="false"
              layout="${date:format=HH\:mm\:ss} ${level:uppercase=true:padding=-5} ${message}" />
    </targets>

    <rules>
      <logger name="*" minlevel="Debug" writeTo="myLogConsole" />
      <logger name="*" minlevel="Info" WriteTo="myRollingLogFile" />
    </rules>  
  </nlog>
</configuration>

里头,autoReload=”true”
表示自动再安顿,无需重启程序、更新配备音信。

专程提醒,在 源码文件\examples\targets\Configuration
File 下有种种各个的 NLog 配置文件能够参见。

至于对陈设文件的恢弘使用

[1]. 根结点 <nlog>
配置属性 internalLogLevel 和 internalLogFile

用以查看 NLog
输出日志时的里边新闻,比如配置文件音信错误等,可是会有作用影响。

[2]. File Archival Based on
Time or Size

Log files can be automatically archived by moving them to another
location after reaching certain size or time.

// 按时间滚动归档
<targets>
    <target name="onTimeFile" xsi:type="File"
        layout="${longdate} ${level:padding=-5} ${message}" 
        fileName="${basedir}/Log/logfile.txt" 
        archiveFileName="${basedir}/Archives/log.{#}.txt"
        archiveEvery="Day"
        archiveNumbering="Rolling"
        maxArchiveFiles="30"
        concurrentWrites="true"
        keepFileOpen="false"
        encoding="iso-8859-2" />
</targets>    

// 按大小滚动归档
<targets>
    <target name="file" xsi:type="File"
        layout="${longdate} ${level:padding=-5} ${message}" 
        fileName="${basedir}/Log/logfile.txt" 
        archiveFileName="${basedir}/Archives/log.{#####}.txt"
        archiveAboveSize="102400" // unit:Byte
        archiveNumbering="Sequence"
        concurrentWrites="true"
        keepFileOpen="false"
        encoding="iso-8859-2" />
</targets>

Archive Numbering 的分解以及此外配置消息可参见:File target
Configuration

[3].
Layout:格式化输出(CSV/XML

Layouts provide a way to format the contents of the logs as it is
written to a file. There are 2 main kinds of layouts:

  • simple layout – just a string,which is composed of Layout
    Renderers
  • structural layouts – which can output XML, CSV, and other complex
    formats

第一,CSV 格式的结点配置

<target name="myCsv" xsi:type="File" fileName="${basedir}/file.csv">
  <layout xsi:type="CSVLayout">
     <column name="Time" layout="${longdate}" /> 
     <column name="Level" layout="${level}"/>
     <column name="Message" layout="${message}" />
  </layout>
</target>

上边给出 XML 格式的结点配置

结点如何配置,暂时不明确
仅有的信息可参见:
[1]. Configuring NLog to log exceptions in an XML output?

在程序中调用方法:

namespace NLogSqh {
    public class Program {

        public static NLog.Logger logger = null;        
        public static void Main(string[] args)
        {
            logger = NLog.LogManager.GetCurrentClassLogger();

            logger.Trace("Trace");
            logger.Debug("Debug");
            logger.Info("Info");
            logger.Warn("Warn");
            logger.Error("Error");
            logger.Fatal("Fatal");                                 
        }
    }
}

参考:NLog
学习连串
NLog
小说种类

    logger.info(“Loading XML bean definitions from ” +
encodedResource.getResource());

log4j

log for java(log四j)是 Apache 的开源项目作用强大的日记组件,Java
编写的保障、快速和灵活的日记框架(API),首要选拔于 Java 项目。

贰个心细编辑的日记代码提供高效的调整、维护方便、应用程序的运维时音讯结构化存款和储蓄。

现阶段最新的 Generation 是 Apache log四j 二,最新的 Release 是 Log四j
2.8.2,相关代码:

在编写翻译前,最佳应正确安装 PATH 和
CLASSPATH ,项目中最棒在 /src/ 目录下存放 log四j.properties
文件。

品质特点

  • 线程安全
  • 参数配置化
  • 两种日记品级,三种输出介质

log4j 包涵多个重点组件

  • loggers:记录日志音信
  • appenders:日志记录方式、输出介质等新闻
  • layouts:日志音讯格式化

上边是 log四j 组件虚拟图

图片 2

输出介质:协理调节台、文件、数据库等

  • ConsoleAppender:控制台
  • FileAppender:文件
  • RollingFileAppender:文件大小滚动生成日志,达到钦点尺寸时生成贰个新的日记文件
  • DailyRollingFileAppender:时间滚动生成日志文件
  • WriterAppender:将日志新闻以流格式发送到任意钦命的地点,eg.
    数据库

主导选用

log四j 的居多新闻能够参见
log4net,此部分略过,过去的事情不要再提。

Log四j
支持两种配备文件格式,一种是XML格式的文书,1种是Java性情文件(键=值)

log4j 通过 log4j.properties
文件(key-value情势)保存属性配置。暗中同意情况下,日志管理在 CLASSPATH
查找贰个名称为 log四j.properties 的文本。基本格式如下:

# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.File = ${log}/log.out
log4j.appender.FILE.Append = true
log4j.appender.FILE.Threshold= INFO

# Define the layout for file appender 
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout 
log4j.appender.FILE.layout.conversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

能够将上述格式调换为 xml 方式,两者如出一辙

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>

    <appender name="FILE" class="org.apache.log4j.FileAppender">
       <param name="file" value="${log}/log.out" />
       <param name="append" value="true" />
       <layout class="org.apache.log4j.PatternLayout">
          <param name="conversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n" />
       </layout>
    </appender>

    <logger name="log4j.rootLogger" additivity="false">
       <level value="ALL"/>
       <appender-ref ref="FILE"/>
    </logger>

</log4j:configuration>

在 Java 程序中调用如下:

import org.apache.log4j.Logger;

import java.io.*;
import java.sql.SQLException;
import java.util.*;

public class log4jExample
{
   public static void main(String[] args) throws IOException,SQLException
   {     
       PropertyConfigurator.configure("log4j.properties"); // 入参为配置文件的路径
       Logger logger = Logger.getLogger( log4jExample.class.getName() );
       logger.debug("DEBUG");
       logger.info("INFO");
   }
}

有关配置文件有多少个要求专注的标题:

[1].
log肆j.properties 文件中根节点配置格式

log4j.rootLogger = [ level ] , appenderName1, appenderName2, …

[2].
关于 appender 结点属性配置

// 为不同的 appender 配置日志输出级别
log4j.appender.AppenderName.Threshold = DEBUG
// 所有的消息都会被立即输出,默认 true
log4j.appender.AppenderName.ImmediateFlush = true 

[3].
对于项目中 log肆j.xml 和 log4j.properties 不在 CLASSPATH
路线的,在档次先后中要求直接加载多少个文本

DOMConfigurator.configure(log4j_xml_Path);  
PropertyConfigurator.configure(log4j_properties_Path);

除此上述基本配备外,能够扩张 FileAppender,协助 Date 和 Size
滚动日志,详情新闻参见:log4j
日志配置教学

1Date:按日期滚动,协助八种DatePattern

# Define the file appender
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender

# Set the DatePattern
log4j.appender.FILE.DatePattern='.' yyyy-MM-dd'.log'

2Size:按文件大小滚动

# Define the file appender
log4j.appender.FILE=org.apache.log4j.RollingFileAppender

# Set the maximum file size before rollover
log4j.appender.FILE.MaxFileSize=10MB
# Set the the backup index
log4j.appender.FILE.MaxBackupIndex=10

至于 log四j 的详细音讯,参考官方网址:log4j
官网

}

SLF4J

Commons Logging
通用日志接口,用户可以自由选取第3方日志组件作为具体落实。通过动态查找体制,在程序运维时自动找寻真正使用的日记框架。代码重视的是
common-logging 而非具体的日志组件,
幸免与具体的日记方案一贯耦合,供给时可改动日志完结的第一方组件。

import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  

public class A {  
    private static Log logger = LogFactory.getLog(this.getClass());  
}  

Commons Logging + log4j
作为Java日志的经文组合,可是 Commons Logging 存在

  • ClassLoader 问题
  • 为了制止多余的字符串拼接,必须 isDebugEnabled() 剖断逻辑

的难题,log四j 团队开采出日记门面框架SLF四J。

Simple Logging Facade for
Java(SLF4J,轻松日志门面),不是切实的日记消除方案,用于服务各类日志系统,为分化的日志框架提供统壹的日记接口,在编写翻译时绑定相应的日记框架(静态绑定)。关于
SLF4J 的详细新闻,参见:SLF4J 官网

好像 JDBC,可是比 JDBC 更简明,无需加载驱动、只需加上特定的包。

面向 Java,推荐 SLF4J,而不是 log4j

参见:干什么使用SLF肆J而不是Log四J来做Java日志怎么要选取SLF四J而不是Log四J

属性特点

  • 独立于各样日志系统
  • Parameterized Logging,提供基于占位符(place
    holder)的日记记录情势,可读性高
  • 日志音信(String)延迟创设,收缩 String 池过多损耗堆内部存款和储蓄器

中心采纳

SLF肆J 只是三个日记外壳,须求在档次中投入
slf四j-jdk1四.jar、slf四j-log四j1二.jar 或
logback.jar,将日志调用转载到骨子里的日记框架。SLF四J 是1个抽象层(
Abstraction Layer),使你的代码独立于自由二个一定的日志API,具体地:

SLF4J 应用 Facade(门面)设计格局,SLF肆J 所提供的宗旨 API
是提供一些对外的接口以及1个 LoggerFactory 工厂类

SLF4J
提供统壹的日志记录接口,只要根据其提供的措施记录就能够,最终日志格式、日志等级、输出格局等由具体的日记系统的配置来完成,由此在利用中能够灵活切换成分歧的日记系统,用户采纳本人愿意的日志系统
loging APIs。

以 log四j 为例,SLF肆J 的绑定操作完结如下

图片 3

  • slf4j-api 作为日志接入的接口,编译时 slf四j-api 的 public final class
    LoggerFactor 类中 private final static void bind()
    方法会搜索并绑定具体的日记达成类,首要透过调用
    StaticLoggerBinder.getSingleton() 方法
  • slf四j-log肆j1贰 是链接 slf四j-api 和 log四j 的中级适配器,该类实现slf四j-api 中 LoggerFactoryBinder 接口,从而在编译时绑定
    slf肆j-log4j1二 的 getSingleton() 方法
  • log四j 是切实的日记系统,通过 slf四j-log④j1二 初阶化 log四j

当中,SLF4J 的主干是 slf四j api(slf4j-api.jar包),该 .jar
包只提供日志记录接口。有关对 SLF4J 基本认识,参见:初识
SLF4J

SLF四J 不借助于其余特殊的 class loader 机制。实际上,SLF四J
与已有日记达成的绑定是在编写翻译时静态实施的,具体绑定工作是经过三个 .jar包
完结的,使用时倘诺把相应的jar包(唯有三个)放到类路径上就可以,SLF四J 通过
.jar包 来报告使用哪一类日志系统实现。

图片 4

在 Java 程序中调用如下,通过工厂类 LoggerFactory 创制日志指标

import org.apache.SLF4J.Logger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestSlf4j 
{
    private static final Logger logger = LoggerFactory.getLogger(TestSlf4j.class);

    public static void main(String[] args) 
    {
        logger.info("Hello {}", "SLF4J");
    }
} 

专注,在品种中 Maven 正视应如下设置

<!-- slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.12</version>
</dependency>

<!-- slf4j-log4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>

<!-- log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

切实达成进程讲授参预:SLF4J+log肆j
原理完毕和源码分析
SLF四J
源码剖析

长远讨论

目前 Latest STABLE version 是
slf4j-1.7.25.zip;个中,SLF4J
的骨干包为

// 核心包
slf4j-api-1.7.25.jar
// 核心包源码
slf4j-api-1.7.25-sources.jar

学学 SLF四J 的源码,能够从以下多少个包开头

slf4j-api,slf4j-log4j12

SLF肆J 有七个关键组件,分别是:

  • ILoggerFactory:工厂接口
  • LoggerFactory:工厂类
  • LoggerFactoryBinder:日志框架绑定接口
  • Logger:日志接口
  • StaticLoggerBinder:日志框架绑定完成类

此处先付给全部的流程:LoggerFactory 调用 getILoggerFactory()
方法,该方法会开头化 LoggerFactory,即通过在 bind() 方法中加载 classpath
中的 StaticLoggerBinder 类,并依照加载结果设置当前 LoggerFactory
的开端化状态,从而在 getILoggerFactory() 方法中经过当前 LoggerFactory
的情事判别再次来到的 ILoggerFactory 实例。下边分别介绍各种有关组件:

1ILoggerFactory

该接口仅提供二个 getLogger() 方法,用于获取日志实例。

public interface ILoggerFactory {
    // 所有日志框架均通过该方法获取Logger实例
    public Logger getLogger(String name);
}

富有的日记框架均须求完成该接口。

2LoggerFactory

承担搜索系统里日志的落实,提供 getLogger() 创立日志实例。

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

该类的 getLogger()
方法通过调用该类的 getILoggerFactory()
方法赚取日志工厂接口的实例,再调用接口提供的 getLogger()
方法获得具体的日志实例。首先看下该类的 getILoggerFactory() 方法

public static ILoggerFactory getILoggerFactory() 
{
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();  // 初始化
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case NOP_FALLBACK_INITIALIZATION:
        ... ...
}

第二步工厂开头化,第贰步调用 StaticLoggerBinder.getSingleton().getLoggerFactory()
重回日志工厂接口的实例,具体查看上边包车型客车(五)StaticLoggerBinder
部分。

这边看下 performInitialization() 方法,用于 绑定日志框架+校验日志框架版本号,代码如下

private final static void performInitialization() 
{
    bind();  // 绑定日志框架
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
        versionSanityCheck();  // 校验日志框架版本号
    }
}

在这之中,bind() 方法是 SLF四J 最大旨的代码,完成日志框架绑定,具体地

private final static void bind() {
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        // skip check under android, see also
        // http://jira.qos.ch/browse/SLF4J-328
        if (!isAndroid()) {
            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();  // 寻找可能的日志框架
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);  // 输出搜索到的所有StaticLoggerBinder日志
        }

        // the next line does the binding:实现绑定
        StaticLoggerBinder.getSingleton(); 
        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
        reportActualBinding(staticLoggerBinderPathSet);  // 输出最终实际绑定的StaticLoggerBinder日志
        fixSubstituteLoggers();
        replayEvents();
        // release all resources in SUBST_FACTORY
        SUBST_FACTORY.clear();
    } 
    catch () {
        ... ...
    }
}

该方法会加载并绑定找见的率先个 StaticLoggerBinder
类,并调用该类的 getSingleton() 方法,达成日志框架的绑定。当中,

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

static Set<URL> findPossibleStaticLoggerBinderPathSet() 
{
    Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
    try 
    {
        ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();

        Enumeration<URL> paths;
        if (loggerFactoryClassLoader == null) {
            paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
        } 
        else {
            paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
        }

        while (paths.hasMoreElements()) {
            URL path = paths.nextElement();
            staticLoggerBinderPathSet.add(path);
        }
    } 
    catch (IOException ioe) {
        Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
}

findPossibleStaticLoggerBinderPathSet() 方法会查找项目根路线下的 “org/slf4j/impl/StaticLoggerBinder.class”
文件。然后 reportMultipleBindingAmbiguity() 方法会报告文本查找的结果音讯

private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
    if (binderPathSet.size() > 1) 
    {
        Util.report("Class path contains multiple SLF4J bindings.");
        for (URL path : binderPathSet) {
            Util.report("Found binding in [" + path + "]");
        }
        Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
    }
}

假如找到四个,则输出 “Class path contains
multiple SLF四J bindings” 提醒音信,表示有多少个日志框架绑定了
SLF四J。

3Logger

该接口提供不一样层级日志音讯记录的相继艺术。

public interface Logger {
    public void debug(String msg);
    public void debug(String format, Object... arguments);
    ... ...
    info, warn, error
}

4LoggerFactoryBinder

日记框架绑定接口,仅且提供 二 个法子

public interface LoggerFactoryBinder {
    public ILoggerFactory getLoggerFactory();
    public String getLoggerFactoryClassStr();
}

种种支持 SLF4J 的日记框架必须存在三个兑现该接口的 StaticLoggerBinder
类。

5StaticLoggerBinder

完成 LoggerFactoryBinder 接口,类 LoggerFactory 调用该类的
getSingleton() 和 getLoggerFactory() 方法再次来到具体日志框架的接口实例,以
slf四j-log四j1二.jar 为例

// 单例模式
public class StaticLoggerBinder implements LoggerFactoryBinder 
{
    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
    public static final StaticLoggerBinder getSingleton() {  // 静态方法
        return SINGLETON;
    }

    private static final String loggerFactoryClassStr = Log4jLoggerFactory.class.getName();
    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }

    private final ILoggerFactory iloggerFactory;
    public ILoggerFactory getLoggerFactory() {
        return iloggerFactory;
    }
    private StaticLoggerBinder() {
        iloggerFactory = new Log4jLoggerFactory();  // 日志框架log4j的工厂类
        ... ...
    }

    public static String REQUESTED_API_VERSION = "1.6.99"; // !final
}

此类的构造函数中新建八个 log四j 的日记工厂接口实例,并通过
getLoggerFactory() 方法重临。再深切地,查看 log四j的厂子类
Log四jLoggerFactory

public class Log4jLoggerFactory implements ILoggerFactory 
{ 
    ConcurrentMap<String, Logger> loggerMap;  // key: name (String), value: a Log4jLoggerAdapter;
    public Log4jLoggerFactory() {
        loggerMap = new ConcurrentHashMap<String, Logger>();  // 线程安全的HashMap类
        org.apache.log4j.LogManager.getRootLogger();  // force log4j to initialize
    }

    // 实现接口 ILoggerFactory 的 getLogger() 方法
    public Logger getLogger(String name) {
        Logger slf4jLogger = loggerMap.get(name);
        ... ...
    }
}

其间,ConcurrentHashMap 用于缓存全部从前创设的 Log四jLoggerAdapter
类实例,日志框架 log4j 的工厂类完成了接口 ILoggerFactory,调用 org.apache.log4j.LogManager.getRootLogger()
完毕对 log四j 的绑定,通过调用 getLogger()
方法重临1个 Log四jLoggerAdapter 类实例,那是因为 Log肆j 的 Logger 接口与
slf4j 的 Logger 接口不完全同样,供给在 Log肆j 的 Logger
对象上进行壹层封装。该类维护多少个 org.apache.log肆j.Logger
logger 对象,用于日志记录

public final class Log4jLoggerAdapter extends MarkerIgnoringBase 
                                      implements LocationAwareLogger, Serializable
{
    final transient org.apache.log4j.Logger logger;

    Log4jLoggerAdapter(org.apache.log4j.Logger logger) {
        this.logger = logger;
        this.name = logger.getName();
    }
}

当中,接口 LocationAwareLogger 承袭了 Logger 接口,是对 Logger
接口的包装

public interface LocationAwareLogger extends Logger { ... }

具体详细新闻参见:slf四j源码剖析 –
xingoo

汇总,要贯彻一个合营 SLF4J 的日志系统,最基本需求八个类:

  • StaticLoggerBinder 类,实现 LoggerFactoryBinder 接口
  • XxxLoggerFactory 类,实现 ILoggerFactory 接口
  • XxxLogger 类,实现 Logger 接口

存在isDebugEnabled()的判定逻辑是为了在幸免多余的字符串拼接,即假诺不存在isDebugEnabled()决断,就算当今日记等级为EQX56RO智跑时,在碰着logger.info()调用时,它还会先凑合日志音信的字符串,然后进入该办法内,才发现这一个日志语句并非打印。而那种多余的拼接不仅浪费了剩余的CPU操作,而且会增添GC的负责。SLF四J则提供以下的方式来缓解那一个主题材料:

logger.info(“Loading XML bean definitions from {}”,
encodedResource.getResource());

 

SLF4J综述

恍如CommonsLogging,SLF肆J在利用时通过LoggerFactory获得命名的Logger实例,然后经过该Logger实例调用相应的章程打字与印刷日志:

final Logger logger = LoggerFactory.getLogger(“levin.logging.slf4j”);

logger.info(“Using slf4j, current time is {}”, new Date());

不过分裂于CommonsLogging的动态绑定机制,SLF四J则动用了一种静态绑定的建制,即每种帮忙SLF四J的Logging框架必须存在1个一连自LoggerFactoryBinder接口的StaticLoggerBinder类:

public interface LoggerFactoryBinder {

 public ILoggerFactory getLoggerFactory();

   public String getLoggerFactoryClassStr();

}

LoggerFactory调用StaticLoggerBinder类中的getLoggerFactory()方法重回相应的ILoggerFactory实例:

public interface ILoggerFactory {

 public Logger getLogger(String name);

}

末段通过ILoggerFactory实例获取Logger实例:

public interface Logger {

   public String getName();

   …(trace)

   public boolean isDebugEnabled();

   public void debug(String msg);

   public void debug(String format, Object arg);

   public void debug(String format, Object arg1, Object arg2);

   public void debug(String format, Object… arguments);

   public void debug(String msg, Throwable t);

   public boolean isDebugEnabled(Marker marker);

   public void debug(Marker marker, String msg);

   public void debug(Marker marker, String format, Object arg);

   public void debug(Marker marker, String format, Object arg1, Object
arg2);

   public void debug(Marker marker, String format, Object… arguments);

   public void debug(Marker marker, String msg, Throwable t);

   …(info)

   …(warn)

   …(error)

}

也多亏因为那么些陈设,SLF4J在classpath下只帮助一个桥接包(slf肆j-simple-<version>.jar、slf4j-log四j12-<version>.jar、slf四j-jdk14-<version>.jar、logback-classic-<version>.jar等)。倘诺在classpath下存在多少个桥接包,则具体用哪个将在看那一个桥接包的加载顺序了,实际中会使用先加载的桥接包。同时SLF四J会打字与印刷使用哪个桥接包,哪些桥接包未有选拔。那种静态绑定的筹划比CommonsLogging在可扩展性上享有越来越灵活的建制,对“可插拔”的支撑也尤其便捷。要是要支持1个新的Logging框架,CommonsLogging须要经过在质量配置文件、或虚拟机性子中配置援助那个新的Logging框架的兑现类(达成Log接口);而SLF四J则只须求编制一个七个照应的类:

一.    实现Logger接口的类

二.    完毕ILoggerFactory接口的类

三.   
完结LoggerFactoryBinder接口的类StaticLoggerBinder类(必须利用StaticLoggerBinder类名),并且设有一个静态的getSingleton()方法。

四.   
完结马克尔FactoryBinder类的Static马克尔Binder类(必须利用Static马克尔Binder类名),可选。1般也会设有四个静态的SINGLETON字段,可是也是可选的。

伍.   
达成StaticMDCBinder类,可选。1般也会存在一个静态的SINGLETON字段,也可选。

SLF四J的类设计也针锋相对相比轻易(也感觉微微零散):

 图片 5

SLF四J达成实例,SLF四J API与SLF肆J Simple

由于使用了静态绑定的不二等秘书诀,而不是像CommonsLogging中的动态绑定,SLF四J中LoggerFactory的贯彻要比康芒斯Logging中LogFactory的贯彻要简明的多。即LoggerFactory调用getILoggerFactory()方法,该方法会开首化LoggerFactory,即通过在bind()方法中加载classpath中的StaticLoggerBinder类,并依照加载结果设置当前LoggerFactory的发轫化状态,从而在getILoggerFactory()方法中通过当前LoggerFactory的图景推断再次来到的ILoggerFactory实例。简单的示意图如下:

 图片 6

bind()方法的基本点源码如下:

 private final static void bind() {

    try {

      …

      //完成绑定

      StaticLoggerBinder.getSingleton();

      INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;

      …

    } catch (NoClassDefFoundError ncde) {

      String msg = ncde.getMessage();

      //推断是还是不是是因为尚未找到StaticLoggerBinder类引起的那几个

     
//此时,使用NOPLoggerFactory类重返给getILoggerFactory(),不打字与印刷任何日志

      if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {

        INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;

        …

      } else {

        // INITIALIZATION_STATE = FAILED_INITIALIZATION

        failedBinding(ncde);

        throw ncde;

      }

    } catch (java.lang.NoSuchMethodError nsme) {

      String msg = nsme.getMessage();

      if (msg != null &&
msg.indexOf(“org.slf4j.impl.StaticLoggerBinder.getSingleton()”) != -1) {

        INITIALIZATION_STATE = FAILED_INITIALIZATION;

        …

      }

      throw nsme;

    } catch (Exception e) {

      //INITIALIZATION_STATE = FAILED_INITIALIZATION;

      failedBinding(e);

      throw new IllegalStateException(“Unexpected initialization
failure”, e);

    }

 }

即bind()方法运用调用StaticLoggerBinder.getSingleton()方法来贯彻绑定,尽管该办法调用成功,则将开端化状态设置为SUCCESSFUL_INITIALIZATION,若是因为尚未找到StaticLoggerBinder类而滋生的丰硕,则将状态设置为NOP_FALLBACK_INITIALIZATION,不然将状态设置为FAILED_INITIALIZATION,并抛出分外。假使在脚下classpath下存在多少个桥接jar包,在贯彻绑定前后会记录存在什么可应用的桥接jar包,绑定了这个ILoggerFactory类。

在bind()重返后,performInitialization()方法会再做1些本子检查,即StaticLoggerBinder能够定义2个静态的REQUESTED_API_VEKoleosSION字段,表示该StaticLoggerBinder匡助的SLF四J版本,若是该版本不在LoggerFactory定义的分外版本列表中(API_COMPATIBILITY_LIST),SLF四J会打字与印刷警告消息,并列出当前LoggerFactory包容的版本列表。而后在getILoggerFactory()方法中会依据目前LoggerFactory的初叶化状态来支配回到的ILoggerFactory实例:

 public static ILoggerFactory getILoggerFactory() {

    if (INITIALIZATION_STATE == UNINITIALIZED) {

      INITIALIZATION_STATE = ONGOING_INITIALIZATION;

      performInitialization();

    }

    switch (INITIALIZATION_STATE) {

      case SUCCESSFUL_INITIALIZATION:

        return StaticLoggerBinder.getSingleton().getLoggerFactory();

      case NOP_FALLBACK_INITIALIZATION:

        return NOP_FALLBACK_FACTORY;

      case FAILED_INITIALIZATION:

        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);

      case ONGOING_INITIALIZATION:

        // support re-entrant behavior.

        // See also http://bugzilla.slf4j.org/show\_bug.cgi?id=106

        return TEMP_FACTORY;

    }

    throw new IllegalStateException(“Unreachable code”);

 }

当LoggerFactory成功开始化,则赶回绑定的StaticLoggerBinder中的ILoggerFactory实例;假若为NOP_FALLBACK_INITIALIZATION(未有找到桥接jar),则赶回NOPLoggerFactory,它回到1个单例的NOPLogger实例,该类不会打字与印刷任何日志;如若起首化状态为FAILED_INITIALIZATION,抛出IllegalStateException卓殊;假使开首化状态为ONGOING_INITIALIZATION,则赶回SubstituteLoggerFactory类实例,这场所产生在一个线程正在发轫化LoggerFactory,而另多少个线程已经开头请求获取ILoggerFactory实例,SubstituteLoggerFactory会记录当前呼吁的Logger名称,然后回到NOPLogger实例。全数这个在LoggerFactory初步化时被忽视的Logger
Name会在LoggerFactory开头化成功未来被report出来(在System.err流中打字与印刷出来)。

     
SLF四J达成了三个轻易易行的日记系统:slf4j-simple-<version>.jar。要贯彻七个包容SLF四J的日记系统,基本的须求八个类:

一.   
StaticLoggerBinder类,达成LoggerFactoryBinder接口。它完结单例情势,存在getSingleton()静态方法,存在REQUESTED_API_VELX570ION静态字段,不用final防止编写翻译器的优化(将值直接写入源码中,而不使用该字段)。重临的ILoggerFactory实例也一直利用同一个实例(SimpleLoggerFactory)。

public class StaticLoggerBinder implements LoggerFactoryBinder {

 private static final StaticLoggerBinder SINGLETON = new
StaticLoggerBinder();

 public static final StaticLoggerBinder getSingleton() {

    return SINGLETON;

 }

 // to avoid constant folding by the compiler, this field must *not*
be final

 public static String REQUESTED_API_VERSION = “1.6.99”; // !final

 private static final String loggerFactoryClassStr =
SimpleLoggerFactory.class.getName();

 private final ILoggerFactory loggerFactory;

 private StaticLoggerBinder() {

    loggerFactory = new SimpleLoggerFactory();

 }

 public ILoggerFactory getLoggerFactory() {

    return loggerFactory;

 }

 public String getLoggerFactoryClassStr() {

    return loggerFactoryClassStr;

 } 

}

二.   
达成ILoggerFactory接口的SimpleLoggerFactory。它有一个loggerMap字段缓存全体在此以前成立的SimpleLogger实例,以Logger
Name为key,达成各个同样名字的Logger实例只必要创制贰回。

public class SimpleLoggerFactory implements ILoggerFactory {

 final static SimpleLoggerFactory INSTANCE = new SimpleLoggerFactory();

 Map loggerMap;

 public SimpleLoggerFactory() {

    loggerMap = new HashMap();

 }

 public Logger getLogger(String name) {

    Logger slogger = null;

    // protect against concurrent access of the loggerMap

    synchronized (this) {

      slogger = (Logger) loggerMap.get(name);

      if (slogger == null) {

        slogger = new SimpleLogger(name);

        loggerMap.put(name, slogger);

      }

    }

    return slogger;

 }

}

3.   
SimpleLogger类,完结Logger接口。SimpleLogger承继自马克尔IgnoringBase类,该基类忽略全部存在马克尔参数的日记打印格局。SimpleLogger将日志等级分成多个等级:TRACE、DEBUG、INFO、WASportageN、E凯雷德RO卡宴,那一个等第对应的INT值一次增大。SimpleLogger还扶助对simplelogger.properties配置文件的分析,它帮助的key值有:

org.slf4j.simpleLogger.defaultLogLevel

org.slf4j.simpleLogger.showDateTime

org.slf4j.simpleLogger.dateTimeFormat

org.slf4j.simpleLogger.showThreadName

org.slf4j.simpleLogger.showLogName

org.slf4j.simpleLogger.showShortLogName

org.slf4j.simpleLogger.logFile

org.slf4j.simpleLogger.levelInBrackets

org.slf四j.simpleLogger.warnLevelString(warn提醒字符,私下认可“WAQX56N”)

还要SimpleLogger还帮助为特定的Logger
Name前缀(以”.”作为分隔符)钦点level:

org.slf4j.simpleLogger.log.<logNamePrefix>

并且具有那个key都足以定义在系统特性中。

SimpleLogger类的贯彻重点分为两步:伊始化和打字与印刷日志:

a.    初始化

加载配置文件,使用加载的配置文件发轫化类字段,即对应以上simplelogger.properties支持的key;保存当前Logger
Name;总结当前Logger实际的Level,即只要未有为该Logger
Name(或其以“.”分隔的前缀)配置特定的Level,则利用默许配置的Level,否则,使用具体的日记,并保存计算出的Level值,假设未有找到Level配置,使用暗中同意值INFO。

b.    打字与印刷日志

对运用format字符串的日志打字与印刷格局,调用formatAndLog()方法,其内部先调用MessageFormatter.arrayFormat()方法,然后调用log()方法达成打字与印刷新闻。log()方法的兑现只是根据分析出来的布署音信,判别什么音信要求打字与印刷,则打字与印刷这一个新闻,实现比较轻松,不再赘言。

Marker、MarkerFactory、StaticMarkerBinder以及MDC、StaticMDCBinder类

并不是有着Logging系统帮忙那几个效能,对它们协助最全面的当属LogBack框架了,由此那么些类将会在介绍LogBack框架时二头谈谈。在slf四j-simple-<version>.jar,Static马克尔Binder重回Basic马克尔Factory实例,而StaticMDCBinder重返NOPMDCAdapter实例。

其它桥接包

如slf4j-log肆j1二-<version>.jar,slf4j-jdk1四-<version>.jar,slf4j-jcl-<version>.jar等,它们的完结类似slf肆j-simple-<version>.jar的兑现,并且愈来愈简明,因此它们对Logger的贯彻将多数的逻辑代理给了底层达成框架,因而这里不再赘言。

SLF肆J与Commons Logging、Log四J之间的并行转化

SLF肆J帮助上层是SLF四J框架,底层依然经过CommonsLogging的动态查找体制,只要将slf肆j-jcl-<version>.jar包加入classpath中就可以(当然slf4j-api-<version>.jar也要存在)。

除此以外SLF4J还帮助上层是CommonsLogging,而底层交给SLF肆J提供的静态绑定机制查找真正的日志实现框架,只需求将jcl-over-slf4j-<version>.jar包参加到classpath中,此时不必要引进commons-logging-<version>.jar包。它的落到实处只是重写了CommonsLogging框架,并在LogFactory中只行使SLF四JLog或SLF肆JLocationAwareLog类。

可是要求小心,slf肆j-jcl-<version>.jar包和jcl-over-slf四j-<version>.jar五个包不可能同时出现在classpath中,不然会挑起循环调用而导致栈溢出的难点,由此slf四j-jcl-<version>.jar在开端化时就会质量评定这么些限制,并抛出尤其。

终极SLF四J还辅助Log四J作为上层,而底层交给SLF4J静态绑定要确实兑现日志打字与印刷的框架,能够将log④j-over-slf四j-<version>.jar包到场到classpath中。其完结也类似jcl-over-slf4j-<version>.jar的落成,重写大多数的Log4J的中间逻辑,而在Logger类达成中,将真的的日志打字与印刷逻辑代理给SLF四J的LoggerFactory。

最后给作者后天在商城付出的那么些系统吐个槽,我们队日志并未统1的治本,有人用CommonsLogging,也有人直接用Log四J,其实具有代码都尚未统1管理,万分换乱,可是SLF4J竟然能够知足那种情状的动员搬迁,就能够以将log四j-over-slf四j-<version>.jar和jcl-over-slf4j-<version>.jar包同时内置classpath下。而到那一年作者才发觉到为什么SLF4J为啥会日趋的行使那么周边了。