• 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可能会被手机号正则匹配到,故加了关键正则过滤:
         *  (?
        private final static Pattern PHONE_PATTERN = Pattern.compile("(?);
    
    
        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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    2. log4j2.xml配置

    需要修改两个地方:

    1. Configuration 标签中 packages 参数需指定自定义插件的位置
    2. 日志打印的地方替换为上面自定义的插件:
    
    
    <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-demoproperty>
            <property name="LOG_HOME">/dataproperty>
        Properties>
    
    
    
        <Appenders>
            <Console name="STDOUT">
                
                
                
                
                <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">
                
                        
                
                <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"/> 
                    Delete>
                DefaultRolloverStrategy>
            RollingFile>
        Appenders>
    
        <Loggers>
            
            <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    3. 参数说明

    1)定义标签(@Plugin)

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

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

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

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

      @PluginAttribute("name") String name
      
      • 1

      对应的 xml 配置是:

      <MyAppender name="MyAppenderTest">Realtimeval>
      
      • 1

      会取标签内name的值

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

      @PluginElement("AppenderRef") AppenderRef[] appenderRefs
      
      • 1

      对应的 xml 配置是:

      <MyAppender name="MyAppenderTest">
      	<AppenderRef ref="AsyncMqLog"/>
      	<AppenderRef ref="AsyncCONSOLE"/>
      MyAppender>
      
      • 1
      • 2
      • 3
      • 4

      会获取标签下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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    mybatis:
        configuration:
    		log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl
    
    • 1
    • 2
    • 3

    参考

  • 相关阅读:
    Swift 元类型、self、Self、AnyObject、Any和AnyClass
    力扣之删除有序数组中的重复项
    用人工智能技术完善国内人力资源管理
    AQS源码阅读与强软弱虚4种引用以及ThreadLocal原理与源码
    windows7 下WOW6432Node和dump文件生成问题
    VS Code 中使用Git实践,学会了效率翻倍!
    研究生综合英语上第五单元
    算法-二叉堆及优先级队列
    离子液体1,3-二甲基咪唑四氟硼酸盐 [C1MIm]PF6 cas:243664-15-5(试剂)
    1. RabbitMq 的基本概念
  • 原文地址:https://blog.csdn.net/zyx1260168395/article/details/126539475