10-Log4j2 之 Lookup

1、概述

“Lookups provide a way to add values to the Log4j configuration at arbitrary places. They are a particular type of Plugin that implements the StrLookup interface. “ —— log4j2 的官方文档lookup

“Lookups 提供了一种在任意位置向 Log4j 配置添加值的方法。它们是实现 StrLookup 接口的特定类型的插件。” —— log4j2 的中文文档lookup

lookups 的主要功能就是提供另外一种方式 以添加某些特殊的值到日志中,以最大化松散耦合地提供可配置属性供使用者以约定的格式进行调用

Lookups 是一种机制,用于动态获取和替换日志记录中的变量或属性的值。它提供了一种灵活的方式,可以在日志消息中引用、解析和插入各种上下文相关的信息。

Log4j2 内置了多个 Lookup 实现,每个 Lookup 都有不同的用途和功能。以下是一些常见的 Lookup 示例:

  1. ${date}:获取当前日期和时间,支持自定义格式。
  2. ${pid}:获取当前进程的 ID。
  3. ${logLevel}:获取当前日志记录的级别。
  4. ${sys:propertyName}:获取系统属性的值,例如 ${sys:user.home} 获取用户主目录。
  5. ${env:variableName}:获取环境变量的值,例如 ${env:JAVA\_HOME} 获取 Java 安装路径。
  6. ${ctx:key}:获取日志线程上下文(ThreadContext)中指定键的值。
  7. ${class:fullyQualifiedName:methodName}:获取指定类的静态方法的返回值。
  8. ${mdc:key}:获取 MDC (Mapped Diagnostic Context) 中指定键的值。

2、配置示例

以下列举了两个主要使用的位置;当然不仅仅如此,log4j2 允许你在任何需要的地方使用约定格式来获取环境中的指定配置信息。

1
2
3
4
5
6
7
8
<properties>
<!-- 之后我们就可以以 ${logPath}来引用该属性值 -->
<property name="logPath">${sys:catalina.home}/xmlogs</property>
</properties>

<!-- 这里的${hostName} 是由log4j2默认提供的, 其值为程序所在的服务器的主机名 -->
<!-- 至于${thread:threadName}, 将是本次我们所提供一个自定义lookup示例 -->
<PatternLayout pattern="[${hostName}];[${thread:threadName}];[%X{user}];[$${ctx:user}];[$${date:YYYY-MM/dd}]" />

3、继承链

在开始构建自定义 lookup 逻辑前,我们先来看看 log4j2 已经为我们提供了的各类 lookup 实现;这样既可以避免作些无用功,也能在我们的自定义实现中最大化地复用现有代码,站在巨人的肩膀上。

StrLookup继承链

由以上类层次结构图可以看出

  1. log4j2 提供不下十种获取所运行环境配置信息的方式,基本能满足实际运行环境中获取各类配置信息的需求。
  2. 我们在自定义 lookup 时,可以根据自身需求自由选择继承自 StrLookupAbstractLookupAbstractConfigurationAwareLookup 等等来简化我们的代码。

以上默认提供的各类lookup,其取值来源看官可以通过下面给出的引用链接中的第二个进行详细的了解,我就不再在这里赘述一遍了。

4、自定义 lookup

下面我们将自定义一个 lookup,以获取记录日志的线程名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 从这里的注解可以看出,lookup 属于一类特殊的 plugin
@Plugin(name = "thread", category = StrLookup.CATEGORY)
public class ThreadLookup implements StrLookup {
@Override
public String lookup(String key) {
return Thread.currentThread().getName();
}

@Override
public String lookup(LogEvent event, String key) {
return event.getThreadName() == null ? Thread.currentThread().getName() : event
.getThreadName();
}
}

可以看到自定义 lookup 的操作非常简单,log4j2 的设计精妙由此也可见一斑。接下来我们就需要进行一些配置工作,让 log4j2 知道如何使用该 lookup。

1
2
3
4
5
6
<!--使用packages属性告知log4j2我们自定义的plugin所在的package; 注意多个package是以 , 进行分割 -->
<Configuration status="TRACE" monitorInterval="5"
packages="com.kanq.extend.cat.log4j2,slf4j._log4j2.classes.core.lookup">
<!--调用方式就是多了一个我们在定义plugin时声明的名称作为前缀 -->
<PatternLayout pattern="${thread:threadName}" />
</Configuration>

最后附上一张执行堆栈图,还是比较清晰的

执行堆栈图

5、补充

接下来我们来探索一些稍微深入的内容,以及一些细节性的内容。

  1. 作为 lookup 对外门面的 Interpolator 是通过 log4j2 中负责解析 <properties> 节点的 PropertiesPlugin 类来并入执行流程中的。具体源码可以参见 PropertiesPlugin.configureSubstitutor 方法。其中注意的是,我们在 <properties> 中提供的属性是以 default 的优先级提供给外界的
  2. 作为 lookup 对外门面的 Interpolator,在其构造函数中载入了所有 category 值为 StrLookup.CATEGORY 的 plugin【即包括 log4j2 内置的(“org.apache.logging.log4j.core” package下的),也包括用户自定义的(log4j2.xml文件中的 Configuration.packages 属性值指示的package下的)】。
  3. Interpolator 可以单独使用,但某些值可能取不到。
  4. 获取MDC中的内容,log4j2 提供了两种方式:$${ctx:user}%X{user}

Reference


10-Log4j2 之 Lookup
https://flepeng.github.io/021-Java-31-API-10-Log4j2-之-Lookup/
作者
Lepeng
发布于
2023年3月23日
许可协议