Apache Log4j2 是对Log4j 的升级版本,参考了logback 的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
官网:https://logging.apache.org/log4j/2.x/
目前市面上最主流的日志门面就是SLF4J,虽然Log4j2 也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将 Log4j2 看作是日志的实现,Slf4j + Log4j2 应该是未来的大势所趋。
1.添加依赖
<dependencies>
<!-- log4j2 日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!-- log4j2 日志实面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.编写代码
package com.log;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
public class Log4j2Test {
// 定义日志记录器对象
public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
// 快速入门
@Test
public void testQuick() throws Exception {
// 日志消息输出
LOGGER.fatal("fatal");
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
}
}
3.运行结果截图
上述报错是因为没有找到日志配置文件,需要添加 log4j2.xml 配置文件到 resource 目录下【classpath】下。log4j2 默认加载classpath 下的 log4j2.xml 文件中的配置
<?xml version="1.0" encoding="UTF-8" ?>
<configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">E:/logs</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</File>
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</RandomAccessFile>
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="E:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</configuration>
实际生产中,我们往往需要 slf4j + log4j2 进行日志管理;就需要导入slf4j 日志门面、log4j2 适配器;然后使用 slf4j 方法接口名称来输出日志
<dependencies>
<!-- 使用slf4j 作为日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!-- 使用 log4j2 的适配器进行绑定 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
</dependency>
<!-- log4j2 日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!-- log4j2 日志实面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
编写 slf4j + log4j2 代码
package com.log;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
// 快速入门
@Test
public void testQuick(){
// 日志输出
LOGGER.error("error");
LOGGER.warn("warning");
LOGGER.info("info"); // 默认的日志级别信息
LOGGER.debug("debug");
LOGGER.trace("trace"); // 追踪信息
// 使用占位符输出日志信息
String name = "java_log";
Integer age = 18;
LOGGER.info("用户:{},{}", name, age);
// 将系统的异常信息输出
try {
int i = 1 / 0;
} catch (Exception e){
// e.printStackTrace();
LOGGER.error("出现异常:", e);
}
}
}
log4j2 vs slf4j + log4j2 日志输出对比:
log4j2 默认加载classpath 下的 log4j2.xml 文件中的配置。下面通过log4j2.xml 配置文件进行测试
<?xml version="1.0" encoding="UTF-8" ?>
<!-- status="warn" 日志框架本身的输出日志级别,可以修改为debug monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5秒;生产环境中修改配置文件,是热更新,无需重启应用 -->
<configuration status="warn" monitorInterval="5">
<!-- 集中配置属性进行管理 使用时通过:${name} -->
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<!-- 日志处理 -->
<Appenders>
<!-- 控制台输出 appender,SYSTEM_OUT输出黑色,SYSTEM_ERR输出红色 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<!-- 日志文件输出 appender -->
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</File>
<!-- 使用随机读写流的日志文件输出 appender,性能提高 -->
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</RandomAccessFile>
<!-- 按照一定规则拆分的日志文件的appender --> <!-- 拆分后的文件 -->
<!-- filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log"> -->
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd}-%i.log">
<!-- 日志级别过滤器 -->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<!-- 日志消息格式 -->
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<!-- 在系统启动时,出发拆分规则,生产一个新的日志文件 -->
<OnStartupTriggeringPolicy />
<!-- 按照文件大小拆分,10MB -->
<SizeBasedTriggeringPolicy size="2MB" />
<!-- 按照时间节点拆分,规则根据filePattern定义的 -->
<TimeBasedTriggeringPolicy />
</Policies>
<!-- 在同一个目录下,文件的个限定为 30个,超过进行覆盖 -->
<DefaultRolloverStrategy max="10" />
</RollingFile>
</Appenders>
<!-- logger 定义 -->
<Loggers>
<!-- 使用 rootLogger 配置 日志级别 level="trace" -->
<Root level="trace">
<!-- 指定日志使用的处理器 -->
<!-- <AppenderRef ref="Console" />-->
<AppenderRef ref="file" />
<AppenderRef ref="rollingFile" />
<AppenderRef ref="accessFile" />
</Root>
</Loggers>
</configuration>
编写代码:
package com.log;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
// 快速入门
@Test
public void testQuick(){
// 日志输出
for (int i = 0; i < 1000000; i++) {
LOGGER.error("error");
LOGGER.warn("warning");
LOGGER.info("info"); // 默认的日志级别信息
LOGGER.debug("debug");
LOGGER.trace("trace"); // 追踪信息
}
// 使用占位符输出日志信息
/*String name = "java_log"; Integer age = 18; LOGGER.info("用户:{},{}", name, age); // 将系统的异常信息输出 try { int i = 1 / 0; } catch (Exception e){ // e.printStackTrace(); LOGGER.error("出现异常:", e); }*/
}
}
运行结果截图:
异步日志
log4j2 最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用log4j2 的异步日志。
Log4j2 提供了两种实现日志的方式,一个是通过AsyncAppender【几乎没人用】,一个是通过AsyncLogger【主要是这个】,分别对应前面我们说的Appender 组件和Logger 组件。
官网详细介绍:http://logging.apache.org/log4j/2.x/performance.html
注意:配置异步日志需要添加依赖
<!--异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
1、AsyncAppender 方式【生产上几乎不使用,因为性能低下】
<?xml version="1.0" encoding="UTF-8" ?>
<!-- status="warn" 日志框架本身的输出日志级别,可以修改为debug monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5秒;生产环境中修改配置文件,是热更新,无需重启应用 -->
<configuration status="warn" monitorInterval="5">
<!-- 集中配置属性进行管理 使用时通过:${name} -->
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<!-- 日志处理 -->
<Appenders>
<!-- 控制台输出 appender,SYSTEM_OUT输出黑色,SYSTEM_ERR输出红色 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<!-- 日志文件输出 appender -->
<File name="file" fileName="${LOG_HOME}/myfile.log">
<!--<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />-->
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
</File>
<Async name="Async">
<AppenderRef ref="file" />
</Async>
</Appenders>
<!-- logger 定义 -->
<Loggers>
<!-- 使用 rootLogger 配置 日志级别 level="trace" -->
<Root level="trace">
<!-- 指定日志使用的处理器 -->
<AppenderRef ref="Console" />
<!-- 使用异步 appender -->
<AppenderRef ref="Async" />
</Root>
</Loggers>
</configuration>
2、编写代码
package com.log;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量
public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
// 快速入门
@Test
public void testQuick(){
// 日志输出
LOGGER.error("error");
LOGGER.warn("warning");
LOGGER.info("info"); // 默认的日志级别信息
LOGGER.debug("debug");
LOGGER.trace("trace"); // 追踪信息
}
}
3、运行结果截图,下面就是异步日志输出
2、AsyncLogger 方式【生产上用得多,因为性能高】
AsyncLogger 才是log4j2 的重头戏,也是官方推荐的异步方式。它可以调用Logger.log 返回的更快。你可以有两种选择:全局异步和混合异步。
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
则此时,向控制台、文件都是异步方式日志输出
<?xml version="1.0" encoding="UTF-8" ?>
<!-- status="warn" 日志框架本身的输出日志级别,可以修改为debug monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5秒;生产环境中修改配置文件,是热更新,无需重启应用 -->
<configuration status="debug" monitorInterval="5">
<!-- 集中配置属性进行管理 使用时通过:${name} -->
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<!-- 日志处理 -->
<Appenders>
<!-- 控制台输出 appender,SYSTEM_OUT输出黑色,SYSTEM_ERR输出红色 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<!-- 日志文件输出 appender -->
<File name="file" fileName="${LOG_HOME}/myfile.log">
<!--<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />-->
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
</File>
<Async name="Async">
<AppenderRef ref="file" />
</Async>
</Appenders>
<!-- logger 定义 -->
<Loggers>
<!-- 自定义 logger 对象 includeLocation="false" 关闭日志记录的行号信息,开启的话会严重影响异步输出的性能 additivity="false" 不再继承 rootlogger对象 -->
<AsyncLogger name="com.log" level="trace" includeLocation="false" additivity="false">
<AppenderRef ref="Console" />
</AsyncLogger>
<!-- 使用 rootLogger 配置 日志级别 level="trace" -->
<Root level="trace">
<!-- 指定日志使用的处理器 -->
<AppenderRef ref="Console" />
<!-- 使用异步 appender -->
<AppenderRef ref="Async" />
</Root>
</Loggers>
</configuration>
同时,AsyncLogger 混合异动日志输出需要将log4j2.component.properties 内容进行注释【因为这个是配置AsyncLogger全局异步日志输出】
如下配置:com.log 日志是异步的, root 日志是同步的。
使用异步日志需要注意的问题:
Log4j2 最牛的地方在于异步输出日志时的性能表现,Log4j2 在多线程的环境下吞吐量与 Log4j 和 Logback 的比较如下图。下图比较中 Log4j2 有三种模式:
可以看出在前两种模式下,Log4j2 的性能较之 Log4j 和Logback有很大的优势。
无垃圾记录
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停。
许多日志库(包括以前版本的Log4j)在稳态日志记录期间分配临时对象,如日志事件对象,字符串,字符数组,字节数组等。这会对垃圾收集器造成压力并增加 GC 暂停发生的概率。
从版本2.6 开始,默认情况下 Log4j 以“无垃圾” 模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal 字段。
Log4j 2.6 中的无垃圾日志记录部分通过重用ThreadLocal 字段中的对象来实现,部分通过在将文件转换为字节时重用缓冲区来实现。
使用Log4j 2.5:内存分配速度809 MB / 秒,141个无效集合。
Log4j 2.6没有分配临时对象:0(零)垃圾回收。
有两个单独的系统属性可用于手动控制Log4j 用于避免创建临时对象的机制:
真诚的建议:如果您的应用程序是多线程的并且日志记录性能很重要,请考虑使用异步记录器。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_43842093/article/details/122074347
内容来源于网络,如有侵权,请联系作者删除!