【一篇文章就够了】Java日志框架

我们常用的日志框架有Log4j、Log4j 2、Log4j 2和JUL(Java Util Logging)。除了日志框架,常用的还有日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用),Commons Logging和Slf4j

要想了解这些日志,就要先了解他们的历史。

  • 1996年,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员,这让 Log4j 近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入Log4j到java的标准库中,但Sun拒绝了。

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

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

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

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

  • 2012年7月,Apache眼看有被Logback反超的势头,重写了Log4j 1.x,成立了新的项目Log4j 2,Log4j 2具有Logback的所有特性。

看完这些之后是不是就理解这些框架的意义了。

日志小刀: JUL(Java Util Logging)

JUL是JDK官方提供的一个记录日志的方式,直接在JDK中就可以使用。

import java.util.logging.Logger;

public class JDKLog {
    public static void main( String[] args )
    {
        Logger logger = Logger.getLogger("JDKLog");
        logger.info("Hello World.");
    }
}

非常简单,但是又因为太简单了,所以功能不够强大,用的很少了。

日志大炮:Log4J

Log4J 有 1.X 版本和 2.X 版本,两个版本相互不兼容,官方推荐使用 2.X 版本。

使用 Log4J 框架首先需要引入依赖的包:

        <!-- Log4J -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.6.2</version>
        </dependency>

配置文件log4j2.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

如果没有配置 log4j2.xml 配置文件,那么Log4j将自动启用类似于下面的的配置文件,只输出Error级别的日志。
测试类:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jLog {
    public static void main(String args[]) {
        Logger logger = LogManager.getLogger(Log4jLog.class);
        logger.debug("Debug Level");
        logger.info("Info Level");
        logger.warn("Warn Level");
        logger.error("Error Level");
    }
}

控制台输出:

Connected to the target VM, address: '127.0.0.1:65086', transport: 'socket'
17:26:15.302 [main] INFO  log.Log4jLog - Info Level
17:26:15.316 [main] WARN  log.Log4jLog - Warn Level
17:26:15.316 [main] ERROR log.Log4jLog - Error Level
Disconnected from the target VM, address: '127.0.0.1:65086', transport: 'socket'

Process finished with exit code 0

因为 Log4J 有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,所以可以很好地记录不同业务问题。因为这些优点,所以在几年前几乎所有人都使用 Log4J 作为日志记录框架,群众基础可谓非常深厚。但 Log4J 本身也存在一些缺点,比如不支持使用占位符,不利于代码阅读等缺点。

LogBack:日志火箭

LogBack 其实可以说是 Log4j 的进化版,因为它们两个都是同一个人(Ceki Gülcü)设计的开源日志组件。

使用 LogBack 需要首先引入依赖:

<!-- LogBack -->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>

配置文件log4j2.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
    <logger name="com.chanshuyi" level="TRACE"/>
 
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

如果没有配置 log4j2.xml 配置文件,那么Log4j将自动启用类似于下面的的配置文件,只输出Error级别的日志。
测试类:

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

public class LogBackLog {

    static final Logger logger = LoggerFactory.getLogger(LogBackLog.class);
    public static void main(String[] args) {
        logger.trace("Trace Level.");
        logger.debug("Debug Level.");
        logger.info("Info Level.");
        logger.warn("Warn Level.");
        logger.error("Error Level.");
    }
}

适配器:SLF4J

上面说了 JDKLog、Log4J、LogBack 这几个常用的日志记录框架,它们都有各自的优缺点,适合在不同的场景下使用。可能简单的项目直接用 JDKLog 就可以了,而复杂的项目需要用上 Log4J。

很多时候我们做项目都是从简单到复杂,也就是我们很可能一开始使用的是 JDKLog,之后业务复杂了需要使用 Log4J,这时候我们如何将原来写好的日志用新的日志框架输出呢?

一个最死板的方法就是一行行代码修改,把之前用 JDKLog 的日志代码全部修改成 Log4J 的日志接口。但是这种方式不仅效率低下,而且做的工作都是重复性的工作,这怎么能忍呢。

正式因为在实际的项目应用中,有时候可能会从一个日志框架切换到另外一个日志框架的需求,这时候往往需要在代码上进行很大的改动。为了避免切换日志组件时要改动代码,这时候一个叫做 SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)的东西出现了。

SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)是一个日志的接口规范,它对用户提供了统一的日志接口,屏蔽了不同日志组件的差异。这样我们在编写代码的时候只需要看 SLF4J 这个接口文档即可,不需要去理会不同日之框架的区别。而当我们需要更换日志组件的时候,我们只需要更换一个具体的日志组件Jar包就可以了。

而整合 SLF4J 和日志框架使用也是一件很简单的事情。

SLF4J+JUL

SLF4J+JUL 需要在 Maven 中导入以下依赖包:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>1.7.21</version>
</dependency>

SLF4J+LOG4J

SLF4J+LOG4J 需要在 Maven 中导入以下依赖包有 slf4j-api.jar、slf4j-412.jar、log4j.jar :

<!-- 2.SLF4J + Log4J -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

SLF4J+LogBack

SLF4J+LogBack 需要在 Maven 中导入以下依赖包:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>1.1.7</version>
</dependency>

logback 常用配置文件

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true" scan="true" scanPeriod="30 seconds"> 

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>
        
        <!-- 常用的Pattern变量,大家可打开该pattern进行输出观察 -->
        <!-- 
          <pattern>
              %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
              Logger: %logger
              Class: %class
              File: %file
              Caller: %caller
              Line: %line
              Message: %m
              Method: %M
              Relative: %relative
              Thread: %thread
              Exception: %ex
              xException: %xEx
              nopException: %nopex
              rException: %rEx
              Marker: %marker
              %n
              
          </pattern>
           -->
    </encoder>
  </appender>
  
  <!-- 按日期区分的滚动日志 -->
  <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/error.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>ERROR</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>

      <!-- keep 30 days' worth of history -->
      <maxHistory>30</maxHistory>
    </rollingPolicy>
  </appender>
  
  <!-- 按文件大小区分的滚动日志 -->
  <appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/info.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
    
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>info.%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>
    
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>5MB</maxFileSize>
    </triggeringPolicy>
    
  </appender>
  
  
  <!-- 按日期和大小区分的滚动日志 -->
  <appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/debug.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>DEBUG</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
   <!-- 级别阀值过滤 -->
  <appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/sum.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
    <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>

      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
  <root level="debug">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="ERROR-OUT" />
    <appender-ref ref="INFO-OUT" />
    <appender-ref ref="DEBUG-OUT" />
    <appender-ref ref="SUM-OUT" />
  </root>
</configuration>

参考文献

https://www.cnblogs.com/chanshuyi/p/something_about_java_log_framework.html
https://www.cnblogs.com/chenhongliang/p/5312517.html
https://segmentfault.com/a/1190000021589244
https://segmentfault.com/a/1190000006925098?utm_source=sf-similar-article

posted @ 2021-06-18 16:01  朱李洛克  阅读(292)  评论(0编辑  收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css