Log4J JsonTemplateLayout日志手机号替换失效问题求助
Log4J 手机号脱敏配置问题解决
问题背景
使用Log4J记录日志时,原配置依赖已弃用的LogStashJsonLayout,为实现手机号脱敏(替换为3********9),切换为JsonTemplateLayout并配置了替换规则,但日志中仍出现明文手机号。
原RollingFile Appender配置:
<RollingFile name="FILE-LOGSTASH" fileName="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash.log" filePattern="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash-%d{yyyy-MM-dd-HH}.log.gz"> <LogStashJsonLayout/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile>
修改后的配置:
<RollingFile name="FILE-LOGSTASH" fileName="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash.log" filePattern="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash-%d{yyyy-MM-dd-HH}.log.gz"> <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile>
对应的EcsLayout.json模板:
{ "@version": "1", "@timestamp": { "$resolver": "timestamp", "pattern": { "format": "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "timeZone": "UTC" } }, "logger": { "$resolver": "logger", "field": "name" }, "priority": { "$resolver": "level", "field": "name" }, "thread": { "$resolver": "thread", "field": "name" }, "message": { "$resolver": "message", "replace": { "regex": "(\\d{2})(\\d{7,11})(\\d{2})", "replacement": "$1********$3" } } }
问题原因
- 正则表达式匹配错误:原正则
(\\d{2})(\\d{7,11})(\\d{2})匹配2位前缀+7-11位中间+2位后缀,总长度11-15位,不符合国内11位手机号的结构(1位前缀+9位中间+1位后缀),导致无法匹配目标手机号。 - 替换规则不符合需求:若目标是将任意手机号替换为固定的
3********9,原规则保留前后两位的逻辑与需求不符。
解决方案
方案一:修正JsonTemplateLayout模板实现脱敏
根据需求调整正则和替换规则:
情况1:将所有11位手机号替换为固定字符串3********9
修改模板中的message节点:
"message": { "$resolver": "message", "replace": { "regex": "\\b1[3-9]\\d{9}\\b", "replacement": "3********9" } }
\\b用于匹配单词边界,避免匹配长数字串中的片段;1[3-9]\\d{9}精准匹配国内标准11位手机号。
情况2:保留手机号首尾1位,中间8位替换为*(如13812345679→1********9)
修改模板中的message节点:
"message": { "$resolver": "message", "replace": { "regex": "(\\d)(\\d{9})(\\d)", "replacement": "$1********$3" } }
方案二:使用Log4J过滤器实现全局脱敏
若手机号可能出现在日志的多个字段(如MDC、参数等),推荐用RewriteAppender配合自定义过滤器:
- 自定义脱敏过滤器类:
import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.SimpleMessage; public class PhoneNumberRewritePolicy implements RewritePolicy { private static final String PHONE_REGEX = "\\b1[3-9]\\d{9}\\b"; private static final String REPLACEMENT = "3********9"; @Override public LogEvent rewrite(LogEvent event) { Message originalMessage = event.getMessage(); String modifiedMessage = originalMessage.getFormattedMessage().replaceAll(PHONE_REGEX, REPLACEMENT); Message newMessage = new SimpleMessage(modifiedMessage); return new Log4jLogEvent.Builder(event).setMessage(newMessage).build(); } }
- 修改Log4J配置,添加
RewriteAppender:
<Configuration> <Appenders> <RollingFile name="FILE-LOGSTASH-ORIGINAL" fileName="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash.log" filePattern="C:\Users\p.bohomaz\IdeaProjects\o2-lcm\log4j-logstash-%d{yyyy-MM-dd-HH}.log.gz"> <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile> <Rewrite name="REWRITE-PHONE"> <RewritePolicy class="com.yourpackage.PhoneNumberRewritePolicy"/> <AppenderRef ref="FILE-LOGSTASH-ORIGINAL"/> </Rewrite> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="REWRITE-PHONE"/> </Root> </Loggers> </Configuration>
验证注意事项
- 确保Log4J版本≥2.17.0(
JsonTemplateLayout的replace功能需该版本支持); - 若日志通过参数化方式生成(如
logger.info("手机号: {}", phone)),需确认格式化后的字符串能被正则匹配; - 检查日志中手机号的出现位置,若不在
message字段,需在模板中对应字段添加替换规则,或使用过滤器实现全局处理。
内容的提问来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

