ZetCode

Java Log4j

最后修改于 2024 年 1 月 27 日

Java Log4j 教程定义了日志记录,介绍了 Log4j 库,并通过几个代码示例演示了日志记录。

日志记录

日志记录是将信息写入日志文件的过程。日志文件包含有关操作系统、软件或通信中发生的各种事件的信息。

日志记录的目的

日志记录的目的如下:

日志记录不仅限于识别软件开发中的错误。它还用于检测安全事件、监控策略违规、在出现问题时提供信息、查找应用程序瓶颈或生成使用数据。

记录哪些事件

应记录的事件包括输入验证失败、身份验证和授权失败、应用程序错误、配置更改以及应用程序启动和关闭。

不记录哪些事件

不应记录的事件包括应用程序源代码、会话标识值、访问令牌、敏感个人数据、密码、数据库连接字符串、加密密钥、银行账户和持卡人数据。

日志记录最佳实践

以下是一些日志记录的最佳实践:

Log4j

Apache Log4j 是一个基于 Java 的日志记录实用程序。它是 Apache 软件基金会的项目。Log4j 可以通过 Java 代码或配置文件进行配置。配置文件可以用 XML、JSON、YAML 或 properties 文件格式编写。

Log4j 组件

Log4j 有三个主要组件:记录器 (loggers)、附加器 (appenders) 和布局 (layouts)。记录器是被命名的目的地,用于捕获日志消息并将其发送到附加器。附加器将日志消息传递到其目的地,例如文件、套接字或控制台。布局用于定义日志消息的格式。

根记录器

Log4j 有一个特定的内置记录器,称为根记录器。它位于层次结构的顶部,并且始终存在,即使未配置。它为应用程序中的所有类写入消息。如果我们不希望将来自特定记录器的消息传递到根记录器,我们将记录器的 additivity 属性更改为 false

特定包的日志记录

我们可能希望将日志记录限制为某些 Java 包。对于 XML 配置,我们使用 name 属性设置特定包的日志记录。

<Logger name="com.zetcode.work" level="info" additivity="false" >
    <AppenderRef ref="MyFile" />
</Logger>

使用此记录器,我们将 com.zetcode.work 包中的信息级别事件消息传递到日志文件目标。将 additivity 设置为 false 后,消息不会传播到根记录器。

Log4j 事件级别

级别用于识别事件的严重性。级别从最具体到最不具体进行组织

下表显示了日志记录级别的工作方式。

日志记录级别规则
事件级别 配置级别
ALL TRACE DEBUG INFO WARN ERROR FATAL OFF
ALL
TRACE
DEBUG
INFO
WARN
ERROR
FATAL

该表说明了事件和配置级别的工作方式。 如果我们在 Debug 级别记录一条消息,并且配置为 Warn,则该消息不会传递。 如果我们在 Info 级别记录一条消息,并且配置级别为 Debug,则该消息将传递到其目的地。

Log4j 基本示例

在第一个示例中,我们为简单的 Java 控制台应用程序设置 Log4j。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           └── JavaLog4jEx.java
│   └── resources
│       └── log4j2.xml
└── test
    └── java

这是项目结构。 Log4j 配置文件位于 src/main/resources 目录中。 我们使用 XML 格式。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JavaLog4jEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.13.1</version>
        </dependency>

    </dependencies>
    
</project>

这是 Maven pom.xml 文件。 我们包含 log4j-core 依赖项。

com/zetcode/JavaLog4jEx.java
package com.zetcode;

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

public class JavaLog4jEx {

    private static final Logger logger = LogManager.getLogger(JavaLog4jEx.class);
    
    public static void main(String[] args) {
        
        logger.info("The main() method is called");
        
        doWork();
        
        logger.warn("Warning message");
        logger.error("Error message");
    }
    
    public static void doWork() {
        
        // doing some work
        
        logger.info("The doWork() method is called");
    }
}

这是一个简单的 Java 控制台示例。

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

我们导入 LogManagerLogger 类。

private static final Logger logger = LogManager.getLogger(JavaLog4jEx.class);

LogManager 中,我们获取记录器。

logger.info("The main() method is called");

doWork();

logger.warn("Warning message");
logger.error("Error message");

我们生成信息、警告和错误消息。

resources/log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${layout}" />
        </Console>     

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false" >
            <AppenderRef ref="Console" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

Log4j 在 log4j2.xml 中配置。 我们选择了 XML 文件格式。

<Properties>
    <Property name="layout">%d [%t] %-5level %logger - %m%n</Property>
</Properties>

Properties 标签中,我们设置日志记录目录和布局。 布局定义了日志消息的格式。

模式布局由转换说明符组成。 每个说明符都以百分号开头,后跟可选的格式修饰符和强制转换字符。 %d 输出日志事件的日期。 %t 输出生成日志事件的线程的名称。 %-5level 输出日志事件的级别,其中级别名称至少有五个字符,并且字符是左对齐的。 %logger 输出发布日志事件的记录器的名称。 %m 打印与日志事件关联的应用程序消息,%n 是平台相关的行分隔符字符或字符。

<Appenders>

    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="${layout}" />
    </Console>     

</Appenders>

附加器是定义将日志消息发送到何处的对象。 我们定义一个控制台附加器; 它使用上述布局将消息写入标准输出。

<Loggers>

    <Logger name="com.zetcode" level="info" additivity="false" >
        <AppenderRef ref="Console" />
    </Logger>

    <Root level="error">
        <AppenderRef ref="Console" />
    </Root>    

</Loggers>

我们有两个记录器。 com.zetcode 记录器具有 info 级别,而根记录器具有 error 级别。 两个记录器都使用 Console 附加器,即将消息传递到控制台。 将 additivity 设置为 false 后,com.zetcode 的消息不会传播到根记录器。 换句话说,消息不会两次打印到控制台。

2020-03-29 18:37:11,775 [main] INFO  com.zetcode.JavaLog4jEx - The main() method is called
2020-03-29 18:37:11,780 [main] INFO  com.zetcode.JavaLog4jEx - The doWork() method is called
2020-03-29 18:37:11,780 [main] WARN  com.zetcode.JavaLog4jEx - Warning message
2020-03-29 18:37:11,780 [main] ERROR com.zetcode.JavaLog4jEx - Error message

运行该示例后,我们在控制台中收到以下消息。

Log4j 基本示例 II

在下一个示例中,我们将解释 Log4j 的其他功能。 我们将把消息写入文件并定义一个特定于包的记录器。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── main
│   │           │   └── JavaLog4jEx2.java
│   │           └── work
│   │               └── MyWork.java
│   └── resources
│       └── log4j2.xml
└── test
    └── java

这是项目结构。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JavaLog4jEx2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.13.1</version>
        </dependency>

    </dependencies>

</project>

这是 pom.xml 文件。

com/zetcode/main/JavaLog4jEx2.java
package com.zetcode.main;

import com.zetcode.work.MyWork;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


public class JavaLog4jEx2 {

    private static final Logger logger = LogManager.getLogger(JavaLog4jEx2.class);
    
    public static void main(String[] args) {
        
        logger.info("The main() method is called");
        
        doJob();
        
        MyWork mw = new MyWork();
        mw.doMyWork();
    }
    
    public static void doJob() {
        
        // doing some job
        
        logger.info("The doJob() method is called");
    }
}

这是主应用程序文件。 它调用几个执行某些日志记录的方法。

com/zetcode/work/MyWork.java
package com.zetcode.work;

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

public class MyWork {

    private static final Logger logger = LogManager.getLogger(MyWork.class);
    
    public void doMyWork() {
        
        // doing some work
        
        logger.info("doMyWork() method called");
    }
}

我们有一个简单的方法,用于记录信息消息。 它的类位于 com.zetcode.work 包中。 我们定义一个记录器,该记录器仅记录来自该包的消息。

resources/log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>     
        
        <File name="MyFile" fileName="/home/user7/tmp/mylog.log" append="false">
            <PatternLayout pattern="${layout}"/>
        </File>        

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode.work" level="info" additivity="false" >
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

log4j2.xml 配置文件中,我们定义两个附加器和两个记录器。

<File name="MyFile" fileName="/home/user7/tmp/mylog.log" append="false">
    <PatternLayout pattern="${layout}"/>
</File>   

我们定义一个文件附加器,该附加器将日志消息写入指定的文件。 文件名由 fileName 属性指定。 将 append 属性设置为 false 后,该文件始终被覆盖。

<Logger name="com.zetcode.work" level="info" additivity="false" >
    <AppenderRef ref="MyFile" />
</Logger>

我们定义一个记录器,该记录器记录来自 com.zetcode.work 包的信息消息。 记录器将消息写入文件。

<Root level="info">
    <AppenderRef ref="Console" />
</Root>

消息的其余部分(在本例中为来自 com.zetcode.main 包的消息)由根记录器处理。

2020-03-29 18:47:07,372 [main] INFO  com.zetcode.main.JavaLog4jEx2 - The main() method is called
2020-03-29 18:47:07,376 [main] INFO  com.zetcode.main.JavaLog4jEx2 - The doJob() method is called

这两条消息已写入控制台。

$ cat mylog.log 
2020-03-29 18:47:07,377 [main] INFO  com.zetcode.work.MyWork - doMyWork() method called

此消息已写入 mylog.log 文件。

Log4j RollingFileAppender

RollingFileAppender 是一种特殊类型的附加器,当日志文件达到一定大小或满足时间标准时,它会备份日志文件。 滚动文件附加器会自动滚动或存档当前日志文件,并在新文件中恢复日志记录。

以下应用程序使用 RollingFileAppender

pom.xml
src
└── main
    ├── java
    │   └── com
    │       └── zetcode
    │           └── JavaLog4jRollingFileEx.java
    └── resources
        └── log4j2.xml

这是项目结构。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JavaLog4jRollingFileEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.13.1</version>
        </dependency>

    </dependencies>    

</project>

这是 pom.xml 文件,其中包含 log4j-core 依赖项。

com/zetcode/JavaLog4jRollingFileEx.java
package com.zetcode;

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

public class JavaLog4jRollingFileEx {

    private static final Logger logger = LogManager.getLogger(
        JavaLog4jRollingFileEx.class);
    
    public static void main(String[] args) {
        
        logger.info("Information message");
        logger.warn("Warning message");
        logger.error("Error message");
    }
}

JavaLog4jRollingFileEx 类中,我们记录三条消息。

resources/log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="logdir">/home/user7/tmp</Property>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
        
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>           
  
        <RollingFile name="MyFile" fileName="${logdir}/app.log"
                     filePattern="${logdir}/app.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${layout}" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="1 MB" />
            </Policies>
            <DefaultRolloverStrategy max="10" />
        </RollingFile>

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false">
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>

</Configuration>

Log4j 在 log4j2.xml 中配置。

<RollingFile name="MyFile" fileName="${logdir}/app.log"
                filePattern="${logdir}/app.%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${layout}" />
...
    <DefaultRolloverStrategy max="10" />
</RollingFile>

使用 RollingFile 标签创建一个滚动文件附加器。 我们使用 fileName 属性设置日志文件的位置。 PatternLayout 设置日志消息的布局。 如果存档数量达到十个,则 DefaultRolloverStrategy 会删除较旧的存档。

<Policies>
  <TimeBasedTriggeringPolicy />
  <SizeBasedTriggeringPolicy size="1 MB" />
</Policies>

触发策略在 Policies 标签中定义。 它们控制发生滚动的条件。 在这里,我们使用两种策略:TimeBasedTriggeringPolicySizeBasedTriggeringPolicyTimeBasedTriggeringPolicy 根据最具体的日期和时间模式启动滚动; 在我们的例子中,每小时一次。 如果日志文件的大小达到 1 MB,则 SizeBasedTriggeringPolicy 启动滚动。

<Loggers>

    <Logger name="com.zetcode" level="info" additivity="false">
        <AppenderRef ref="MyFile" />
    </Logger>

    <Root level="error">
        <AppenderRef ref="Console" />
    </Root>    

</Loggers>

我们定义了两个记录器。 com.zetcode 记录器将日志记录到文件附加器中。 根记录器未在此应用程序中使用。

$ cat app.log 
2020-03-29 18:55:46,101 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2020-03-29 18:55:46,106 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2020-03-29 18:55:46,106 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2020-03-29 18:56:07,533 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2020-03-29 18:56:07,537 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2020-03-29 18:56:07,537 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2020-03-29 18:56:11,000 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2020-03-29 18:56:11,004 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2020-03-29 18:56:11,005 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2020-03-29 18:56:13,958 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2020-03-29 18:56:13,962 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2020-03-29 18:56:13,962 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2020-03-29 18:56:16,676 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2020-03-29 18:56:16,680 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2020-03-29 18:56:16,680 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message

这是日志文件的示例输出。

Log4j 与 Spring Boot

下一个示例演示如何在 Spring Boot 应用程序中使用 Log4j。 该应用程序是一个控制台 Java 程序。

Spring Boot 默认使用 Logback 进行日志记录。 因此,我们需要配置 Spring Boot 以排除 Logback 并包含 Log4j。

常规日志记录设置在 application.properties 文件中设置。 要配置日志记录系统的更精细的设置,我们需要使用本机配置格式。 在我们的例子中,是 Log4j 的设置。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           └── MyRunner.java
│   └── resources
│       ├── app.log
│       └── log4j2.xml
└── test
    └── java

这是项目结构。 日志消息将写入 app.log 文件。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JavaLog4jSpringBootEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
   
    <properties>
        <java.version>11</java.version>
    </properties>
    
    <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-log4j2</artifactId>
        </dependency>     

    </dependencies>    

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>            
        </plugins>
    </build>      
    
</project>

pom.xml 文件中,我们排除 spring-boot-starter-logging 依赖项,并添加 spring-boot-starter-log4j2 依赖项。

resources/log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>     
        
        <File name="MyFile" fileName="src/main/resources/app.log">
            <PatternLayout pattern="${layout}" />
        </File>        

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false" >
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

Spring Boot 在 src/main/resources 目录中查找 log4j2.xml 配置文件。

<File name="MyFile" fileName="src/main/resources/app.log">
    <PatternLayout pattern="${layout}" />
</File> 

日志消息将写入 src/main/resources/app.log 文件。

com/zetcode/MyRunner.java
package com.zetcode;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LogManager.getLogger(MyRunner.class);

    @Override
    public void run(String... args) throws Exception {

        logger.info("Information message");
        logger.warn("Warning message");
    }
}

这是我们的命令行运行器。 run 方法生成信息和警告消息。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 类中,我们设置 Spring Boot 应用程序。

来源

Apache Log4j

在本文中,我们使用了 Log4j 库。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程