zoukankan      html  css  js  c++  java
  • 探索java世界中的日志奥秘

                                      java日志简单介绍

     

     

     

    对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。JAVA领域存在多种日志框架,目前常用的日志框架包括Log4jLog4j 2Commons LoggingSlf4jLogbackJul

     

     

    一、java日志发展史

     

    二、java 常用日志框架类别介绍

     

    三、门面、实现、桥接

     

    四、commons logging vs Slf4j

     

    五、 log4j vs logback

    六、 slf4j 源码解读

     

    七、logback

     

    八、MDC

     

     

     

     

     

     

     

     

     

     

     

     

     

    一、java日志发展史

     

    1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。

     

    期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4jjava的标准库中,但Sun拒绝了。

    2002Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。

    接着,Apache推出了Jakarta Commons LoggingJCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是log4j,也可以是Java Util Logging

    后来(2006)Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于Commons Logging)Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The GenericReliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)

    现今,Java日志领域被划分为两大阵营:Commons Logging阵营和SLF4J阵营。
    Commons LoggingApache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub30000个项目,统计出了最流行的100Libraries,可以看出slf4j的发展趋势更好:

     

    Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2Log4j 2具有logback的所有特性。

     

     

    二、java常用日志框架类别介绍

     

             • Log4j Apache Log4j是一个基于Java的日志记录工具。现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。 Log4j应该说是Java领域资格最老,应用最广的日志工具。从诞生之日到现在一直广受业界欢迎。Log4j是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX系统日志等。

    Log4j 2 Apache Log4j 2apache开发的一款Log4j的升级产品。

     

    Logback 一套日志组件的实现(slf4j阵营)

            Jul (Java Util Logging),Java1.4以来的官方日志实现。JDK1.4开始,通过java.util.logging提供日志功能。它能满足基本的日志需要,但是功能没有Log4j强大,而且使用范围也没有Log4j广泛。

     

    Commons Logging Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging

    Slf4j 类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。

     

                       这个些所有的日志们都要归功于一个人       Ceki Gülcü !!!

     

     

     

     

     

     

     

    三、门面、实现、桥接

     

    经历了上述的发展,现在使用日志框架时往往会涉及三个层面的东西。 

                    

    · 门面

    · Slf4j: The simple logging facade for java.

    ·    JCL: Jakarta Commons Logging.

    · 实现类

    · log4j-1.2

    · log4j-2.x

    · logback

    · jul: java.util.logging

    ·

    · 桥接包

    · SLF4J LOG4J 12 Binding

    · JUL To SLF4J Bridge

    · JCL 1.1.1 Implemented Over SLF4J ??

    · SLF4J JDK14 Binding

    · Apache Log4j Commons Logging Bridge

    ·

     

    门面主要只负责定义接口,实现类才负责具体的编码工作。

     

    为什么要定义门面呢? 依赖接口而不依赖实现

     

    桥接包顾名思义就是桥接门面和实现类。比如SLF4J LOG4J 12 Binding这个桥接包可以使Slf4j和log4j1.2结合起来正常工作。一个已经成型的系统如果使用了这个模式,底层又想将log4j1.2换成log4j2.0实现,则只需要替换实现包为log4j2.x以及桥接包为 Log4j 2 SLF4J Binding。

     

     

                        

     

     

     

     

     

     

     

     

     

     

    四、commons logging vs Slf4j

     

    我们先看一下java日志框架之间的关系

     

    Commons LoggingSlf4j是日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)log4jLogback则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用这只需要关注接口而无需关注具体的实现,做到解耦。

    比较常用的组合使用方式是Slf4jLogback组合使用,Commons LoggingLog4j组合使用。

    Logback必须配合Slf4j使用。由于LogbackSlf4j是同一个作者,其兼容性不言而喻。(https://stackoverflow.com/questions/10117788/how-to-setup-commons-logging-to-use-logback)

     

    Commons logging实现机制

    Commons logging是通过动态查找机制,在程序运行时,使用自己的ClassLoader寻找和载入本地具体的实现。详细策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。

     

    Slf4j实现机制

    Slf4j在编译期间,静态绑定本地的LOG库。它是通过查找类路径下org.slf4j.impl.StaticLoggerBinder,然后绑定工作都在这类里面进行。

     

     

    静态绑定 & 动态绑定

    静态绑定又称编译时绑定,动态绑定又称运行时绑定。

    JCL作为第一个log接口框架,使用了基于反射的动态绑定的方法,原理很简单,预先定义好支持的log实现的工厂类的全路径到一个数组中,遍历这个数组,调用Class.forName依次尝试寻找各个log实现,如果当前class loader没找到,就去父class loader去找,直到找到任意一个实现为止。

    这种方法有致命的缺陷,这也正是SLF4J诞生的原因。Java EE的web容器,为了实现servlet规范中同一个容器中不同web app之间、web app和web容器之间的隔离,都使用的自己实现的class loader,逻辑和标准的class loader不同,导致一系列的无法正常发现log实现库的问题。

    Taxonomy of class loader problems encountered when using Jakarta Commons Logging

    这篇文章做了非常详尽的分析解释,文章的作者正是log4j和SLF4J的作者Ceki Gülcü,有兴趣的同学可以阅读。

     

    另外一个小改进:

    JCL 输出一个 debug 级别的 log:

    logger.debug("start process request, url:" + url);

    这个有什么问题呢?一般生产环境 log 级别都会设到 info 或者以上,那这条 log 是不会被输出的。然而不管会不会输出,这其中都会做一个字符串连接操作,然后生产一个新的字符串。如果这条语句在循环或者被调用很多次的函数中,就会多做很多无用的字符串连接,影响性能。

    所以 JCL 的最佳实践推荐这么写:

    if (logger.isDebugEnabled()) {

        logger.debug("start process request, url:" + url);

    }

    然而开发者常常忽略这个问题或是觉得麻烦而不愿意这么写。

     

     

    所以SLF4J提供了新的API,方便开发者使用:

    logger.debug("start process request, url:{}", url);

    这样的话,在不输出 log 的时候避免了字符串拼接的开销;在输出的时候需要做一个字符串format,代价比手工拼接字符串大一些,但是可以接受。

     

     

    五、 log4j vs logback

    logback算是log4j的升级版本 ,基本实现了所有log4j的功能。

    logback比log4j有更多的优点

    更快的实现 

    Logback的内核重写了,在一些关键执行路径上性能提升10倍以上。而且logback不仅性能提升了,初始化内存加载也更小了。 

     

    非常充分的测试 

    Logback经过了几年,数不清小时的测试。Logback的测试完全不同级别的。在作者的观点,这是简单重要的原因选择logback而不是log4j。 

     

    Logback-classic非常自然实现了SLF4j 

    Logback-classic实现了SLF4j。在使用SLF4j中,你都感觉不到logback-classic。而且因为logback-classic非常自然地实现了SLF4J,所以切换到log4j或者其他,非常容易,只需要提供成另一个jar包就OK,根本不需要去动那些通过SLF4JAPI实现的代码。 

     

    非常充分的文档 

    Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档 

     

     

     

    Filters(过滤器) 

    有些时候,需要诊断一个问题,需要打出日志。在log4j,只有降低日志级别,不过这样会打出大量的日志,会影响应用性能。在Logback,你可以继续保持那个日志级别而除掉某种特殊情况,如alice这个用户登录,她的日志将打在DEBUG级别而其他用户可以继续打在WARN级别。要实现这个功能只需加4行XML配置。

     

    SiftingAppender(一个非常多功能的Appender) 

    它可以用来分割日志文件根据任何一个给定的运行参数。如,SiftingAppender能够区别日志事件跟进用户的Session,然后每个用户会有一个日志文件。 

     

    自动压缩已经打出来的log 

    RollingFileAppender在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩是个异步过程,所以甚至对于大的日志文件,在压缩过程中应用不会受任何影响。 

     

    堆栈树带有包版本 

    Logback在打出堆栈树日志时,会带上包的数据。 

     

    自动去除旧的日志文件 

    通过设置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory属性,你可以控制已经产生日志文件的最大数量。如果设置maxHistory为12,那那些log文件超过12个月的都会被自动移除。 

     

     

    六、 slf4j源码解读

     

    我们写代码的时候是怎么打日志的呢?

     

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

     

    LoggerFactory.getLogger(getClass());

     

    这里看不到任何跟实现类有关联的代码,然而我们已经可以使用get到的logger打日志了。那么slf4j到底是怎么找到实现类的了?

     

    根据Path = “org/slf4j/impl/StaticLoggerBinder.class”去加载相应的实现类

     

     

    为什么要获取类的名字,而根据名字来获取对象呢?

    因为每个类使用的日志处理实现可能不同,iLoggerFactory中也是根据名字来判断一个类的实现方式的。

     

     

     

     

     

     

    那么这里会有一个问题,如果找到多个实现类,最终会绑定哪一个呢?

     

    The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. As of version 1.6.6, SLF4J will name the framework/implementation class it is actually bound to.

    Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J’s purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways.

    如果发现有多个实现类,那么slf4j会打印出warning信息。但是仅仅是warning而已。即使有多个实现类,slf4j也只会挑选其中一个,这个选择取决于JVM和所有其他实际因素,基本算是随机性的。同时,slf4j建议删除多余的实现类,仅仅保留一个。

     

     

     

     

     

    七、logback

     

     

    1StaticLoggerBinder 初始化并创建logFactory ()

     

     

     

    StaticLoggerBinder.init();

     

    初始化 new ContextInitializer(defaultLoggerContext).autoConfig();

     

    getLoggerFactory()

     

     

    总结一下这个过程: 

    1StaticLoggerBinder在加载的时候,会去读取配置文件,并根据配置文件对LoggerContext进行初始化 

    2、然后初始化ContextSelectorStaticBinder,在这个类内部new一个DefaultContextSelector,并把第一步中配置完毕的LoggerContext传给DefaultContextSelector 

    3、调用getLoggerFactory()方法,直接返回第一步中配置的LoggerContext,或者委托DefaultContextSelector类返回LoggerContext

     

     

    2loggerContext工厂类产出logger对象 

     

     

     

    Logger getLogger(final String name);

     

    com.darcytech.controller.LoginController

     

    Logger[com]、Logger[com.darcytec]、Logger[com.darcytech.controller]、Logger[com.darcytech.controller.LoginController] 

     

     

    总结一下创建Logger的完整流程: 

    1、如果请求ROOT logger,则直接返回root 

    2、如果请求的Logger已经存在,则直接返回 

    3、如果请求的Logger尚未创建,则从ROOT开始,级联创建所有Logger 

    4、每创建一个Logger,都要设置父子关系,继承生效级别 

    5、每创建一个Logger,都将其放入loggerCache,并将size++ 

     

     

    3、Logger

     

    transient private AppenderAttachableImpl<ILoggingEvent> aai;

    Logger是委托这个类实现AppenderAttachable接口,也是委托这个类来调用Appender组件来实际记录日志,所以这个字段是最关键的。

     

    主要方法

    getChildByName

    setLevel

    createChildByName每创建一个Logger,都要设置父子关系,继承生效级别

    info

     

     

     

     

    callAppenders

    如果子Logger和父Logger都关联了同样的Appender,则日志信息会重复记录

     

     

     

    总结一下Logger类中定义的字段和方法,是出于以下目的: 

     

    1、定义parentchildList,用于实现父子Logger的树形结构 

    2、定义createChildByName()getChildByName()方法,是供LoggerContext创建Logger 

    3、定义leveleffectiveLevelInt,是为了判定日志级别是否足够 

    4、最后,filterAndLog()buildLoggingEventAndAppend()callAppenders()appendLoopOnAppenders()方法,是Logger类的核心方法,一步步地委托AppenderAttachableImpl类来实际记录日志 

     

     

     

    4Appender

     

     

    实现类就是最常见的ConsoleAppender和FileAppender

     

    doAppend()

     

    最终writeOut()方法委托配置给它的Encoder组件来记录

     

     

     

    5、简单了解一下RollingFileAppender,rollingPolicy

    常用的RollingFileAppender, TimeBasedRollingPolicy

     

     

     

     

     

     

     

     

    八、什么是MDC

     

     

     

    MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对,当需要记录日志时,只需要从 MDC 中获取所需的信息即可。

     

     

     

    此外,对于一些线程池使用的应用场景,可能我们在最后使用结束时,需要调用clear方法来清洗将要丢弃的数据。

     

    LogbackMDCAdapter

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    整数子数组求最大和
    四则运算实现
    四则运算
    2015年大二下学期读书计划
    java变量和数据类型
    jdk的安装和java的入门概念
    数据库的设计
    多表查询
    数据约束和外键
    表数据的简单查询语句
  • 原文地址:https://www.cnblogs.com/wazqy/p/8315757.html
Copyright © 2011-2022 走看看