Log4j2日志框架

2023-10-31

Log4j2日志框架

1、简介及入门示例

1、背景介绍

官网地址:https://logging.apache.org/log4j/2.x/、Maven 仓库地址:https://search.maven.org/artifact/org.apache.logging.log4j/log4j-api

Log4j2是对Log4j1x的升级版,同时修复与升级了Logback的不足,被誉为是目前最优秀的Java日志框架。Apache Log4j2日志框架的主要特征:

  • 自动重新加载配置:参考了Logback的设计,提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而无需重启应用
  • 异常处理:在logback中,Appender中的异常不会被应用感知到,但是在Log4j2中,提供了一些异常处理机制
  • 性能提升:log4j2相较于log4j和logback都具有很明显的性能提升,据官方测试,异步记录器的吞吐量比Log4j 1.x 和 Logback高18倍,延迟低
  • 无垃圾机制:(默认开启)log4j2在大部分情况下,都可以使用无垃圾机制【对象重用、内存缓冲】,避免频繁的日志收集导致的 jvm gc
  • 插件架构:Log4j2使用插件模式配置组件。因为无需编写代码来创建和配置Appender、Layout、Pattern Converyer等。在配置了的情况下,Log4j2自动识别插件并使用他们
  • 高级过滤:与Logback一样,Log4j2支持基于Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。此外,过滤器还可以与记录器关联。与Logback不同,Log4j2可以在任何这些情况下使用通用的Filter类

Log4j2 除了提供日志实现以外,也拥有一套自己的独立的门面。不过目前市面上最主流的日志门面是SLF4J,虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2看作是日志的实现,SLF4J + Log4j2 的组合是市场上最强大的日志功能实现方式,绝对是主流日志框架。

2、模块介绍

Log4j2中分为:门面(log4j-api.jar)和实现 (log4j-core.jar) 两个模块。门面与SLF4J是一个类型,属于日志抽象门面。而实现部分才是Log4j2的核心:

  • 日志门面:org.apache.logging.log4j » log4j-api
  • 日志实现:org.apache.logging.log4j » log4j-core

Log4j2 重要类的概念:

  • org.apache.logging.log4j.LogManager:日志类工厂,日志管理器,提供静态方法getContext,getFactory,getLogger,可以方便的获取相关信息
  • org.apache.logging.log4j.Logger:日志记录类,用来输出被记录的日志
  • org.apache.logging.log4j.core.LoggerContext:日志上下文,包含已配置的Configuration、Appender,Logger,Filter等信息
  • org.apache.logging.log4j.core.config.Configuration:每一个LoggerContext都有一个有效的Configuration。Configuration包含了所有的Appenders、上下文范围内的过滤器、LoggerConfigs。在重配置期间,新与旧的Configuration将同时存在。当所有的Logger对象都被重定向到新的Configuration对象后,旧的Configuration对象将被停用和丢弃

3、入门案例

1、使用Log4j做日志门面

<!-- log4j2日志门面 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.2</version>
</dependency>
<!-- log4j2日志实面,log4j-api在log4j-core中已经有依赖了,单独依赖core即可 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.2</version>
</dependency>
package com.xyz;

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

public class Log4j2Test {
    public static void main(String[] args) {
        // 定义日志记录器对象,如下没有使用SLF4J门面技术,完全使用的log4j中的类和方法
        Logger logger = LogManager.getLogger(Log4j2Test.class);
        // log4j2中存在6中日志输出级别
        logger.fatal("fatal");
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");
    }
}
12:10:00.727 [main] FATAL com.xyz.Log4j2Test - fatal
12:10:00.727 [main] ERROR com.xyz.Log4j2Test - error

此时可以发现只输出了error及以上级别的日志,因为Log4j2门面默认是error级别,就说明此时这个日志是使用Log4j2框架实现的。

2、使用SLF4J作为日志的门面

<!-- 导入slf4j日志门面依赖 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.36</version>
</dependency>
<!-- log4j适配器 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.17.2</version>
</dependency>
<!-- log4j2日志门面 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.2</version>
</dependency>
<!-- log4j2日志实面,log4j-api在log4j-core中已经有依赖了,单独依赖core即可 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.2</version>
</dependency>
package com.xyz;

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

public class Log4j2Test {
    public static void main(String[] args) {
        // 注意:此时使用的slf4j的门面技术,没有适应到log4j2,输出的是 slf4j的日志级别(有5个级别)
        Logger logger = LoggerFactory.getLogger(Log4j2Test.class);
        // slf4j中存在5种日志输出级别,此时使用是slf4j的记录器,而不是log4j2的,所以只能输出slf4j中的五种级别
        logger.error("error信息");
        logger.warn("warn信息");
        logger.info("info信息");
        logger.debug("debug信息");
        logger.trace("trace信息");
    }
}
23:11:22.977 [main] ERROR com.xyz.Log4j2Test - error信息

执行结果看到不管是Log4j2自己的门面还是SLF4J门面搭配Log4j2实现默认日志级别都是是error。

2、最牛的功能及性能

1、最强的异步和同步性能

Log4j2在目前JAVA中的日志框架里,异步日志的性能是最高的,没有之一。先来看一下,几种日志框架benchmark对比结果(官方测试结果)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRGuMCwG-1650171303296)(https://logging.apache.org/log4j/2.x/images/async-throughput-comparison.png)]

从图上可以看出,Log4j2的异步(全异步,非混合模式)下的性能,远超Log4j1和Logback,简直吊打。压力越大的情况下,吞吐上的差距就越大。在64线程测试下,Log4j2的吞吐达到了180w+/s,Llogback/Log4j1只有不到20w,相差近十倍。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjltEjL4-1650171303297)(https://logging.apache.org/log4j/2.x/images/SyncThroughputLoggerComparisonLinux.png)]

上图是同步测试报告,明显可以看出,与各个日志框架对比而言,无论在同步或者异步情况下,log4j2表现更加优异,而其他日志就显得差强人意了。

2、零GC(Garbage-free)

从Log4j 2.6版本开始(2016年),Log4j2默认就以零GC模式运行了。什么叫零GC呢?就是不会由于Log4j2而导致GC。Log4j2中各种Message对象,字符串数组,字节数组等全部复用,不重复创建,大大减少了无用对象的创建,从而做到“零GC”。

3、更高性能 I/O 写入的支持

Log4j2还提供了一个MemoryMappedFileAppender,I/O 部分使用MemoryMappedFile来实现,可以得到极高的I/O性能。不过在使用MemoryMappedFileAppender之前,得确定你足够了解MemoryMappedFile的相关知识,否则不要轻易使用呦。

4、更强大的参数格式化

Log4j的API与SLF4J相比,提供了更丰富的参数格式化功能。使用 {} 占位符格式化参数。在SLF4J里,我们可以用 {} 的方式来实现“format”的功能(参数会直接toString替换占位符),像下面这样:

org.apache.logging.log4j.Logger logger = LogManager.getLogger("com.xyz");
logger.debug("Logging in user {} with birthday {}", user.getName(), user.getBirthdayCalendar());

5、使用String.format形式格式化参数

Log4j2 中除了支持 {} 的参数占位符,还支持 String.format 的形式

org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("com.xyz");
logger.debug("Logging in user %s with birthday %s", user.getName(), user.getBirthdayCalendar());
logger.debug("Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());
logger.debug("Integer.MAX_VALUE = %,d、Long.MAX_VALUE = %,d", Integer.MAX_VALUE, Long.MAX_VALUE);

注意:如果想使用 String.format 的形式,需要使用 LogManager.getFormatterLogger 而不是 LogManager.getLogger

6、使用logger.printf格式化参数

Log4j2的Logger接口中,还有一个logger.printf()方法,无需创建 LogManager.getFormatterLogger,就可以使用 String.format 的形式

org.apache.logging.log4j.Logger logger = LogManager.getLogger("com.xyz");
logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());
logger.debug("Opening connection to {}...", someDataSource);

7、“惰性”打日志(lazy logging)

这个功能虽然小,但非常实用。在某些业务流程里,为了留根或追溯问题,需要完整的打印入参,一般是把入参给用JSON序列化后用debug级别打印:

org.apache.logging.log4j.Logger logger = LogManager.getLogger("com.xyz");
logger.debug("入参报文:{}", JSON.toJSONString(policyDTO));

如果需要追溯问题时,会将系统的日志级别调到debug,这样就可以打印。但是这里有个问题,虽然在info级别下debug不会输出内容,但JSON.toJSONString()这个序列化的代码一定会执行,严重影响正常流程下的执行效率。

我们期望的结果是info级别下,连序列化都不执行。这里可以通过logger.isDebugEnable()来判断当前配置下debug级别是否可以输出:

org.apache.logging.log4j.Logger logger = LogManager.getLogger("com.xyz");
if(logger.isDebugEnabled()) {
    logger.debug("入参报文:{}", JSON.toJSONString(policyDTO));
}

这样虽然可以避免不必要的序列化,但每个地方都这么写还是有点难受的,一行变成了三行。

Log4j2的Logger对象,提供了一系列Lambda的支持,通过这些接口可以实现“惰性”打日志:

void fatal(String message, Supplier... paramSuppliers);
void error(String message, Supplier<?>... paramSuppliers);
void warn(String message, Supplier... paramSuppliers);
void info(String message, Supplier<?>... paramSuppliers);
void debug(String message, Supplier<?>... paramSuppliers);
void trace(String message, Supplier<?>... paramSuppliers);

org.apache.logging.log4j.Logger logger = LogManager.getLogger("com.xyz");
// 等同于下面的先判断,后打印
logger.debug("入参报文:{}",() -> JSON.toJSONString(policyDTO));
// 与上面效果一样
if(logger.isDebugEnabled()) {
    logger.debug("入参报文:{}",JSON.toJSONString(policyDTO));
}

这种 Supplier + Lambda 的形式,等同于上面的先判断 logger.isDebugEnable() 然后打印,三行的代码变成了一行。

8、更简化的配置文件

Log4j2同时支持XML/JSON/YML/Properties四种形式的配置文件,不过最主流的还是XML的方式,最直观。来查看logback和log4j2的配置文件对比:

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name = "File" class= "ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/archives/app-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <!--一天内大于size就单独分隔-->
            <maxFileSize>1 GB</maxFileSize>
        </rollingPolicy>
    </appender>
    <root level="info">
        <appender-ref ref="File"/>
    </root>
</configuration>

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="XInclude" xmlns:xi="http://www.w3.org/2001/XInclude">
    <Appenders>
        <RollingFile name="File" fileName="logs/app.log" filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] %-40.40c{1.} : %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <!--一天内大于size就单独分隔-->
                <SizeBasedTriggeringPolicy size="1 GB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

在log4j2中,appender的配置从使用 Appender 实现名即标签名的形式,语法上更简洁一些:

<RollingFile name="File"></RollingFile>
<!-- 等同于logback中的 -->
<appender name = "File" class= "ch.qos.logback.core.rolling.RollingFileAppender"></appender>

3、组件介绍与配置

1、几大组件的介绍

Log4j2由如下几部分构成:

  1. Logger:负责捕获日志记录,并传递给 Appender,他是日志行为的发起者
  2. Appender:负责将日志事件进行分类处理,将日志发往他应该去的目标去向,因此也可以称为 Handler
  3. Layout:Layout 负责在日志输出前决定日志的格式,因此也可以称为 Fomatter
  4. Filter:是可选的组件,每一个 Logger、Appender 甚至全局都可以配置若干个 Filter,来决定相应的组件对当前的日志时间是否关心
  5. Level:日志级别有:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL(默认为:ERROR)源码:org.apache.logging.log4j.Level

2、Log4j2默认配置

DefaultConfiguration类中提供的默认配置将设置,通过debug可以在LoggerContext类中发现

package org.apache.logging.log4j.core;

public class LoggerContext extends AbstractLifeCycle 
    implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener, LoggerContextShutdownEnabled {
    /**
     * The Configuration is volatile to guarantee that initialization of the Configuration has completed before the
     * reference is updated.
     */
    private volatile Configuration configuration = new DefaultConfiguration();
}

启动时可以通过debug看到:

  • 默认的root日志的layout:%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
  • 默认的日志级别:ERROR

实际上也能从默认配置类中看到一些默认的配置:

public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration {
    protected void setToDefault() {
        // LOG4J2-1176 facilitate memory leak investigation
        setName(DefaultConfiguration.DEFAULT_NAME + "@" + Integer.toHexString(hashCode()));
        final Layout<? extends Serializable> layout = PatternLayout.newBuilder()
            .withPattern(DefaultConfiguration.DEFAULT_PATTERN)
            .withConfiguration(this)
            .build();
        final Appender appender = ConsoleAppender.createDefaultAppenderForLayout(layout);
        appender.start();
        addAppender(appender);
        final LoggerConfig rootLoggerConfig = getRootLogger();
        rootLoggerConfig.addAppender(appender, null, null);

        rootLoggerConfig.setLevel(getDefaultLevel());
    }
}

3、自定义配置文件位置

1、Log4j2默认在classpath下查找配置文件,可以修改配置文件的位置。在非web项目中:

ConfigurationSource source = new ConfigurationSource(new FileInputStream("D:/log4j2.xml"));
Configurator.initialize(null, source);
org.apache.logging.log4j.Logger logger = LogManager.getLogger(Log4j2Test.class);

2、如果是在SpringBoot项目,在appliaction.properties 或 application.yml 中配置即可:

logging.config=classpath:log4j2.xml

3、如果是web项目,在web.xml中添加(Listener使用的是Log4j2默认的):

<listener>
    <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<context-param>
    <param-name>log4jConfiguration</param-name>
    <param-value>/WEB-INF/conf/log4j2.xml</param-value>
</context-param>

4、如果是web项目,在web.xml中添加(使用自定义Listener):

<!-- 系统日志配置监听器 -->
<listener>
    <listener-class>com.xyz.Log4j2ConfigListener</listener-class>
</listener>
<context-param>
    <description>Logging Configuration File Path</description>
    <param-name>log4j.configurationFile</param-name>
    <param-value>log4j/log4j2.xml</param-value>
</context-param>
package com.xyz;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.core.config.Configurator;
public class Log4j2ConfigListener implements ServletContextListener {
    private static final String KEY = "log4j.configurationFile";
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
    }
    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        String fileName = getContextParam(arg0);
        Configurator.initialize("Log4j2", "classpath:" + fileName);
    }
    @SuppressWarnings("unchecked")
    private String getContextParam(ServletContextEvent event) {
        Enumeration<String> names = event.getServletContext().getInitParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            String value = event.getServletContext().getInitParameter(name);
            if(name.trim().equals(KEY)) {
                return value;
            }
        }
        return null;
    }
}

4、XML配置文件模板

模板 1(简约版):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="1" status="ERROR" strict="true" name="LogConfig">
    <Properties>
        <Property name="logbasedir">E:/logs</Property>
        <Property name="log.layout">%d %-5p %t (%c:%L) - %m%n</Property>
    </Properties>

    <!--此处使用了两种类型的appender,RollingFile为滚动类型,满足策略条件后会新建文件夹记录 -->
    <Appenders>
        <Appender type="Console" name="STDOUT">
            <Target>SYSTEM_OUT</Target>
            <Layout type="PatternLayout" pattern="${log.layout}"/>
        </Appender>
        <Appender type="RollingFile" name="FILE" 
                  fileName="${logbasedir}/jutap-${sys:APPNAME}.log"
                  filePattern = "${logbasedir}/jutap-${sys:APPNAME}-%d{yyyy-MM-dd}.%i.log">
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <Layout type="PatternLayout">
                <Charset>GBK</Charset>
                <Pattern>${log.layout}</Pattern>
            </Layout>
        </Appender>
        <Appender type="RollingFile" 
                  name="ExceptionLog" 
                  fileName="${logbasedir}/exception-${sys:APPNAME}.log"
                  filePattern="${logbasedir}/exception-${sys:APPNAME}-%d{yyyy-MM-dd}.%i.log">
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <Layout type="PatternLayout">
                <Charset>GBK</Charset>
                <Pattern>${log.layout}</Pattern>
            </Layout>
        </Appender>
    </Appenders>
    <Loggers>
        <Logger name="exception" level="error" additivity="false">
            <AppenderRef ref="ExceptionLog"/>
        </Logger>
        <Root level="info">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FILE"/>
        </Root>
        <Logger name="com.garfield.learn" level="debug"/>
        <Logger name="com.garfield.learnp" level="info"/>
    </Loggers>
</Configuration>

模板 2(复杂版):

<?xml version="1.0" encoding="UTF-8"?>
<!--
    status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出
    monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。
    注:本配置文件的目标是将不同级别的日志输出到不同文件,最大2MB一个文件,
    文件数据达到最大值时,旧数据会被压缩并放进指定文件夹
-->
<Configuration status="WARN" monitorInterval="600">
    <Properties>
        <!-- 配置日志文件输出目录,此配置将日志输出到tomcat根目录下的指定文件夹 -->
        <Property name="LOG_HOME">${sys:catalina.home}/WebAppLogs/SSHExample</Property>
    </Properties>
    <Appenders>
        <!--这个输出控制台的配置,这里输出除了warn和error级别的信息到System.out-->
        <Console name="console_out_appender" target="SYSTEM_OUT">
            <!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="warn" onMatch="DENY" onMismatch="ACCEPT"/>
            <!-- 输出日志的格式 -->
            <PatternLayout pattern="%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n"/>
        </Console>
        <!--这个输出控制台的配置,这里输出warn和error级别的信息到System.err,在eclipse控制台上看到的是红色文字-->
        <Console name="console_err_appender" target="SYSTEM_ERR">
            <!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 输出日志的格式 -->
            <PatternLayout pattern="%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n"/>
        </Console>
        <!-- TRACE级别日志 -->
        <!-- 设置日志格式并配置日志压缩格式,压缩文件独立放在一个文件夹内,
        日期格式不能为冒号,否则无法生成,因为文件名不允许有冒号,此appender只输出trace级别的数据到trace.log -->
        <RollingRandomAccessFile name="trace_appender"
                                 immediateFlush="true" 
                                 fileName="${LOG_HOME}/trace.log"
                                 filePattern="${LOG_HOME}/trace/trace - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies><!-- 两个配置任选其一 -->
                <!-- 每个日志文件最大2MB -->
                <SizeBasedTriggeringPolicy size="2MB"/>
            </Policies>
            <Filters><!-- 此Filter意思是,只输出debug级别的数据 -->
                <!-- DENY,日志将立即被抛弃不再经过其他过滤器;
                       NEUTRAL,有序列表里的下个过滤器过接着处理日志;
                       ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
                <ThresholdFilter level="debug" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>
        <!-- DEBUG级别日志 -->
        <!-- 设置日志格式并配置日志压缩格式,压缩文件独立放在一个文件夹内,
        日期格式不能为冒号,否则无法生成,因为文件名不允许有冒号,此appender只输出debug级别的数据到debug.log -->
        <RollingRandomAccessFile name="debug_appender"
                                 immediateFlush="true" 
                                 fileName="${LOG_HOME}/debug.log"
                                 filePattern="${LOG_HOME}/debug/debug - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies><!-- 两个配置任选其一 -->
                <!-- 每个日志文件最大2MB -->
                <SizeBasedTriggeringPolicy size="2MB"/>
                <!-- 如果启用此配置,则日志会按文件名生成新压缩文件,
                即如果filePattern配置的日期格式为 %d{yyyy-MM-dd HH} ,则每小时生成一个压缩文件,
                如果filePattern配置的日期格式为 %d{yyyy-MM-dd} ,则天生成一个压缩文件 -->
                <!--                 <TimeBasedTriggeringPolicy interval="1" modulate="true" /> -->
            </Policies>
            <Filters><!-- 此Filter意思是,只输出debug级别的数据 -->
                <!-- DENY,日志将立即被抛弃不再经过其他过滤器;
                       NEUTRAL,有序列表里的下个过滤器过接着处理日志;
                       ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
                <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>
        <!-- INFO级别日志 -->
        <RollingRandomAccessFile name="info_appender"
                                 immediateFlush="true" 
                                 fileName="${LOG_HOME}/info.log"
                                 filePattern="${LOG_HOME}/info/info - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="2MB"/>
            </Policies>
            <Filters>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>
        <!-- WARN级别日志 -->
        <RollingRandomAccessFile name="warn_appender"
                                 immediateFlush="true" 
                                 fileName="${LOG_HOME}/warn.log"
                                 filePattern="${LOG_HOME}/warn/warn - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="2MB"/>
            </Policies>
            <Filters>
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>
        <!-- ERROR级别日志 -->
        <RollingRandomAccessFile name="error_appender"
                                 immediateFlush="true" 
                                 fileName="${LOG_HOME}/error.log"
                                 filePattern="${LOG_HOME}/error/error - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
            <PatternLayout>
                <pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="2MB"/>
            </Policies>
            <Filters>
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </RollingRandomAccessFile>
    </Appenders>
    <Loggers>
        <!-- 配置日志的根节点 -->
        <root level="trace">
            <appender-ref ref="console_out_appender"/>
            <appender-ref ref="console_err_appender"/>
            <appender-ref ref="trace_appender"/>
            <appender-ref ref="debug_appender"/>
            <appender-ref ref="info_appender"/>
            <appender-ref ref="warn_appender"/>
            <appender-ref ref="error_appender"/>
        </root>
        <!-- 第三方日志系统 -->
        <logger name="org.springframework.core" level="info"/>
        <logger name="org.springframework.beans" level="info"/>
        <logger name="org.springframework.context" level="info"/>
        <logger name="org.springframework.web" level="info"/>
        <logger name="org.jboss.netty" level="warn"/>
        <logger name="org.apache.http" level="warn"/>
    </Loggers>
</Configuration>

5、动态切换日志级别

package com.xyz;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;

public class Log4j2Test {
    private static  final Logger logger = LogManager.getLogger(Log4j2Test.class);
    public static void main(String[] args) {
        logger.fatal("fatal...");
        logger.error("error...");

        // LoggerContext getContext(final boolean currentContext):获取log4j日志上下文,false表示返回合适调用方的上下文
        LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
        // 返回当前配置,发生重新配置时,将替换该配置。
        Configuration configuration = loggerContext.getConfiguration();
        // 查找记录器名称的相应 LoggerConfig
        LoggerConfig loggerConfig = configuration.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
        // 设置日志级别,如果 level 值不属于 ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF,则默认会设置为 DEBUG
        loggerConfig.setLevel(Level.toLevel("FATAL"));
        // 根据当前配置更新所有记录器
        loggerContext.updateLoggers();
        // 查询 root(根)日志输出级别结果返回
        System.out.println("设置后的Root日志级别: " + LogManager.getRootLogger().getLevel().name());

        logger.fatal("fatal...");
        logger.error("error...");
    }
}
15:27:25.768 [main] FATAL com.xyz.Log4j2Test - fatal...
15:27:25.768 [main] ERROR com.xyz.Log4j2Test - error...
设置后的Root日志级别: FATAL
15:27:25.778 [main] FATAL com.xyz.Log4j2Test - fatal...

4、自定义配置文件

Log4j2配置文件通常为:log4j2.xml和log4j2-test.xml,加载顺序为:log4j2-test.xml » log4j2.xml。

XML配置文件语法

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
    <Properties>
        <Property name="name1">value</property>
        <Property name="name2" value="value2"/>
    </Properties>
    <filter  ... />
    <Appenders>
        <appender ... >
            <filter  ... />
        </appender>
        ...
    </Appenders>
    <Loggers>
        <Logger name="name1" level="level">
            <filter  ... />
        </Logger>
        ...
        <Root level="level">
            <AppenderRef ref="name"/>
        </Root>
    </Loggers>
</Configuration>
节点名称 含义 配置项
Configuration 配置的根节点,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger) 该节点有两个配置项status:用于指定log4j2本身的日志打印级别,日志级别从低到高分为TRACE、DEBUG、INFO、WARN、ERROR、FATAL,如果设置为WARN,则低于WARN的信息都不会输出。monitorinterval:用于指定log4j自动重新配置的监测间隔时间,单位是秒(s),最小是5s
Appenders 负责将日志输出到目的地(这个目的地可以是控制台,文件,数据库,甚至是邮件),定义输出内容,输出格式,输出方式,日志保存策略等 log4j2支持的appender种类非常多,完整的appender可通过官方网站进行查询。其中,比较常见的appender有:ConsoleAppender、FileAppender、RandomAccessFileAppender、RollingFileAppender、RollingRandomAccessFileAppender,另外,还提供异步的AsyncAppender,实现异步处理日志事件
Loggers LogEvent生产的源头,只有定义了logger并引入的appender,appender才会生效 常见的有两种:Root和Logger

1、Configuration

1、Configuration

根节点Configuration中有两个常用的属性:status、monitorterval、name。示例及解释如下:

Configuration元素的属性:

  • status:用于指定Log4j2日志的输出级别,会打印Log4j2加载、运行等信息。 有如下值:trace, debug, info, warn, error, fatal
  • monitorterval:用于指定自动重新检测读取配置内容的间隔时间,单位为秒(s),最小值为5秒
  • name:配置的名称,用的也不太多
<!--status表示log4j2自身的日志打印级别,如WARN会打印出log4j2加载、运行等信息, monitorinterval监控间隔,单位为秒-->
<Configuration status="WARN" monitorInterval="30" name="xyzConfiguration">
</Configuration>

2、Properties

子节点元素Properties是用来定义常量,以便在其他配置项中引用,该配置是可选的,例如定义日志的存放位置

<Properties>
    <!-- 配置全局自定义变量,使用时通过${name}引用 -->
    <Property name="nowDate">%date{yyyy-MM-dd}</Property>
    <Property name="logDir">${sys:catalina.home}/logs/</Property>
    <Property name="logPath">./logs</Property>
    <property name="pattern_format">[%d{HH:mm:ss:SSS}] [%-5p] - %l -- %m%n</property>
</Properties>
Prefix Context
base64 Base64编码数据,格式为 b a s e 64 : B a s e 6 4 e n c o d e d d a t a , 如 : {base64:Base64_encoded_data},如: base64:Base64encodeddata{base64:SGVsbG8gV29ybGQhCg==} 输出 Hello World!
bundle 资源绑定。格式为: b u n d l e : B u n d l e N a m e : B u n d l e K e y , 示 例 如 : {bundle:BundleName:BundleKey},示例如: bundle:BundleName:BundleKey{bundle:com.domain.Messages:MyKey}
ctx 线程上下文映射(MDC),如:$${ctx:loginId}
date 插入指定的时间格式。如:$${date:MM-dd-yyyy}
env 系统环境变量,格式为${env:ENV_NAME} 或者 e n v : E N V N A M E : − d e f a u l t v a l u e , 如 : {env:ENV_NAME:-default_value} ,如: env:ENVNAME:defaultvalue e n v : U S E R , {env:USER}, env:USER${env:USER:-jdoe}
jndi 设置JNDI上下文,如:$${jndi:logging/context-name}。注意:在Android下面不能使用
sys 系统属性,格式为${sys:some.property}或者 s y s : s o m e . p r o p e r t y : − d e f a u l t v a l u e , 如 : {sys:some.property:-default_value},如: sys:some.property:defaultvalue{sys:logPath}
jvmrunargs 通过 JMX 访问的 JVM 输入参数,但不是主参数; 请参阅 RuntimeMXBean.getInputArguments()。 在Android上不可用
log4j Log4j配置属性。 表达式 ${log4j:configLocation} 和 ${log4j:configParentLocation} 分别提供了log4j配置文件及其父文件夹的绝对路径
main 使用 MapLookup.setMainArguments(String[]) 设置的值
map 来自 MapMessage 的值

3、Loggers

Loggers 定义日志输出位置,包含Logger和Root结点。Root用来指定项目的根日志,若没有单独指定Logger,则会默认使用该Root日志输出

Root:每个配置都必须有一个根记录器Root。如果未配置,则将使用默认根LoggerConfig,其级别为ERROR且附加了ConsoleAppender。根记录器和其他记录器之间的主要区别是:1.根记录器只有level属性。2.根记录器没有name属性。3.根记录器不支持additivity属性,因为它没有父级

  • 属性:level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
  • 属性:includeLocation:如果你的layouts或custom过滤器需要location信息,你需要在所有相关日志记录器(包括根日志记录器)的配置中设置“includeLocation=true”,默认为false
  • 子节点:AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender

Logger:用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。Logger元素必须有一个name属性,每个Logger可以使用TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF之一配置级别。如果未指定级别,则默认为ERROR。可以为additivity属性分配值true或false。如果省略该属性,则将使用默认值true。子节点AppenderRef用于指定日志输出到哪个Appender,若没有指定,默认集成自Root

  • 属性:name:用来指定该Logger所适用的类或包全路径,继承自Root节点。一般是项目或者框架的包名,如:com.xyz、org.springframework
  • 属性:level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF
  • 属性:additivity:表示需不需要打印此logger的父logger,如果是false则只打印当前logger;如果是true则继续打印上一层的logger,直到root
  • 属性:includeLocation:如果你的layouts或custom过滤器需要location信息,需要在所有相关日志记录器(包括根)中设置includeLocation为true,默认false
  • 子节点:AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root。如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出

AsyncLogger:异步日志,Log4j2有三种模式:全异步日志,混合模式,同步日志,性能从高到底,线程越多效率越高,也可以避免日志卡死线程情况发生

  • includeLocation:如果你的layouts或custom过滤器需要location信息,需要在所有相关日志记录器(包括根)中设置includeLocation为true,默认false
<Configuration>
    <!--定义日志输出配置-->
    <Loggers>
        <!---->
        <!--Logger节点控制指定包或类的日志输出(包括等级和目的地),如下是过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <!--参数详解: name为包路径,level为日志级别,additivity表示日志信息是否向上传递,false为不传递(即不重复打印)-->
        <Logger name="org.springframework" level="INFO"></logger>
        <Logger name="org.mybatis" level="INFO"></logger>
        <Logger name="com.xyz"  level="false" additivity="false">
            <AppenderRef ref="consoleAppender" />
        </Logger>

        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <Root level="info">
            <!--日志输出到控制台和文件中-->
            <AppenderRef ref="consoleAppender" />
            <AppenderRef ref="fileAppender" />
        </Root>

        <!--AsyncLogger: 异步日志,LOG4J有三种日志模式,全异步日志,混合模式,同步日志,性能从高到底,线程越多效率越高,也可以避免日志卡死线程情况发生-->
        <!--additivity="false" : additivity设置事件是否在rootLogger输出,为了避免重复输出,可以在Logger标签下设置additivity为”false”-->
        <AsyncLogger name="AsyncLogger" level="trace" includeLocation="true" additivity="false">
            <appender-ref ref="RollingFileError"/>
        </AsyncLogger>
    </Loggers>
</Configuration>

4、XInclude

可以使用这个标签来引入外部xml文件

<configuration status="warn" name="XIncludeDemo">
    <xi:include href="log4j-xinclude-appenders.xml" />
    <xi:include href="log4j-xinclude-loggers.xml" />
</configuration>

2、Appenders

参考官网:http://logging.apache.org/log4j/2.x/manual/appenders.html

Appenders是输出源,用于定义日志输出的地方。Log4j2支持的输出源有很多:ConsoleAppender、FileAppender、RandomAccessFileAppender、RollingFileAppender、RollingRandomAccessFileAppender、AsyncAppender 等,如下是 Appender 所有分类(参考官网):

名称 描述
AsyncAppender 使用一个单独的线程记录日志,实现异步处理日志时间
CassandraAppender 将日志信息输出到一个Apache的Cassandre数据库
ConsoleAppender 将日志信息输出到控制台
FailoverAppender 包含其他Appenders,按照顺序尝试,直至成功或结尾
FileAppender 一个OutputStreamAppenders,将日志输出到文件
FlumeAppender 将日志输出到Apache Flume系统
JDBCAppender 将日志通过JDBC输出到关系型数据库
JMSAppender 将日志输出到JMS(Java Message Service)
JPAAppender 将日志输出到JPA框架
HTTPAppender 通过HTTP输出到日志
KafkaAppender 将日志输出到Apache Kafka
MemoryMappedFileAppender 将日志输出到一块文件关联的内存
NoSQLAppender 将日志输出到NoSQL数据库,如MongoDB、CouchDB
OutputStreamAppender 将日志输出到一个OutputStream
RandomAccessFileAppender 性能比FileAppender高20%~200%的文件输出Appender
RewriteAppender 允许对日志信息进行加工掩码输出
RollingFileAppender 按日志文件最大长度限度生成新文件
RollingRandomAccessFileAppender 添加了缓存的RollingFileAppender
RoutingAppender 将日志事件分类,允许通过规则路由日志到不同的输出地(子Appender)
SMTPAppender 将日志输出到邮件
ScriptAppenderSelectorAppender 使用自定义脚本的形式来输出日志
SocketAppender 将日志输出到Socket
SyslogAppender 是一个SocketAppender,将日志输出到远程系统日志
ZeroMQ/JeroMQAppender 使用JeroMQ库将日志输出到ZeroMQ终端

1、Appender常用模板

<!--日志输出到控制台-->
<Console name="consoleAppender" target="SYSTEM_OUT">
    <PatternLayout pattern="%date %logger %processId %threadId %method %class %file %highlight{%level} : %green{%msg} %n" />
</Console>

<!--日志写入文件中-->
<File name="fileAppender" fileName="log4j2Study_${date:yyyy-MM}.log">
    <PatternLayout pattern="%date %logger %level : %msg%n" />
</File>

<!--日志写入文件中,和fileAppender(File)类似,只是RandomAccessFile加入了buffer缓存,且该缓存不可删除 -->
<RandomAccessFile  name="randomFileAppender" fileName="log4j2_randomFileAppender.log">
    <PatternLayout pattern="%date %logger %level : %msg%n" />
</RandomAccessFile >

<!--日志写入文件,根据自定义的滚动策略归档文件,filePattern定义日志文件归档格式-->
<RollingFile name="rollingFileAppender" fileName="log4j2_rollingFileAppender.log"
             filePattern="$${date:yyyy-MM}/%d{MM-dd-yyyy}-%i.log.gz">
    <PatternLayout pattern="%date %logger %level : %msg%n" />
    <!--定义滚动策略-->
    <Policies>
        <!--基于cron表达式触发归档-->
        <CronTriggeringPolicy schedule="0 0 * * * ?"/>
        <!--基于时间触发归档,与上文filePattern配合使用,当filePattern不符合时,就归档-->
        <TimeBasedTriggeringPolicy />
        <!--基于文件大小触发归档-->
        <SizeBasedTriggeringPolicy size="100 MB" />
    </Policies>

    <!--默认日志保留7天-->
    <DefaultRolloverStrategy max="7" />

    <!--log4j 2.5引入,可更精细地控制删除日志策略-->
    <DefaultRolloverStrategy>
        <!--同时满足下述条件,进行删除-->
        <Delete>
            <!--文件名符合.log.gz后缀-->
            <IfFileName glob="*.log.gz" />
            <!--超过60天-->
            <IfLastModified age="60d" />
        </Delete>
    </DefaultRolloverStrategy>
</RollingFile>

<!--和RollingFile类似,只是加入了buffer缓存,且该缓存不可删除-->
<RollingRandomAccessFile></RollingRandomAccessFile>

<!--异步记录日志,配合其它appender使用,不单独使用-->
<Async name="fileAsync" >
    <AppenderRef ref="fileAppender" />
</Async>

<!--日志写入cassandra库中,具体配置在此略过,详情请查看官网-->
<Cassandra></Cassandra>

<!--日志写入jdbc库中,具体配置在此略过,详情请查看官网-->
<JDBC></JDBC>

<!--日志写入消息队列中,具体配置在此略过,详情请查看官网-->
<JMS></JMS>

<!--日志写入kafka消息队列中,具体配置在此略过,详情请查看官网-->
<Kafka></Kafka>

<!--日志写入nosql库中,具体配置在此略过,详情请查看官网-->
<NoSql></NoSql>

<!--日志写入邮件中,具体配置在此略过,详情请查看官网-->
<SMTP></SMTP>

<!--日志写入socket中,具体配置在此略过,详情请查看官网-->
<Socket></Socket>

<!--日志写入http中,具体配置在此略过,详情请查看官网-->
<Http></Http>

<!--日志重写,具体配置在此略过,详情请查看官网-->
<Rewrite></Rewrite>

2、ConsoleAppender

ConsoleAppender:使用该Appender可以将日志信息输出到控制台,参数与配置如下

Console 元素的属性:

  • name:Appender的名字
  • target:输出方法,SYSTEM_OUT 或 SYSTEM_ERR。默认值:SYSTEM_OUT

Console 元素的节点:

  • PatternLayout:输出格式,有一个pattern属性,不设置默认为:%m%n
  • Filter:设置过滤器,可以为单个Appenders设置,也可以全局设置,后面会详细介绍
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorinterval="10" name="myApp">
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>

3、FileAppender

FileAppender:用于将LogEvent写入到一个文件中,是文件输出源,用于将日志写入到指定的文件,其底层是一个OutputStreamAppender

File 元素的属性

  • name:Appender的名字
  • fileName:指定写入的log文件的名称
  • append:指定是否是追加写入(append=true,默认情况),还是覆盖写入(append=false)
  • bufferedIO:是否对数据进行缓冲到缓冲区满后再写入。测试显示,即使在启用immediateFlush的情况下,设置bufferedIO=true也能提高性能。
  • bufferSize:当bufferedIO=true时,缓冲区的大小,默认是8192bytes
  • locking:是否对文件上锁,当有多个线程可能同时写该文件时需要考虑上锁(在《异常处理反模式》中就提到要把在一起的日志输出语句写到一句,而不是拆成几句来避免并发线程导致的日志语句之间的错位)。但对文件上锁会影响系统的性能,所以需要谨慎使用。默认值是false
  • immediateFlush:是否立即将数据刷入磁盘,能够最大程度保证数据的完整性,但是对性能会有一定的影响,默认值为true

File 元素的节点

  • PatternLayout:用于指定输出格式,不设置的话,默认为:%m%n
  • Filter:设置过滤器,可以为单个Appenders设置,也可以全局设置,后面会详细介绍
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorinterval="10" name="myApp">
    <Appenders>
        <File name="fileAppender" fileName="logs/app.log" append="true">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="fileAppender"/>
        </Root>
    </Loggers>
</Configuration>

4、RandomAccessFileAppender

RandomAccessFile参考:https://www.cnblogs.com/jyy599/p/12076662.html

与FileAppender很相似,除了将BufferdOutputStream替换为了ByteBuffer + RandomAccessFile,还使用了缓存,它的性能比FileAppender高20%~200%

RandomAccessFile 元素的属性

  • 配置基本上与FileAppender相同,见上文,主要区别在于bufferSize
  • bufferSize:缓存空间大小,默认为256M

RandomAccessFile 元素的节点

  • PatternLayout:用于指定输出格式,不设置的话,默认为:%m%n
  • Filter:设置过滤器,可以为单个Appenders设置,也可以全局设置,后面会详细介绍
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorinterval="10" name="myApp">
    <Appenders>
        <RandomAccessFile name="randomAccessFile" fileName="logs/app.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </RandomAccessFile>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="randomAccessFile"/>
        </Root>
    </Loggers>
</Configuration>

5、RollingFileAppender(最常用)

RollingFile用于实现日志文件动态更新的Appender,当满足条件(日志大小、指定时间等)重命名或打包原日志文件进行归档生成新文件,比File更强大

RollingFileAppender是一个OutputStreamAppender,先写入指定文件,并根据TriggeringPolicy和RolloverPolicy滚动文件。RollingFile需要两个策略的支持,分别是TriggeringPolicy和RolloverPolicy。TriggeringPolicy定义何时应该生成新的日志文件而RolloverPolicy则决定如何生成新的日志文件。RollingFile不支持文件锁

RollingFile 元素的属性:

  • name:用于指定Appender的名称
  • fileName:用于指定日志文件的全路径
  • filePattern:用于指定分割文件的日志全路径(命名规则)

RollingFile 元素的节点:

  • PatternLayout:用于指定输出格式,不设置的话,默认为:%m%n`
  • Policies :设置日志文件切割参数
    • OnStartupTriggeringPolicy:Policies的子节点,每次JVM启动,都滚动到新的日志文件开始记录,一般不怎么使用
    • SizeBasedTriggeringPolicy:Policies的子节点,按照日志文件大小进行触发滚动策略,size属性表示分割文件大小,单位 KB、MB、GB 或 TB
    • TimeBasedTriggeringPolicy:Policies的子节点,根据日期时间间隔触发进行滚动,interval属性用于指定滚动时间间隔,默认是1小时,modulate属性是用于对interval进行偏移调节,默认为false。若为true,则第一次触发时是第一个小时触发,后续以interval间隔触发
    • CronTriggeringPolicy:Policies的子节点,用于设置基于Cron表达式触发的滚动策略
    • 等等还有许多,后面会单独介绍,也可以参考官网
  • DefaultRolloverStrategy:设置默认策略设置
  • Filter:设置过滤器,可以为单个Appenders设置,也可以全局设置,后面会详细介绍

如下配置:有三个Trigger,表示每秒生成一个文件或者每小时生成一个文件或者当文件大小达到250MB时生成一个新的文件;有两个Strategy,表示最多保持文件数量为10,达到最大值后,旧的文件将被删除,或者文件名符合.log.gz后缀 or 超过60天 也会删除旧文件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorinterval="0" name="myApp">
    <Appenders>
        <RollingFile name="rollingFile"
                     fileName="logs/app.log"
                     filePattern="logs/app-%d{yyyy-MM-dd_HH-mm-ss}-%i.log.zip">
            <PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
            <!--定义滚动策略,满足其中一个就会触发滚动策略-->
            <Policies>
                <!--由于filePattern是按秒配置的,TimeBasedTriggeringPolicy触发后会每秒生成文件,所以后面的策略都不会触发-->
                <!--基于时间触发归档,与上文filePattern配合使用,当filePattern不符合时,就归档-->
                <TimeBasedTriggeringPolicy />
                <!--基于cron表达式触发归档-->
                <CronTriggeringPolicy schedule="0 0 * * * ?"/>
                <!--基于文件大小触发归档-->
                <SizeBasedTriggeringPolicy size="250 MB"/>
            </Policies>
            <!--默认日志保留10天-->
            <DefaultRolloverStrategy max="10"/>
            <!--log4j 2.5引入,可更精细地控制删除日志策略-->
            <DefaultRolloverStrategy>
                <!--同时满足下述条件,进行删除,maxDepth表示要访问的目录的最大级别数-->
                <Delete basePath="logs/" maxDepth="2">
                    <!--文件名符合.log.gz后缀-->
                    <IfFileName glob="logs/*.log.zip"/>
                    <!--超过60天-->
                    <IfLastModified age="60d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="rollingFile"/>
        </Root>
    </Loggers>
</Configuration>

如下单独使用三个示例进行详细讲解:

(1)基于大小的滚动策略

日志先写入logs/app.log中,每当文件大小达到100MB时或经过1天,按照在logs/2020-09/目录下以app-2020-09-09-1.log.gz格式对该日志进行压缩重命名并归档,并生成新的文件app.log进行日志写入。其中,filePattern属性的文件格式中%i就类似于一个整数计数器,受到<DefaultRolloverStrategy max="10"/>控制,要特别注意的是:当文件个数达到10个的时候会循环覆盖前面已归档的1-10个文件。若不设置该参数,默认为7。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
    <Appenders>
        <RollingFile name="RollingFile" fileName="logs/app.log"
                     filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

(2)基于时间间隔的滚动策略

日志先写入logs/app.log中,每当文件的时间间隔到达6小时(由%d{yyyy-MM-dd-HH}决定,也可以设置成%d{yyyy-MM-dd-HH-mm},则间隔为分钟级别),触发rollover操作。如下配置设置好后,10点的日志开始重启服务,则从11点触发一次rollover操作,生成2022-04-10-10.log.gz对该日志进行压缩重命名并归档,并生成新的文件app.log进行日志写入;然后,每间隔6小时,则下一次是17点触发一次,生成2022-04-10-17.log.gz对该日志进行压缩重命名并归档,并生成新的文件app.log进行日志写入。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
    <Appenders>
        <RollingFile name="RollingFile" fileName="logs/app.log"
                     filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}.log.gz">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <!--interval:时间间隔数,默认1,例如:filePattern配置是小时,如果是2小时间隔,则配置成2即可-->
                <!--modulate:翻转发生在间隔边界上,就是整点,例如:启动时间3:30,间隔时间2小时,开启配置后下次反转时间为6:00-->
                <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

(3)基于时间间隔和文件大小的滚动策略

日志先写入logs/app.log中,每当文件大小达到100MB或者当时间间隔到达6小时(由%d{yyyy-MM-dd-HH}决定),触发rollover操作,按照在logs/2022-04/目录下以app-2022-04-1-1.log.gz格式对该日志进行压缩重命名并归档,并生成新的文件app.log进行日志写入。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
    <Appenders>
        <RollingFile name="RollingFile"
                     fileName="logs/app.log"
                     filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
            <Policies>
                <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

6、RollingRandomAccessFileAppender

RollingRandomAccessFile类似于标准的RollingFile,除了它总是被缓冲(且不能被关闭),并且在内部它使用 ByteBuffer + RandomAccessFile 而不是BufferedOutputStream。与RollingFileAppender相比,当 bufferedIO = true 时,性能提升 20-200%。

RollingFile 元素的属性:

  • name:指定Appender的名字。
  • fileName 指定当前日志文件的位置和文件名称
  • filePattern 指定当发生Rolling时,文件的转移和重命名规则
  • immediateFlush 设置为true时 - 默认值,每次写入后都会进行刷新。这将保证数据写入磁盘,但可能会影响性能。
  • bufferSize 缓冲区大小,默认为256M 或 262,144字节(256 * 1024)

RollingFile 元素的节点:

  • PatternLayout:用于指定输出格式,不设置的话,默认为:%m%n`
  • Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志
    • SizeBasedTriggeringPolicy:指定当文件大小大于size指定的值时,触发Rolling
    • TimeBasedTriggeringPolicy:这个配置需要和filePattern结合使用,日期格式精确到哪一位,interval也精确到哪一个单位。注意filePattern中配置的文件重命名规则是${FILE_NAME}-%d{yyyy-MM-dd HH-mm-ss}-%i,最小的时间粒度是ss,即秒钟。TimeBasedTriggeringPolicy默认的size是1,结合起来就是每1秒钟生成一个新文件。如果改成%d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件
  • DefaultRolloverStrategy:指定最多保存的文件个数
  • Filter:设置过滤器,可以为单个Appenders设置,也可以全局设置,后面会详细介绍
<!--和RollingFile类似,只是加入了buffer缓存,且该缓存不可删除,案例可以直接参考RollingFile,替换标签即可-->
<RollingRandomAccessFile></RollingRandomAccessFile>

7、AsyncAppender(异步)

异步输出日志,默认使用 java.util.concurrent.ArrayBlockingQueue 来实现的,不需要其他的外部库,这个配置在多线程的情况下需要小心,可能会出现锁竞争,如果在多线程下,可以使用Disruptor库(一个无锁的线程间通信库,而不是队列,从而产生更高的吞吐量和更低的延迟),可以使用asyncRoot或者asyncLogger来进行配置。

<Async name="Async">
    <AppenderRef ref="RollingFileAppender" />
    <AppenderRef ref="Console" />
    <LinkedTransferQueue/>
</Async>

3、Filters

官网地址:https://logging.apache.org/log4j/2.x/manual/filters.html

Filter用于过滤日志(是可选的)Log4j2会在日志产生时自动调用预先配置的Filter的filter方法进行过滤,以便获得是否允许打印的标识。

是否允许打印的标识是一个 Result 类型的枚举,他的值有三种:ACCEPT、DENY、NEUTRAL

  • ACCEPT:(直接接受)日志会被立即处理,不再经过剩余过滤器
  • DENY:(直接拒绝)日志将立即被抛弃不再经过其他过滤器
  • NEUTRAL:(中立,不做处理,交由后面代码处理)有序列表里的下个过滤器过接着处理日志

重点讲解 NEUTRAL,如果只有一个 Filter,那么 NEUTRAL 与 ACCEPT 没有任何区别,只有在多个 Filter 级联使用时,NEUTRAL 才有意义,他表示由下一个 filter 决定是否 ACCEPT。

通常 filter 并不直接决定最终的结果,因为不同的场景下,filter 命中后的行为并不一定相同,因此 filter 只返回命中或未命中,然后由业务具体需要决定是否允许打印相应的日志是更好的选择。Log4j2 的 Filter 就是基于上述原则创建的,他提供了 onMatch(命中)与 onMisMatch(未命中)两个参数供用户配置,filter 值返回当前场景命中或未命中

Log4j2 允许你将 Filter 配置为全局有效或对某个 Appender 生效。

1、Filters常用模板

常用的过滤器如下几类,解析请查看注释

<!--每秒平均允许的日志事件数为16,最大等待处理日志数为100,onMatch和onMismatch的值可以为ACCEPT、DENY、NEUTRAL-->
<BurstFilter level="info" rate="16" maxBurst="100"  onMatch="NEUTRAL" onMismatch="DENY"  />

<!--基于特定值org.apache.logging.log4j.ThreadContext值,对特定key设置特定的日志级别,这里key为ThreadContext中的key名-->
<DynamicThresholdFilter key="tcKey" defaultThreshold="info" onMatch="ACCEPT" onMismatch="DENY" >
    <!--这里key为ThreadContext中的value值-->
    <KeyValuePair key="tcVal1" value="info" />
    <KeyValuePair key="tcVal2" value="error" />
</DynamicThresholdFilter>

<!--针对info(Message msg)类似方法过滤, 使用如:MapMessage mm = new MapMessage()-->
<MapFilter onMatch="ACCEPT" onMismatch="DENY" >
    <KeyValuePair key="mmKey1" value="mmVal1" />
    <KeyValuePair key="mmKey2" value="mmVal2" />
</MapFilter>

<!--针对info(Marker marker, String message)类似方法过滤,使用如:Marker mk = MarkerManager.getMarker("mk1")-->
<MarkerFilter marker="mk1" onMatch="ACCEPT" onMismatch="DENY" />

<!--正则过滤器,根据正则表达式对合要求的日志记录执行过滤-->
<RegexFilter regex=".*or.*" onMatch="ACCEPT" onMismatch="DENY" />

<!--基于特定值org.apache.logging.log4j.ThreadContext值过滤,operator为or时,表示可以匹配一项,否则为所有项都匹配-->
<ContextMapFilter onMatch="ACCEPT" onMismatch="DENY" operator="or">
    <!--这里key为ThreadContext中的key值,key为ThreadContext中的value值-->
    <KeyValuePair key="tcKey" value="tcVal1" />
</ContextMapFilter>

<!--基于日志级别的过滤器-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />

<!--基于时间的过滤器,时间格式为:HH:mm:ss-->
<TimeFilter start="00:00:00" end="01:00:00" onMatch="ACCEPT" onMismatch="DENY" />

<!--组合过滤器,内可包含多个过滤器-->
<Filters></Filters>

2、BurstFilter

BurstFilter 可以控制每秒日志量,对于超过数量的日志进行丢弃。实际就是:控制日志打印速度

  • level:筛选消息级别。如果超过maxBurst,则将过滤掉此级别或以下的任何内容。默认WARN,任何高于WARN的日志都被记录,无论Burst大小如何
  • rate:参数表示每秒最大日志数
  • maxBurst:参数则表示在开始过滤前允许多少条日志请求,默认是rate的10倍
  • onMatch:过滤器匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是NEUTRAL
  • onMismatch:当过滤器不匹配时要采取的操作。可以是ACCEPT、DENY、NEUTRAL,默认是DENY
<RollingFile name="RollingFile" fileName="logs/app.log" filePattern="logs/app-%d{MM-dd-yyyy}.log.gz">
    <!--每秒平均允许的日志事件数为16,最大等待处理日志数为100-->
    <BurstFilter level="INFO" rate="16" maxBurst="100"/>
    <PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
    <TimeBasedTriggeringPolicy />
</RollingFile>

3、TimeFilter

TimeFilter 是限制时间的Filter,允许只在一天中的指定时间进行日志记录:

  • start:开始时间,格式为:HH
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Log4j2日志框架 的相关文章

随机推荐

  • 电脑设置定时关机的5种方法

    转自 微点阅读 https www weidianyuedu com 方法汇总于网络 仅供参考 目录 如何用系统命令设置定时关机 两款定时关机软件 小而好用 功能强大 如何用任务计划程序设置 常用的电脑软件如何设置 包括360安全卫士 迅雷
  • Java中以英文逗号分割的字符串在前端添加时正则判断

    Java中以英文逗号分割的字符串在前端添加时正则判断 只能是英文状态逗号且只能以逗号隔开不能以逗号结尾 只能是英文状态逗号 不能有中文逗号 var m uff0c if goodstype match m alert 不能有中文逗号 ret
  • sql注入之万能密码总结

    万能密码 万能密码原理 原验证登陆语句 SELECT FROM admin WHERE Username username AND Password md5 password 输入 1 or 1 1 or 1 1万能密码语句变为 SELEC
  • systemd启动mysql后一直卡住,Systemd Mysql不会停止

    升级到15 04后 我有很多乐趣了解systemd 我想我一切正常 除了我无法阻止mysql service systemctl命令只是挂起而且mysql一直在运行 有没有其他人经历过这个或者可能知道发生了什么 解决方法 我有同样的问题 升
  • 蓝桥杯.剪格子(DFS)

    Question Solve 深搜板子题 分成两部分 两部分的数字和相同 dfs去创造路径 然后比对路径上的数字和与剩余点的数字和 优化点 读入时候先求和sum 路径和ans另算 直接去判断ans是不是sum的一半 ans gt sum 2
  • 理解fasterRCNN模型的构成,并进行训练和预测

    学习目标 了解VOC数据集的应用 理解fasterRCNN模型的构成 能够利用fasterRCNN网络模型进行训练和预测 1 VOC数据集简介 Pascal VOC数据集作为基准数据 在目标检测中常被使用到 很多优秀的计算机视觉模型比如分类
  • 逆向图片搜索 搜索自己想搜索的

    Tineye 是一个用图片搜索图片的技术 http www tineye com 开始时Tineye是邀请注册 后来是开放注册 不过都需要注册才能使用 现在终于完全放开 无需再注册或登录即可使用该搜索引擎 此外 Tineye最近还增添了一下
  • Vue+ElementUI el-radio列表单选

    实现效果 对某条数据进行数据修改 步骤 1 添加单选按钮 点击获取该条信息的id 并将id传给修改按钮 div 1 修改按钮 span size mini 修改信息 span 2 列表单选按钮
  • OptiSystem应用:光放大器EDFA的仿真

    Optisystem可以设计和模拟光纤放大器和光纤激光器 此处展示的案例可在Optisystem安装文件夹samplesOptical amplifiers中找到 该教程将会介绍光放大器库这一部分 光放大器 全局参数 使用Optisyste
  • Linux系统下Java 转换Word到PDF时,结果文档内容乱码的解决方法

    本文分享在Linux系统下 通过Java 程序代码将Word转为PDF文档时 结果文档内容出现乱码该如何解决 具体可参考如下内容 1 问题出现的背景 在Windows系统中 使用Spire Doc for Java将Word文档转换为PDF
  • [深度学习入门]Python基础语法(上)

    目录 一 程序设计基本方法 1 计算机是根据指令操作数据的设备 2 编程设计语言概述 3 计算机编程 4 IPO程序编写方法 5 使用计算机解决问题 二 基础知识 1 pyCharm 为人工智能领域常用的IDE 2 Python的简单使用
  • NVIDIA Shield 消失的解决办法和Moonlight串流

    Foreword 之前有用Moonlight串口pc的游戏到公司电脑 然后突然有一天串流就不可用了 NVIDIA Shield 就消失了 怎么都开不起来 串流就失败了 然后也记录一下Moonlight串流的操作 由于NVIDIA单方面宣布停
  • vue+element 根据状态,显示不同的操作按钮

    效果截图 VUE 核心功能代码片段
  • 【yolov5】yolov5训练自己的数据集全流程----包含本人设计的快速数据处理脚本

    关于yolo应用时能用到的脚本集合 推荐收藏 https chenlinwei blog csdn net article details 127299428 文章目录 1 工程化快速yolo训练流程指定版 无讲解 1 1 抽样数据集 xm
  • Spring MVC中如何进行转发和重定向呢?

    转自 Spring MVC中如何进行转发和重定向呢 重定向 我们将用户的定向到另一个视图 jsp 中处理 此操作是一个客户端行为 类似与url的链接操作 转发 将用户的请求转发到另一个视图或controller处理 此操作是一个服务器端行为
  • 【日常遇坑总结】类成员变量的空间分配和初始化顺序

    遇坑 今天在用QT的时候 传从主ui页面创建的一个指针到建模ui页面 在运行时程序发生奔溃 经过测试发现问题 主页面的指针和传进建模页面的指针不是同一个 导致在调用类指针方法时发生错误 测试 以下代码仅展示测试代码的部分 不可运行 但能从下
  • spring+ jcaptcha(spring框架下的彩色验证码)

    从jcaptcha官方网站下载jcaptcha的发行包 并将其发行包中的jar文件考贝到本地项目WEB INF目录下的lib目录中 官方网址http jcaptcha sourceforge net 在web xml文件中配置 Java代码
  • 嵌入式知识图谱WiKi(嵌入式开发/研发入门教程和路线图)

    嵌入式知识图谱WiKi 作者 将狼才鲸 创建时间 2022 02 18 因图床更新不方便 最新版请跳转到Gitee文档源文件仓库网址 才鲸 嵌入式知识图谱WiKi CSDN有图的文档阅读网址 嵌入式知识图谱WiKi Bilibili视频讲解
  • 数据结构--二叉树

    前言 关于二叉树知识的考察主要分两部分 第一部分在初赛中体现 一般考察二叉树的节点个数 树高和遍历问题 1 二叉树定义 在计算机科学中 二叉树是每个结点最多有两个子树的树结构 通常子树被称作 左子树 left subtree 和 右子树 r
  • Log4j2日志框架

    Log4j2日志框架 1 简介及入门示例 1 背景介绍 官网地址 https logging apache org log4j 2 x Maven 仓库地址 https search maven org artifact org apach