log4j2 是 java 中最常用且支持自定义 patternlayout 的日志框架;它既可通过配置文件快速定制格式,也支持继承 patternlayout 或注册插件实现动态扩展,还可结合 jsonlayout 构建结构化日志体系。

在 Java 日志系统中,最常用且支持自定义 PatternLayout 的是 Log4j2(注意不是 Log4j 1.x)。Log4j2 提供了高度可扩展的布局机制,你可以通过 XML/JSON/YAML 配置直接使用内置的 PatternLayout,也可以编写自定义 Layout 类实现完全个性化的日志格式输出。
用内置 PatternLayout 快速定制日志格式
无需写 Java 代码,仅靠配置就能满足大多数个性化需求。Log4j2 的 PatternLayout 支持丰富转换符(Conversion Patterns),例如:
-
%d{yyyy-MM-dd HH:mm:ss.SSS}:带毫秒的时间 -
%t:线程名 -
%p:日志级别(INFO、ERROR 等) -
%c{1}:简写类名(如UserService) -
%M:方法名 -
%m%n:日志消息 + 换行 -
%X{traceId}:MDC 中的 traceId(可用于链路追踪)
示例 log4j2.xml 配置:
<configuration><appenders><console name="Console" target="SYSTEM_OUT"><patternlayout pattern="%d{HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %X{traceId} - %m%n"></patternlayout></console></appenders><loggers><root level="info"><appenderref ref="Console"></appenderref></root></loggers></configuration>扩展 PatternLayout:继承 PatternLayout 实现自定义逻辑
当需要动态插入内容(如统一加前缀、混淆敏感字段、注入运行时环境变量),可继承 org.apache.logging.log4j.core.layout.PatternLayout 并重写关键方法:
- 重写
toSerializable(LogEvent event):在序列化前修改LogEvent或其上下文(如向 MDC 添加字段) - 或更推荐:继承
org.apache.logging.log4j.core.layout.PatternLayout.Builder,注册自定义PatternConverter
常见场景:自动添加服务名和主机 IP
public class CustomPatternLayout extends PatternLayout {
private final String serviceName = System.getProperty("service.name", "unknown");
private final String host = getLocalHost();
<p>private String getLocalHost() {
try { return InetAddress.getLocalHost().getHostName(); }
catch (Exception e) { return "unknown"; }
}</p><p>@Override
public Serializable toSerializable(LogEvent event) {
// 向 MDC 注入服务与主机信息(确保后续 pattern 可引用 %X{svc} %X{host})
ThreadContext.put("svc", serviceName);
ThreadContext.put("host", host);
return super.toSerializable(event);
}
}</p>注册自定义 Layout 到 Log4j2 配置中
自定义 Layout 需配合插件机制注册,步骤如下:
- 在类上添加
@Plugin(name = "CustomPatternLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE) - 提供静态
createLayout()工厂方法,并用@PluginFactory标注 - 在 log4j2.xml 中通过
type="CustomPatternLayout"引用
示例插件声明片段:
@Plugin(name = "CustomPatternLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE)
public class CustomPatternLayout extends PatternLayout {
@PluginFactory
public static CustomPatternLayout createLayout(
@PluginAttribute("pattern") String pattern,
@PluginAttribute("alwaysWriteExceptions") boolean alwaysWriteExceptions,
@PluginAttribute("noConsoleNoAnsi") boolean noConsoleNoAnsi,
@PluginAttribute("disableAnsi") boolean disableAnsi,
@PluginAttribute("escapeUnicode") boolean escapeUnicode) {
String finalPattern = StringUtils.defaultString(pattern, "%d %p %c{1.} [%t] %m%n");
return new CustomPatternLayout(finalPattern, alwaysWriteExceptions,
noConsoleNoAnsi, disableAnsi, escapeUnicode);
}
// ... 构造函数和 toSerializable 实现
}然后在配置中使用:
<console name="Console" target="SYSTEM_OUT"><custompatternlayout pattern="%d{ISO8601} [%t] %p %c{1} - %X{svc}@%X{host} - %m%n"></custompatternlayout></console>替代方案:用 JsonLayout + 自定义 ObjectMapper(适合结构化日志)
若目标是发送日志到 ELK、Loki 等系统,推荐用 JsonLayout 并自定义序列化逻辑:
- 继承
JsonLayout,重写toSerializable()返回ObjectNode - 或配置
JsonLayout的objectMapper,注册自定义 Serializer 处理特殊字段(如脱敏手机号) - 配合
KeyValuePair在配置中静态添加字段:<keyvaluepair key="env" value="prod"></keyvaluepair>
这种方式比纯 Pattern 更易维护、更利于下游解析,适合云原生日志体系。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











