1. 概述

日志是理解和调试程序运行时行为的利器。它能捕获并持久化关键数据,方便随时进行分析。本文将深入探讨最流行的Java日志框架——Log4j 2和Logback,以及它们的鼻祖Log4j,并简要介绍SLF4J这个为不同日志框架提供统一接口的门面库。

2. 启用日志

所有日志框架都遵循三个核心概念:Logger(日志记录器)、Appender(输出端)和Layout(布局)。在项目中启用日志通常分三步走:

  1. 添加所需依赖
  2. 配置框架
  3. 在代码中插入日志语句

接下来我们分别针对每个框架详解这些步骤。

3. Log4j 2

Log4j 2是Log4j的升级版,最吸引人的改进是支持异步日志。需要添加以下依赖:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.6.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.6.1</version>
</dependency>

3.1 配置

Log4j 2的核心配置文件是log4j2.xml。首先要配置Appender,它决定了日志消息的输出目的地(控制台、文件、Socket等)。Log4j 2提供了丰富的Appender类型,详情可查阅官方文档

简单配置示例:

<Configuration status="debug" name="baeldung" packages="">
    <Appenders>
        <Console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </Console>
    </Appenders>
</Configuration>

关键点说明:

  • name属性可自定义(如用"console"替代"stdout")
  • PatternLayout控制日志格式:
    • %d:日期格式
    • %p:日志级别
    • %m:日志消息
    • %n:换行符

启用Appender:需在<Loggers>部分添加引用:

<Loggers> 
    <Root level="error">
        <AppenderRef ref="STDOUT"/>
    </Root>
</Loggers>

3.2 文件日志

实际开发中常需要日志文件,添加File Appender:

<Appenders>
    <File name="fout" fileName="baeldung.log" append="true">
        <PatternLayout>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
        </PatternLayout>
    </File>
</Appenders>

File Appender核心参数:

  • fileName:日志文件名
  • append:默认true(追加模式)
  • PatternLayout:消息格式(同前)

启用文件日志:

<Root level="INFO">
    <AppenderRef ref="stdout" />
    <AppenderRef ref="fout"/>
</Root>

3.3 异步日志

启用异步日志需添加LMAX disruptor依赖(无锁线程通信库):

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

两种异步配置方式:

  1. 使用<AsyncRoot>替代<Root>
    <AsyncRoot level="DEBUG">
     <AppenderRef ref="stdout" />
     <AppenderRef ref="fout"/>
    </AsyncRoot>
    
  2. 设置系统属性:
    System.setProperty("Log4jContextSelector", 
     "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
    

性能优化细节可参考官方指南

3.4 使用示例

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

public class Log4jExample {
    private static Logger logger = LogManager.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.debug("Debug log message");
        logger.info("Info log message");
        logger.error("Error log message");
    }
}

输出结果:

2016-06-16 17:02:13 INFO  Info log message
2016-06-16 17:02:13 ERROR Error log message

日志级别控制:当Root level设为ERROR时:

<level value="ERROR" />

输出将变为:

2016-06-16 17:02:13 ERROR Error log message

异常记录技巧

try {
    // 可能抛出异常的代码
} catch (Exception e) {
    logger.error("Error log message", throwable); // 关键:第二个参数传异常对象
}

3.5 包级别配置

假设需要为com.baeldung.log4j2包启用TRACE级别日志:

logger.trace("Trace log message"); // 仅在特定包生效

配置方法:在<Root>前添加:

<Logger name="com.baeldung.log4j2" level="debug">
    <AppenderRef ref="stdout"/>
</Logger>

输出效果:

2016-06-16 17:02:13 TRACE Trace log message
2016-06-16 17:02:13 DEBUG Debug log message
2016-06-16 17:02:13 INFO  Info log message
2016-06-16 17:02:13 ERROR Error log message

4. Logback

Logback是Log4j原作者打造的升级版,功能更丰富(官方优势说明)。添加依赖:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>

此依赖会自动引入logback-coreslf4j-api

4.1 配置示例

<configuration>
  <!-- 控制台输出 -->
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
    </layout>
  </appender>

  <!-- 文件输出 -->
  <appender name="fout" class="ch.qos.logback.core.FileAppender">
    <file>baeldung.log</file>
    <append>false</append>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</pattern>
    </encoder>
  </appender>

  <!-- 包级别覆盖 -->
  <logger name="com.baeldung.log4j" level="TRACE"/>

  <root level="INFO">
    <appender-ref ref="stdout" />
    <appender-ref ref="fout" />
  </root>
</configuration>

4.2 SLF4J集成

Logback原生使用SLF4J作为接口,代码示例:

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

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

    public static void main(String[] args) {
        logger.debug("Debug log message");
        logger.info("Info log message");
        logger.error("Error log message");
    }
}

输出结果与Log4j 2示例完全一致

5. Log4j(经典版)

虽然Log4j 1.x已过时,但作为现代日志框架的基石仍值得了解。配置细节与Log4j 2相似。

5.1 配置

添加依赖:

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

控制台输出配置示例:

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration debug="false">

    <!-- 控制台Appender -->
    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" 
              value="%d{yyyy-MM-dd HH:mm:ss} %p %m%n" />
        </layout>
    </appender>

    <root>
        <level value="INFO" />
        <appender-ref ref="stdout" />
    </root>

</log4j:configuration>

debug="false"表示不输出Log4j自身的调试信息

5.2 使用示例

import org.apache.log4j.Logger;

public class Log4jExample {
    private static Logger logger = Logger.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.debug("Debug log message");
        logger.info("Info log message");
        logger.error("Error log message");
    }
}

6. 总结

本文通过实战示例展示了Log4j、Log4j 2和Logback的核心用法。关键要点:

  • Log4j 2:异步日志性能优异,配置灵活
  • Logback:SLF4J原生支持,功能丰富
  • Log4j:历史版本,新项目不推荐

踩坑提示

  • 日志配置文件路径错误会导致静默失败
  • 异步日志需注意Disruptor版本兼容性
  • 包级别配置时注意大小写敏感

完整代码示例见GitHub仓库


原始标题:Introduction to Java Logging