Log4j2自定义插件实现自定义日志打印功能(脱敏/加密)

Log4j2 中文文档

1. 自定义appender插件

import cn.hutool.core.util.DesensitizedUtil;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * log4j1.x重写log4j的PatternLayout,实现日志信息中敏感信息的编码或加密
 * log4j2.x重写log4j的AbstractStringLayout,实现日志信息中敏感信息的编码或加密
 *
 * @author Zyx
 * @since 2022/8/24 10:55
 */
@Plugin(name = "Log4jEncodeLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
public class Log4jEncodeLayout extends AbstractStringLayout {

    /**
     * 手机号正则匹配式
     * 实测雪花算法生成的Id可能会被手机号正则匹配到,故加了关键正则过滤:
     *  (?<![0-9a-zA-Z]):手机号前面紧挨的不能是字母和数字
     *  (?![0-9a-zA-Z]):手机号后面紧挨的不能是字母和数字
     */
    private final static Pattern PHONE_PATTERN = Pattern.compile("(?<![0-9a-zA-Z])1[345789]\d{9}(?![0-9a-zA-Z])");


    private PatternLayout patternLayout;

    protected Log4jEncodeLayout(Charset charset, String pattern) {
    	//调用父类设置基本参数
        super(charset);
        //PatternLayout 是原本的输出对象,用来获取到原本要输出的日志字符串
        patternLayout = PatternLayout.newBuilder().withPattern(pattern).build();
    }

    @Override
    public String toSerializable(LogEvent event) {
        //调用原本的 toSerializable 方法,获取到原本要输出的日志
        String message = patternLayout.toSerializable(event);

        //在原本输出的字符串上做正则匹配过滤
        Matcher match = PHONE_PATTERN.matcher(message);

        StringBuffer sb = new StringBuffer();

        while (match.find()) {
            match.appendReplacement(sb, DesensitizedUtil.mobilePhone(match.group()));
        }
        match.appendTail(sb);// 增加

		// 将脱敏后的日志输出
        return sb.toString();
    }

	//定义插件传入的参数
    @PluginFactory
    public static Layout createLayout(
            @PluginAttribute(value = "pattern") final String pattern,
            // LOG4J2-783 use platform default by default, so do not specify
            // defaultString for charset
            @PluginAttribute(value = "charset") final Charset charset) {
        return new Log4jEncodeLayout(charset, pattern);
    }
}

2. log4j2.xml配置

需要修改两个地方:

  1. Configuration 标签中 packages 参数需指定自定义插件的位置
  2. 日志打印的地方替换为上面自定义的插件: <Log4jEncodeLayout pattern="..." charset="UTF-8"/>
<?xml version="1.0" encoding="UTF-8"?>
<!--packages 参数指定插件包路径,多个路径用逗号隔开-->
<Configuration status="INFO" name="XMLConfigTest" packages="org.apache.logging.log4j.test,com.zyx.demo">
    <Properties>
        <Property name="PATTERN">
            %d{yyyy-MM-dd HH:mm:ss SSS} [%p] [c=%c{1}] [%thread] %m%n
        </Property>
        <property name="MODULE_NAME">log4j2-demo</property>
        <property name="LOG_HOME">/data</property>
    </Properties>



    <Appenders>
        <Console name="STDOUT">
            <!--<PatternLayout>-->
            <!--    <pattern>${PATTERN}</pattern>-->
            <!--</PatternLayout>-->
            <!--将原先的日志输出替换为自定义的日志输出appender插件-->
            <Log4jEncodeLayout pattern="${PATTERN}" charset="UTF-8"/>
        </Console>
        <RollingFile name="ROLLINGFILE" fileName="${LOG_HOME}/${MODULE_NAME}.log"
                     filePattern="${LOG_HOME}/log/${MODULE_NAME}-%d{yyyy-MM-dd}-%i.log.gz">
            <!--<PatternLayout-->
                    <!--pattern="[${MODULE_NAME}] %d{yyyy-MM-dd HH:mm:ss SSS} [%p] [c=%c{1}] [%thread] %m%n"/>-->
            <!--将原先的日志输出替换为自定义的日志输出appender插件-->
            <Log4jEncodeLayout pattern="[${MODULE_NAME}] %d{yyyy-MM-dd HH:mm:ss SSS} [%p] [c=%c{1}] [%thread] %m%n" charset="UTF-8"/>
            <Policies>
                <TimeBasedTriggeringPolicy modulate="true"
                                           interval="1" />
                <SizeBasedTriggeringPolicy size="100MB"/>
                <CronTriggeringPolicy schedule="0 0 * * * ?"/> <!-- 这里是每小时监测一次 -->
            </Policies>
            <DefaultRolloverStrategy max="100">
                <Delete basePath="${LOG_HOME}" maxDepth="3">
                    <IfFileName glob="*/${MODULE_NAME}-*.log.gz"/>
                    <IfLastModified age="30d"/> <!-- 这里保留30天 -->
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>
    </Appenders>

    <Loggers>
        <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
        <Root level="INFO">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Root>
        <Logger name="com.snbc.vems" level="INFO" additivity="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Logger>
        <Logger name="org.apache" level="WARN" additivity="false" includeLocation="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Logger>
        <Logger name="org.mybatis" level="WARN" additivity="false" includeLocation="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Logger>
        <Logger name="org.hibernate" level="WARN" additivity="false" includeLocation="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Logger>
        <Logger name="org.springframework" level="WARN" additivity="false" includeLocation="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="ROLLINGFILE"/>
        </Logger>
    </Loggers>
</Configuration>

3. 参数说明

1)定义标签(@Plugin)

@Plugin(name = "MyAppender", category = "Core", elementType = "layout", printObject = true)
  • name:插件的名称。请注意,此名称不区分大小写
  • category:用于放置插件的类别。类别名称区分大小写
  • elementType:此插件所属元素的相应类别的名称,当前扩展所属元素的类别是 layout(layout:日志处理重新输出,appender:日志追加处理,如存入数据库中)。
  • printObject:指示插件类是否实现 Object.toString() 方法,消息中使用。

2)定义标签参数或子元素(@PluginFactory)

createAppender 方法,用于 log4j2 在扫描到插件之后根据配置文件中的配置生成插件自定义的插件对象。

  • @PluginAttribute:是指插件的属性,如

    @PluginAttribute("name") String name
    

    对应的 xml 配置是:

    <MyAppender name="MyAppenderTest"></Realtimeval>
    

    会取标签内name的值

  • @PluginElement:是指插件的子元素,如

    @PluginElement("AppenderRef") AppenderRef[] appenderRefs
    

    对应的 xml 配置是:

    <MyAppender name="MyAppenderTest">
    	<AppenderRef ref="AsyncMqLog"/>
    	<AppenderRef ref="AsyncCONSOLE"/>
    </MyAppender>
    

    会获取标签下AppenderRef 元素的值,如果是多个AppenderRef 子元素,将会获取都一个数组,可以根据业务需要自定义元素或者属性。

4. 获取容器中对象

再说一个上下文的问题,log4j2在dao层service层初始化结束之前就已经初始化了,如果采用@Resource这种依赖注入的方式构建bean是行不通的,获取到的只能是null,但是ApplicationContext已经加载,可以通过ApplicationContext手动获取bean。

当我们在 Spring 框架中使用 log4j2 框架时,可能你想要让 Appender 接收外部 spring 容器中的 bean。如果使用 @Autowared 注入获取对象为 null 时,则实现 ApplicationContextAware 接口,通过 ApplicationContext 来直接获取容器中的对象。

5. Mybatis 设置日志打印实现为Log4j2Impl

自定义 Log 对象的实现类,该类中使用 Log4j2 打印日志,同时配置 Mybatis 使用自定义的 Log 对象,即可实现对 Mybatis 打印的 sql 日志脱敏

public class Log4j2Impl implements Log {

    private final Log logImpl;

    /**
     * Instantiates a new Snbc log 4 j 2.
     *
     * @param clazz the clazz
     */
    public Log4j2Impl(String clazz) {
        Logger tmplLog = LogManager.getLogger(clazz);

        if (tmplLog instanceof AbstractLogger) {
            logImpl = new Log4j2AbstractLoggerImpl((AbstractLogger) tmplLog);
        } else {
            logImpl = new Log4j2LoggerImpl(tmplLog);
        }
    }

    public boolean isDebugEnabled() {
        return true;
    }

    public boolean isTraceEnabled() {
        return true;
    }

    public void error(String s, Throwable e) {
        logImpl.error(s, e);
    }

    public void error(String s) {
        logImpl.error(s);
    }

    public void debug(String s) {
        logImpl.debug(s);
    }

    public void trace(String s) {
        logImpl.trace(s);
    }

    public void warn(String s) {
        logImpl.warn(s);
    }
}
mybatis:
    configuration:
		log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl

参考

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值