log4j2提供的过滤器(功能简述):
提示: 下图基于log4j2.13.3。
在这里插入图片描述
过滤器 说明 是否常用
StringMatchFilter 如果格式化后(即:最终)的日志信息中包含%20%3F%20onMatch%20%3A%20onMismatch%3B%09%E6%98%AF%0ALevelRangeFilter%09%E8%8B%A5#card=math&code=%7B%E6%8C%87%E5%AE%9A%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%7D%EF%BC%8C%E5%88%99onMatch%EF%BC%8C%E5%90%A6%E5%88%99onMismatch%0A%E5%8D%B3%EF%BC%9A%20msg.contains%28this.text%29%20%3F%20onMatch%20%3A%20onMismatch%3B%09%E6%98%AF%0ALevelRangeFilter%09%E8%8B%A5){maxLevel} <= 日志级别 <=
{指定的正则表达式},则onMatch,否则onMismatch
注:可通过useRawMsg属性来控制这个日志信息是格式化处理后(即:最终)的日志信息,还是格式化处理前(即:代码中输入)的日志信息。 是
ThresholdFilter 若日志级别 >= {指定的日志级别},则onMatch,否则onMismatch 是
ThreadContextMapFilter 通过context(可以理解为一个Map)中对应的key-value值进行过滤
注:上下文默认是ThreadContext,也可以自定义使用ContextDataInjectorFactory配置ContextDataInjector来指定。 是
DynamicThresholdFilter 若上下文中包含指定的key,则触发DynamicThresholdFilter生效;若该key对应的value值等于任意一个我们指定的值,那么针对本条日志,可记录日志级别的约束下限调整为指定的级别
注:上下文默认是ThreadContext,也可以自定义使用ContextDataInjectorFactory配置ContextDataInjector来指定。
示例说明:配置,有以下情况:
情况一:存在键loginRole,假设从上下文(可以理解为一个Map)中取出来的对应的值为user,那么此时,对于日志级别大于等于warn的日志,会走onMatch;其它的日志级别走onMismatch。
情况二:存在键loginRole,假设从context(可以理解为一个Map)中取出来的对应的值为admin,那么此时,对于日志级别大于等于debug的日志,会走onMatch;其它的日志级别走onMismatch。
情况三:【上下文(可以理解为一个Map)中,不存在键loginRole】或【存在键loginRole,但从日志上下文中取出来的值(假设)为abc, 没有对应的KeyValuePair配置】,那么此时等价于。 是
CompositeFilter 组合过滤器,即:按照xml配置中的配置,一个过滤器一个过滤器的走,如果在这过程中,任意一个过滤器ACCEPT或DENY了,那么就不会往后走了,直接返回对应的结果。 是
TimeFilter 如果记录日志时的当前时间落在每天指定的时间范围[start, end]内,则onMatch,否则onMismatch
如:。 否
ScriptFilter 是否匹配取决于指定的脚本返回值是否为true 否
DenyAllFilter This filter causes all logging events to be dropped 否
BurstFilter 对低于或等于{指定的值}, 则onMatch,否则onMismatch 否
MapFilter The MapFilter allows filtering against data elements that are in a MapMessage.
注:需要使用org.apache.logging.log4j.Loggerj进行记录,且记录org.apache.logging.log4j.message.MapMessage日志,才会生效。
注:因为暂时不兼容Slf4j这里不多作说明 否
StructuredDataFilter The StructuredDataFilter is a MapFilter that also allows filtering on the event id, type and message.
注:需要使用org.apache.logging.log4j.Loggerj进行记录,且记录org.apache.logging.log4j.core.filter.StructuredDataFilter日志,才会生效。
注:因为暂时不兼容Slf4j这里不多作说明 否
… … …
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><!-- 去掉springboot默认配置 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><!-- 引入log4j2依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency>
指定配置文件位置
logging:config: classpath:log4j2-spring.xml
配置
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--变量配置-->
<Properties>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<!-- %logger{36} 表示 Logger 名字最长36个字符 -->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<!-- 定义日志存储的路径 -->
<property name="FILE_PATH" value="./logs" />
<property name="FILE_NAME" value="li-iwu" />
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="${LOG_PATTERN}"/>
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
<PatternLayout pattern="${LOG_PATTERN}"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1 hour-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
</appenders>
<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
<!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.mybatis" level="info" additivity="false">
<AppenderRef ref="Console"/>
</logger>
<!--监控系统信息-->
<!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
<Logger name="org.springframework" level="info" additivity="false">
<AppenderRef ref="Console"/>
</Logger>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="Filelog"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
Log4j2配置文件中引用application.yml参数
新增GenericApplicationListener拦截系统启动过程
import org.slf4j.MDC;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import java.util.List;
import java.util.stream.Collectors;
public class ApplicationStartEvent implements GenericApplicationListener {
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
private static Class<?>[] EVENT_TYPES = {
ApplicationStartingEvent.class,
ApplicationEnvironmentPreparedEvent.class,
ApplicationPreparedEvent.class,
ContextClosedEvent.class,
ApplicationFailedEvent.class
};
private static Class<?>[] SOURCE_TYPES = {
SpringApplication.class,
ApplicationContext.class
};
@Override
public boolean supportsEventType(ResolvableType eventType) {
return isAssignableFrom(eventType.getRawClass(), EVENT_TYPES);
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return isAssignableFrom(sourceType, SOURCE_TYPES);
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
try {
ConfigurableEnvironment envi = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment();
MutablePropertySources mps = envi.getPropertySources();
List<PropertySource<?>> psList = mps.stream()
.filter(item -> item.getName().startsWith("applicationConfig") || item.getName().startsWith("commandLineArgs"))
.sorted((o1, o2) -> {
boolean o1StartWithCommandLineArgs = o1.getName().startsWith("commandLineArgs");
boolean o2StartWithCommandLineArgs = o2.getName().startsWith("commandLineArgs");
if (o1StartWithCommandLineArgs != o2StartWithCommandLineArgs) {
return o1StartWithCommandLineArgs ? -1 : 1;
}
int o1NameLength = o1.getName().length();
int o2NameLength = o2.getName().length();
return o2NameLength - o1NameLength;
})
.collect(Collectors.toList());
String logLocation = getPropertyInPropertySourceList("log.location", 0, psList);
MDC.put("log.location", logLocation);
} catch (Exception e) {
System.out.println("初始化参数发生错误:" + e.getMessage());
e.printStackTrace();
MDC.put("log.location", "/home/iwu/runningstat");
}
}
}
/**
* 获取配置
* @param key
* @param currentIndex
* @param psList
* @return
*/
private String getPropertyInPropertySourceList(String key, Integer currentIndex, List<PropertySource<?>> psList) {
String value = null;
if (currentIndex >= psList.size()) {
return value;
}
PropertySource<?> propertySource = psList.get(currentIndex);
if (propertySource.containsProperty(key)) {
value = propertySource.getProperty(key).toString();
if (value.startsWith("$")) {
String replaceKey = value.replaceAll("\\$", "").replaceAll("\\{", "").replaceAll("\\}", "");
value = getPropertyInPropertySourceList(replaceKey, currentIndex, psList);
}
} else {
value = getPropertyInPropertySourceList(key, currentIndex + 1, psList);
}
return value;
}
private boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
if (type != null) {
for (Class<?> supportedType : supportedTypes) {
if (supportedType.isAssignableFrom(type)) {
return true;
}
}
}
return false;
}
}
修改启动命令,增加监听器
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new ApplicationStartEvent());
app.run(args);
配置中间中引用参数
<property name="FILE_PATH" value="${ctx:log.location}" />
常用的配置参数
日志级别
机制:如果一条日志信息的级别大于等于配置文件的级别,就记录。
- trace:追踪,就是程序推进一下,可以写个trace输出
- debug:调试,一般作为最低级别,trace基本不用。
- info:输出重要的信息,使用较多
- warn:警告,有些信息不是错误信息,但也要给程序员一些提示。
- error:错误信息。用的也很多。
-
输出源
CONSOLE(输出到控制台)
-
格式
SimpleLayout:以简单的形式显示
- HTMLLayout:以HTML表格显示
- PatternLayout:自定义形式显示
PatternLayout自定义日志布局:
%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间 %-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0%c : logger的名称(%logger) %t : 输出当前线程名称 %p : 日志输出格式 %m : 日志内容,即 logger.info(“message”) %n : 换行符 %C : Java类名(%F) %L : 行号 %M : 方法名 %l : 输出语句所在的行数, 包括类名、方法名、文件名、行数 hostName : 本地机器名 hostAddress : 本地ip地址
根节点Configuration有两个属性:
- status
- monitorinterval
有两个子节点:
- Appenders
- Loggers(表明可以定义多个Appender和Logger).
status用来指定log4j本身的打印日志的级别.
monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.
- name:指定Appender的名字.
- target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.
PatternLayout:输出格式,不设置默认为:%m%n.
File节点用来定义输出到指定位置的文件的Appender.
name:指定Appender的名字.
- fileName:指定输出日志的目的文件带全路径的文件名.
PatternLayout:输出格式,不设置默认为:%m%n.
RollingFile节点用来定义超过指定条件自动删除旧的创建新的Appender.
name:指定Appender的名字.
- fileName:指定输出日志的目的文件带全路径的文件名.
- PatternLayout:输出格式,不设置默认为:%m%n.
- filePattern : 指定当发生Rolling时,文件的转移和重命名规则.
- Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.
- TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am.
- SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
Loggers节点,常见的有两种:Root和Logger.
Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.
- Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
- level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
- name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity=”false”只在自定义的Appender中进行输出。
Filter.Result的三种过滤结果
log4j2走过滤器的逻辑后,会返回对应的过滤Result结果,以控制是否记录日志、怎样记录日志。过滤器的结果有:

ACCEPT:不需要再走后面的过滤器了,)需要记录当前日志。NEUTRAL:需不需要记录当前日志,由后续过滤器决定。若所有过滤器返回的结果都是NEUTRAL,那么需要记录日志。DENY:不记录当前日志。
提示:log4j2的此机制与logback是一样的。log4j2提供的过滤器(功能简述):
提示: 下图基于log4j2.13.3。
| 过滤器 | 说明 | 是否常用 |
|---|---|---|
StringMatchFilter |
如果格式化后(即:最终)的日志信息中包含${指定的字符串},则onMatch,否则onMismatch即: msg.contains(this.text) ? onMatch : onMismatch; |
是 |
LevelRangeFilter |
若${maxLevel} <= 日志级别 <= ${minLevel}, 则onMatch,否则onMismatch如: 即为只记录日志info及warn级别的日志。 |
是 |
RegexFilter |
如果日志信息匹配${指定的正则表达式},则onMatch,否则onMismatch注:可通过useRawMsg属性来控制这个日志信息是格式化处理后(即:最终)的日志信息,还是格式化处理前(即:代码中输入)的日志信息。 |
是 |
ThresholdFilter |
若日志级别 >= ${指定的日志级别}, 则onMatch,否则onMismatch |
是 |
LevelMatchFilter |
如果日志级别等于${指定的日志级别},则onMatch,否则onMismatch |
是 |
ThreadContextMapFilter |
通过context(可以理解为一个Map)中对应的key-value值进行过滤 注:上下文默认是ThreadContext,也可以自定义使用ContextDataInjectorFactory配置ContextDataInjector来指定。 |
是 |
DynamicThresholdFilter |
若上下文中包含指定的key,则触发DynamicThresholdFilter生效;若该key对应的value值等于任意一个我们指定的值,那么针对本条日志,可记录日志级别的约束下限调整为指定的级别 注:上下文默认是ThreadContext,也可以自定义使用ContextDataInjectorFactory配置ContextDataInjector来指定。 示例说明: <DynamicThresholdFilter key="loginRole" defaultThreshold="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL"><KeyValuePair key="admin" value="DEBUG"/><KeyValuePair key="user" value="warn"/></DynamicThresholdFilter>配置,有以下情况:情况一:存在键loginRole,假设从上下文(可以理解为一个Map)中取出来的对应的值为user,那么此时,对于日志级别大于等于warn的日志,会走onMatch;其它的日志级别走onMismatch。 情况二:存在键loginRole,假设从context(可以理解为一个Map)中取出来的对应的值为admin,那么此时,对于日志级别大于等于debug的日志,会走onMatch;其它的日志级别走onMismatch。 情况三:【上下文(可以理解为一个Map)中,不存在键loginRole】或【存在键loginRole,但从日志上下文中取出来的值(假设)为abc, 没有对应的KeyValuePair配置】,那么此时 <DynamicThresholdFilter key="userRole" defaultThreshold="AAA" onMatch="BBB" onMismatch="CCC">等价于<LevelMatchFilter level="AAA" onMatch="BBB" onMismatch="CCC">。 |
是 |
CompositeFilter |
组合过滤器,即:按照xml配置中的配置,一个过滤器一个过滤器的走,如果在这过程中,任意一个过滤器ACCEPT或DENY了,那么就不会往后走了,直接返回对应的结果。 | 是 |
| TimeFilter | 如果记录日志时的当前时间落在每天指定的时间范围[start, end]内,则onMatch,否则onMismatch如: <TimeFilter start="05:00:00" end="05:30:00" onMatch="ACCEPT" onMismatch="DENY"/>。 |
否 |
| ScriptFilter | 是否匹配取决于指定的脚本返回值是否为true | 否 |
| DenyAllFilter | This filter causes all logging events to be dropped | 否 |
| BurstFilter | 对低于或等于${指定日志级别}的日志,进行限流控制 |
否 |
| NoMarkerFilter | 如果从对应事件对象获取(LogEvent#getMarker)到的marker为null, 则onMatch,否则onMismatch | 否 |
| MarkerFilter | 如果从对应事件对象获取(LogEvent#getMarker)到的marker的name值为等于${指定的值}, 则onMatch,否则onMismatch |
否 |
| MapFilter | The MapFilter allows filtering against data elements that are in a MapMessage. 注:需要使用org.apache.logging.log4j.Loggerj进行记录,且记录org.apache.logging.log4j.message.MapMessage日志,才会生效。 注:因为暂时不兼容Slf4j这里不多作说明 |
否 |
| StructuredDataFilter | The StructuredDataFilter is a MapFilter that also allows filtering on the event id, type and message. 注:需要使用org.apache.logging.log4j.Loggerj进行记录,且记录org.apache.logging.log4j.core.filter.StructuredDataFilter日志,才会生效。 注:因为暂时不兼容Slf4j这里不多作说明 |
否 |
| … | … | … |
Filter的作用范围:
log4j2在处理日志时,各个Filter会组成过滤链,越靠前的Filter越先过滤,自然影响范围就越大。在log4j2的xml配置文件中,Filter可以配置在四个位置,由全局到局部依次是 Context-wide、 Logger和Appender、AppenderReference, 图示说明:
