日志脱敏之Log4j源码分析(一)

日志脱敏之log4j源码分析(一)

这篇博客提供了一种日志脱敏的实现方式-利用log4j进行脱敏,本文基于log4j 1.2版本。
日志脱敏的几种方式:

  • 业务简单,少量日志记录的情况下,可以去手动替换敏感信息
  • 大量日志记录的时候,每次调用logger.info都要去考虑脱敏太过麻烦,可以在pojo类里修改toString方法,将敏感数据脱敏
  • 大量日志,并且pojo类里有Map类型参数,没办法判断map的value是否是敏感数据。或者日志记录点太多,就需要一种方法,在底层进行脱敏,做到调用方无感知

实际工作中公司可能会使用自己的组件进行日志采集,所以在底层加入脱敏的支持是最好的选择。
这是一个最简单的log4j的demo

import org.apache.log4j.Logger;

public class Demo {
    private static Logger logger = Logger.getLogger(Demo.class);
    public static void main(String[] args) {
        logger.info("start");
        // ..do something
        logger.info("end");
    }
}

首先了解下log4j的结构,log4j的核心类
Logger:提供记录日志的各种api,是最核心的类。
Category:Logger类的爸爸,Logger类继承自父类Category的各种属性,用来做到封装细节,暴露简单api。
Category类中有两个关键类属性:

  • 1.LoggerRepository,维护了全部的logger的单例对象(为什么这么做?记录日志的动作是同步得,会影响服务性能,单例对象可以复用,提升服务器性能)他的默认实现是Hierarchy,这个类维护了一个HashTable对象,存放你无数个Controller或者Service里面的静态logger。(同时这个类也实现了log4j的一个关键特性,日志继承,有兴趣的可以看下官方文档,对今天大的分析用处不大,不在赘述)
  • 2.AppenderAttachableImpl 好长的名字,不过看意思应该是‘可连接的追加器’,什么意思呢?就是他属于一个管理类,用来管理你的各种Appender,比如ConsoleAppender,FileAppender等等,同时他可以在老的Appender列表后面追加、删除Appender,所以就有了这个名字,个人见解。其实叫AppenderManager,或者AppenderHolder不是更符合常规的命名习惯吗?个性化的命名不太适合开源项目。

OK,关键类了解之后,来看看logger4j的工作流程,下面是从网上找的调用logger.info的时序图,这个是最简单的版本,适合新手学习。先看一眼,不用完全明白,后面再讲。
在这里插入图片描述

  1. 我的工程的依赖
<dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
</dependency>
  1. 调用logger.info(“hello”)
  2. 进入info方法,判断logger级别达到需要记录的级别后,就记录日志,找到forcedLog这行。
  3. 进入forcedLog方法,他创建了一个新的LoggingEvent对象,调用了callAppenders方法。(这里留心一下,每次都创建新的LoggingEvent对象,是性能不友好的,感兴趣的可以了解下,号称性能优越的Log4j是如何处理LoggingEvent这个类的。)
  4. 进入callAppenders方法,判断AppenderAttachableImpl 他维护的appenderList不为空后,进入appendLoopOnAppenders这一行
  5. 进入appendLoopOnAppenders方法。重头戏来了,遍历appenderList的每个appender,进行doAppender操作(这里用到了两种设计模式,有没有人知道?答案在下一篇博客揭晓日志脱敏之Log4j源码分析(二)
  6. Appender的默认实现AppenderSkeleton(appender骨架。。命名真是特别)doAppender方法实际调用了append方法,在此之前经过了一些Filter的判断,这里的判断就是其中一种设计模式的佐证。
  7. Appender的实现太多,我们随便选择一个,进入SyslogAppender实现,无视上面的层层判断,看下这行layout.format(event);这里使用Appender对应的layout对event进行了format,后面的操作是这个appender的业务实现,不用太关注。

OK,目前为止,我们已经知道了整个流程,不要忘了我们最初的目的,是要用logger4j的特性进行日志脱敏。

  • 首先我们能想到的是自定义Appender,然后在里面自己想怎么脱都行,但是有时候我们使用的是自带的Appender,或者公司自己的Appender组件(比如说flume Appender来做日志收集),重写或者继承这些Appender都会给你的代码带来bad smell。
  • 其次,在event处理时,会使用appender对应的layout进行处理。我们可以在layout.format方法里进行脱敏,自定义一个layout太简单不过。
    比如对手机号进行脱敏:
public class PhoneMaskLayout extends Layout {
    
    private static Pattern pattern = Pattern.compile("(1[0-9]{2})[0-9]{4}([0-9]{3})");
    private static String replace = "$1****$2";
    public String format(LoggingEvent event) {
        String msg = event.getMessage().toString();
        Matcher matcher = pattern.matcher(msg);
        if (matcher.find()) {
            msg = matcher.replaceAll(replace);
        }
        return msg;
    }
    //...
}

至此,看似好像可以了,但是在工作中仅仅实现功能是不可以的,性能,安全都要考虑到,上面说过了,appender的appende方法是同步操作,用正则去匹配手机号会造成严重的性能问题,在下一篇我会写下如何提升性能。

系列博客:

感兴趣的可以看下我的其他博客,包学包会,药到病除…

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值