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 事件级别
级别用于识别事件的严重性。级别从最具体到最不具体进行组织
- OFF - 最具体,不记录日志
- FATAL - 严重的错误,会阻止应用程序继续运行;非常具体,很少的数据
- ERROR - 严重的错误,可能可以恢复
- WARN - 潜在的有害消息
- INFO - 信息性消息
- DEBUG - 一般的调试事件
- TRACE - 细粒度的调试消息,通常捕获通过应用程序的流程;很少具体,很多数据
- ALL - 最不具体,所有数据
下表显示了日志记录级别的工作方式。
事件级别 | 配置级别 | |||||||
---|---|---|---|---|---|---|---|---|
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 格式。
<?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
依赖项。
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;
我们导入 LogManager
和 Logger
类。
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");
我们生成信息、警告和错误消息。
<?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
这是项目结构。
<?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
文件。
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"); } }
这是主应用程序文件。 它调用几个执行某些日志记录的方法。
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
包中。 我们定义一个记录器,该记录器仅记录来自该包的消息。
<?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
这是项目结构。
<?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
依赖项。
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
类中,我们记录三条消息。
<?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
标签中定义。 它们控制发生滚动的条件。 在这里,我们使用两种策略:TimeBasedTriggeringPolicy
和 SizeBasedTriggeringPolicy
。 TimeBasedTriggeringPolicy
根据最具体的日期和时间模式启动滚动; 在我们的例子中,每小时一次。 如果日志文件的大小达到 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
文件。
<?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
依赖项。
<?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
文件。
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
方法生成信息和警告消息。
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 应用程序。
来源
在本文中,我们使用了 Log4j 库。
作者
列出所有Java教程。