美文网首页
Java日志体系

Java日志体系

作者: 贪睡的企鹅 | 来源:发表于2019-08-19 17:43 被阅读0次

    1 日志系统

    在应用程序的开发中,通常会使用日志记录监视代码中变量的变化情况,输出到控制台,文件系统或者网络。记录日志目的在于线上问题追踪,作为其他系统进行统计分析的"数据".

    2 Java常用日志框架

    Java 的日志框架有很多,比如:JUL(Java Util Logging)、Log4j、Logback、Log4j2、Tinylog 等。除此之外,还有 JCL(Apache Commons Logging)和 SLF4J 这样的“门面日志”

    2.1 门面日志

    什么是“门面日志”。“门面日志”利用了设计模式中的门面模式思想,对外提供一套通用的日志记录的 API,而不提供具体的日志输出服务,如果要实现日志输出,需要集成其他的日志框架,比如 Log4j、Logback、Log4j2 等。

    这种门面模式的好处在于,记录日志的 API 和日志输出的服务分离开,代码里面只需要关注记录日志的 API,通过 SLF4J 指定的接口记录日志;而日志输出通过引入 JAR 包的方式即可指定其他的日志框架。当我们需要改变系统的日志输出服务时,不用修改代码,只需要改变引入日志输出框架 JAR 包。

    2.2 日志框架的历史
    • 1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j(作者Ceki Gülcü)。后来Log4j成为Apache基金会项目中的一员。
    • 期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但Sun拒绝了.
    • 2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。但是如果有人想换成其他日志组件,如log4j换成JUL,因为api完全不同,就需要改动代码。
    • Apache见此,开发了JCL(Jakarta Commons Logging),即commons-logging-xx.jar。它只提供一套通用的日志接口api,并不提供日志的实现。很好的设计原则嘛,依赖抽象而非实现。这样应用程序可以在运行时选择自己想要的日志实现组件。
    • 这样看上去也挺美好的,但是log4j的作者觉得JCL不好用,自己开发出slf4j,它跟JCL类似,本身不替供日志具体实现,只对外提供接口或门面。目的就是为了替代JCL。同时,还开发出logback,一个比log4j拥有更高性能的组件,目的是为了替代log4j。
    • Apache参考了logback,并做了一系列优化,推出了log4j2

    3 commons-logging

    3.1 概述

    Apache Commons Logging是一个门面日。代码里面只需要关注记录日志的 API,通过JCL指定的接口记录日志;而日志输出通过引入 JAR 包的方式实现。如果没有引入日志框架,Apache Commons Logging会使用内部默认实现 SimpleLog

    JUL最后更新于2014年7月,现以停止更新。

    image

    3.2 包结构

    image
    • Log:日志对象接口,封装了操作日志的方法,定义了日志操作的5个级别:trace < debug < info < warn < error
    • LogFactory:抽象类,日志工厂,获取日志类;
    • LogFactoryImpl:LogFactory的实现类,真正获取日志对象的地方;
    • Log4JLogger:对log4j的日志对象封装;
    • Jdk14Logger:对JDK1.4的日志对象封装;
    • Jdk13LumberjackLogger:对JDK1.3以及以前版本的日志对象封装;
    • SimpleLog:commons-logging自带日志对象;

    3.2 日志框架加载

    jcl可以通过在ClassPath下创建commons-logging.properties配置文件指定加载日志实现框架。

    #指定日志对象:
    org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger
    #指定日志工厂:
    org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl
    

    jcl如果没有指定日志实现框架则默认加载时按照顺序log4j>jul> simpleLog依次加载,如果查找到log4j包则使用log4j,如果没有依次类推。

    3.3 JCL使用

    引用JCL依赖包

        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
    

    测试代码

    此时由于项目也没有放入其他日志框架依赖包,会按照顺序log4j>jul> simpleLog加载,因此此时会使用jdk自带jul实现日志打印。

    public class commons_loggingDemo {
        Log log= LogFactory.getLog(commons_loggingDemo.class);
        @Test
        public void test() throws IOException {
            log.debug("Debug info.");
            log.info("Info info");
            log.warn("Warn info");
            log.error("Error info");
            log.fatal("Fatal info");
        }
    }
    

    引用log4j依赖包

    在不修改代码的前提下,引入log4j依赖包。在次调用测试代码,此时会使用log4j实现日志打印。

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>
    

    4 SLF4J

    4.1 概述

    SLF4J同样是一个门面日志。代码里面只需要关注记录日志的 API,通过SLF4J指定的接口记录日志;而日志输出通过引入 JAR 包的方式实现。

    image
    • slf4j-api.jar 表示SLF4J提供的日志接口相关的jar包

    • natvie implementation of slf4j-api: 表示SLF4J本地底层日志框架,其中包括logback-classic.jar + logback-core.jar作为logback日志框架底层实现,SLF4J-Simple作为SLF4J简单日志框架实现,slf4j-nop.jar表示空实现。

    • none-natvie implementation of slf4j-api :表示非本地,外部日志框架。其中包括lo4j.jar表示log4j日志框架,JUL表示JDK自带日志框架,log4j-core.jar表示log4j2日志框架(图中没画)

    • Adaptation layer:表示外部日志框架的适配器

    组合如下

    • 使用logback作为日志框架
      • slf4j-api.jar + logback-classic.jar + logback-core.jar
    • 使用log4j作为日志框架
      • slf4j-api.jar + slf4j-log4j12.jar + log4j.jar
    • 使用jul作为日志框架
      • slf4j-api.jar + slf4j-jdk14.jar
    • 使用log4j2作为日志框架
      • slf4j-api.jar + +log4j-slf4j-impl+log4j-core
    • 只用slf4j无日志实现
      • slf4j-api.jar + slf4j-nop.jar
    4.2 桥接器

    在实际开发过程中通常会遇到下面的问题,项目中旧模块使用的是log4j来打印日志的话,而新模块想用logback来统一打印日志的话,由于两者并不兼容,如果直接修改业务代码会存在一定风险。那么该如何解决呢?

    SLFJ提供了各种日志框架的桥接器,我们只需要引用对应适配器的实现包,并去掉原始的依赖包,SLFJ会自动帮我们完成日志框架的统一。上面案例中可以去掉log4j包log4j.jar引入log4j-over-slf4j包(这个包使之前代码使用log4j不会因为找不到而报错,并会将打印日志桥接给slf4j-api.jar),并同时引入slf4j-api.jar + logback-classic.jar + logback-core.jar。

    image

    SLFJ提供的桥接器

    image
    • JCL桥接器
      • 使用jcl-over-slf4j.jar
    • log4j桥接器
      • 使用log4j-over-slf4j.jar
    • JUL桥接器
      • 使用jul-to-slf4j.jar
    4.3 小结
    image
    4.4 使用

    springBoot默认使用logback作为日志框架,使用slf4j作为门面日志。

    使用springBoot+slf4j+logback

    <dependencyManagement>
        <dependencies>
          <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.18.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <exclusions>
            <exclusion>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
      </dependencies>
    
    springbootSJ4J默认Logback.jpg

    这里已经帮助我们添加各种类型日志桥接器,如果我们我们向将原来项目中日志框架去掉,并使用Logback框架时。只需要去掉原先依赖。

    使用springBoot+slf4j+log4j

    这里需要添加spring-boot-starter-log4j依赖,并将spring-boot-starter-logging从依赖中排除。

    <dependencyManagement>
        <dependencies>
          <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.18.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <exclusions>
            <exclusion>
              <artifactId>spring-boot-starter-logging</artifactId>
              <groupId>org.springframework.boot</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-log4j</artifactId>
          <version>1.3.8.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
      </dependencies>
    
    image

    同样这里已经帮助我们添加各种类型日志桥接器,如果我们我们向将原来项目中日志框架去掉,并使用logb4j框架时。只需要去掉原先依赖。

    使用springBoot+slf4j+log4j2

    这里需要添加spring-boot-starter-log4j2依赖,并将spring-boot-starter-logging从依赖中排除。

    <dependencyManagement>
        <dependencies>
          <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.18.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <exclusions>
            <exclusion>
              <artifactId>spring-boot-starter-logging</artifactId>
              <groupId>org.springframework.boot</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
      </dependencies>
    
    image

    同样这里已经帮助我们添加各种类型日志桥接器,如果我们我们向将原来项目中日志框架去掉,并使用logb4j框架时。只需要去掉原先依赖。

    关于其他使用日志框架可以排除spring-boot-starter-logging后自行添加就不再详诉。

    测试代码

    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    
    public class Sj4jTest {
    
        Logger logger = LoggerFactory.getLogger("Sj4jTest");
        
        @Test
        public void test() throws IOException {
            logger.debug("Debug info.");
            logger.info("Info info");
            logger.warn("Warn info");
            logger.error("Error info");
        }
    }
    

    相关文章

      网友评论

          本文标题:Java日志体系

          本文链接:https://www.haomeiwen.com/subject/mtuesctx.html