Java日志体系

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月,现以停止更新。

Java日志体系_第1张图片
image

3.2 包结构

Java日志体系_第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依赖包

    
      commons-logging
      commons-logging
      1.2
    

测试代码

此时由于项目也没有放入其他日志框架依赖包,会按照顺序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实现日志打印。


    log4j
    log4j
    1.2.16

4 SLF4J

4.1 概述

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

Java日志体系_第3张图片
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。

Java日志体系_第4张图片
image

SLFJ提供的桥接器

Java日志体系_第5张图片
image
  • JCL桥接器
    • 使用jcl-over-slf4j.jar
  • log4j桥接器
    • 使用log4j-over-slf4j.jar
  • JUL桥接器
    • 使用jul-to-slf4j.jar
4.3 小结
Java日志体系_第6张图片
image
4.4 使用

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

使用springBoot+slf4j+logback


    
      
        
        org.springframework.boot
        spring-boot-dependencies
        1.5.18.RELEASE
        pom
        import
      
    
  
  
    
      org.springframework.boot
      spring-boot-starter
      
        
          org.springframework.boot
          spring-boot-starter-logging
        
      
    
    
      org.springframework.boot
      spring-boot-starter-logging
    
    
      org.springframework.boot
      spring-boot-starter-test
    
  
Java日志体系_第7张图片
springbootSJ4J默认Logback.jpg

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

使用springBoot+slf4j+log4j

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


    
      
        
        org.springframework.boot
        spring-boot-dependencies
        1.5.18.RELEASE
        pom
        import
      
    
  
  
    
      org.springframework.boot
      spring-boot-starter
      
        
          spring-boot-starter-logging
          org.springframework.boot
        
      
    
    
      org.springframework.boot
      spring-boot-starter-log4j
      1.3.8.RELEASE
    
    
      org.springframework.boot
      spring-boot-starter-test
    
  
Java日志体系_第8张图片
image

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

使用springBoot+slf4j+log4j2

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


    
      
        
        org.springframework.boot
        spring-boot-dependencies
        1.5.18.RELEASE
        pom
        import
      
    
  
  
    
      org.springframework.boot
      spring-boot-starter
      
        
          spring-boot-starter-logging
          org.springframework.boot
        
      
    
    
      org.springframework.boot
      spring-boot-starter-log4j2
    
    
      org.springframework.boot
      spring-boot-starter-test
    
  
Java日志体系_第9张图片
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日志体系)