一次性解决打日志时的4个重复低效场景(日志脱敏、日期格式化、json序列化)...

2023-11-02

关注公众号【1024个为什么】,及时接收最新推送文章!

本篇文章是对之前的两篇文章的一个总结、补充。


日志里打出来的都是时间戳?教你一行代码搞定它icon-default.png?t=N2N8https://blog.csdn.net/JiuQianWan/article/details/127002924?spm=1001.2014.3001.5501

日志里的敏感信息还在打明文?3 种日志脱敏方案任你选icon-default.png?t=N2N8https://blog.csdn.net/JiuQianWan/article/details/127020281?spm=1001.2014.3001.5501

之前的解决方案是基于 fastjson + logback,本次补充了 fastjson + log4j。

至于其他的 json 序列化框架、日志框架,大家可以继续深究,重点是解决问题的思路。

背景

我们在打日志的过程有 4 个重复低效的场景

1、对象都是 json 序列化之后,再把数据交给日志框架

2、Date 类型默认会序列化为 时间戳,排查问题十分不友好

3、日志里的内容需要脱敏时,需要在数据交给日志框架前完成脱敏处理

4、每一个日志内容都要写占位符,万一忘记了,还要重新上线

dc61071ffe049a3a8205ffa0fdd1015c.png

34fb1e72d0e888ffa8dae590fc6ab0e7.png

目标

不再关心 json 序列化!

不再关心 Date 类型的序列化!

不再关心脱敏!

不再关心占位符!

一切都由底层自动完成。

解决思路

要想解决这个问题,必须要搞懂 json 和 log 框架的运行原理。

依托日志框架的扩展能力,把序列化、脱敏的工作交由日志框架完成。

常规流程

88f0c4cd8b4975c125b5547a1c474c8f.png

我们的解决流程

c567e6829cd2ec0667274424ee2db21c.png

所以首先要搞明白如何扩展日志框架,拦截住日志数据。

log4j2

它的扩展点是 MessageFactory,详见官网:

https://logging.apache.org/log4j/2.x/manual/extending.html#MessageFactory

具体实现是:

(1)自定义一个 MessageFactory,自己实现序列化 toString() 的逻辑。

(2)修改配置, 指定我们自定义的 MessageFactory。(log4j2.messageFactory=com.log.CustomParameterizedMessageFactory)

public class CustomParameterizedMessageFactory extends AbstractMessageFactory {
    static {
        // 这里指定 fastjson 序列化时 Date 类型的格式
        SerializeConfig.getGlobalInstance().put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        // 这里指定 fastjson 序列化时的 Filter,用于数据脱敏(遇到 Person 类就会自动脱敏)
        // 有多个类需要脱敏的,依次添加即可;无需脱敏的,可以删除此行代码
        SerializeConfig.getGlobalInstance().addFilter(Person.class, DataMaskFilter.instance());
    }

    public CustomParameterizedMessageFactory() {
        super();
    }

    @Override
    public Message newMessage(String s, Object... objects) {

        List<String> objStr = new ArrayList<>();
        for (int i = 0; i < objects.length; i++) {
            // 连占位符都可以由底层实现,上游再也不用担心忘写占位符了
            s = s + ", param" + i + "={}";
            Object o = objects[i];
            if (o instanceof String || o instanceof CharSequence
                    || o instanceof Character || o instanceof Short
                    || o instanceof Integer || o instanceof Long
                    || o instanceof Double || o instanceof Float
                    || o instanceof Boolean) {
                // 基础数据类型,直接转 String
                objStr.add(String.valueOf(o));
            }else {
                // 其他包装类通过 json 序列化为 String
                objStr.add(JSON.toJSONString(o));
            }
        }

        return new ParameterizedMessage(s, objStr.toArray());
    }
}

注意:

某些版本是不支持修改默认 MessageFactory 的,比如:

<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.2</version>

是否支持,看这段代码就行 org.apache.logging.log4j.spi.AbstractLogger

2cc914bc7553ff666a69e217e199ad09.png

这个版本里面是写死的,如果想要用自定义的 MessageFactory ,只能在 getLogger() 的时候,指定 MessageFactory 。

private static Logger logger = LogManager.getLogger(TestLog4j.class CustomParameterizedMessageFactory.class);


支持修改的代码是下面这个样子的:

7052cd6fd5c523ba6b24faf237fd859d.png

logback

它的扩展点是 ch.qos.logback.classic.pattern.MessageConverter

(1)自定义一个 MessageConverter,实现自己实现序列化 toString() 的逻辑。

(2)修改配置,指定我们自定义的 MessageConverter。

<conversionRule conversionWord="msg" converterClass="com.log.CustomConverter "/>
public class CustomConverter extends MessageConverter {
    static {
        // 这里指定 fastjson 序列化时 Date 类型的格式
        SerializeConfig.getGlobalInstance().put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        // 这里指定 fastjson 序列化时的 Filter,用于数据脱敏(遇到 Person 类就会自动脱敏)
        // 有多个类需要脱敏的,依次添加即可;无需脱敏的,可以删除此行代码
        SerializeConfig.getGlobalInstance().addFilter(Person.class, DataMaskFilter.instance());
    }
    
    @Override
    public String convert(ILoggingEvent event){
        try {
            return toJsonString(event);
        }catch (Exception e){
            return super.convert(event);
        }
    }
 
    private String toJsonString(ILoggingEvent event){
        try {
            List<String> objStr = new ArrayList<>();
            String msg = event.getMessage();
            Object[] argumentArray = event.getArgumentArray();
            for (int i = 0; i < argumentArray.length; i++) {
                msg = msg + ", param" + i + "={}";
                Object o = argumentArray[i];
                if (o instanceof String || o instanceof CharSequence
                        || o instanceof Character || o instanceof Short
                        || o instanceof Integer || o instanceof Long
                        || o instanceof Double || o instanceof Float
                        || o instanceof Boolean) {
                    // 基础数据类型,直接转 String
                    objStr.add(String.valueOf(o));
                }else {
                    // 其他包装类通过 json 序列化为 String
                    objStr.add(JSON.toJSONString(o));
                }
            }
            return MessageFormatter.arrayFormat(msg, objStr.toArray()).getMessage();
        } catch (Exception e) {
            return event.getMessage();
        }
    }
}

脱敏实现

通过 fastjson 的扩展点 com.alibaba.fastjson.serializer.ValueFilter,通过对 value 内容的修改,从而实现脱敏效果。

(1)自定义一个 ValueFilter,自己实现 process() 替换 value 的逻辑。

(2)修改配置, 把我们自定义的 ValueFilter 和 要脱敏的类,添加到全局的序列化配置中。

SerializeConfig.getGlobalInstance().addFilter(Person.class, DataMaskFilter.instance());
SerializeConfig.getGlobalInstance().addFilter(Xxxclass, DataMaskFilter.instance());
...
import com.alibaba.fastjson.serializer.ValueFilter;

import java.util.Arrays;
import java.util.List;

public class DataMaskFilter implements ValueFilter {
 
    public static DataMaskFilter instance(){
        return new DataMaskFilter();
    }
 
    @Override
    public Object process(Object object, String name, Object value) {
        for (DataMaskRuleEnum rule : DataMaskRuleEnum.values()) {
            List<String> fields = Arrays.asList(rule.fieldName.split("\\|"));
            if(fields.contains(name.toUpperCase())){
                value = value.toString().replaceAll(rule.regular, rule.result);
            }
        }
        return value;
    }
}
public enum DataMaskRuleEnum {
    ID_CARD("身份证号脱敏", "IDCARD", "^(\\d{4})\\d+(\\d{4})$", "$1****$2"),
    PHONE("手机号脱敏", "PHONE|BANKPHONE", "^(\\d{3})\\d+(\\d{4})$", "$1****$2"),
    BANK_CARD("银行卡号脱敏", "CARDNUM", "^(\\d{4})\\d+(\\d{4})$", "$1****$2"),
    NAME("姓名脱敏", "NAME|REALNAME|WORKERNAME", "^([\\u4E00-\\u9FA5]{1,3})([\\u4E00-\\u9FA5])$", "**$2");


    DataMaskRuleEnum(String description, String fieldName, String regular, String result) {
        this.description = description;
        this.fieldName = fieldName;
        this.regular = regular;
        this.result = result;
    }


    /**
     * 脱敏规则描述
     */
    public String description;
    /**
     * 要脱敏的属性名
     */
    public String fieldName;
    /**
     * 脱敏规则
     */
    public String regular;
    /**
     * 脱敏结果
     */
    public String result;

}

最终效果

0a931ca234fb7cfa92a33795903743ca.jpeg

甚至还可以在底层处理集合对象,

大集合只打印出集合的条数,

避免打印全部内容,造成大日志文件。

原创不易,多多关注,一键三连,感谢支持!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一次性解决打日志时的4个重复低效场景(日志脱敏、日期格式化、json序列化)... 的相关文章

  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • JAXb、Hibernate 和 beans

    目前我正在开发一个使用 Spring Web 服务 hibernate 和 JAXb 的项目 1 我已经使用IDE hibernate代码生成 生成了hibernate bean 2 另外 我已经使用maven编译器生成了jaxb bean
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • jQuery AJAX 请求在 IE8 中失败,并显示消息“错误:调用 open 方法之前无法调用此方法。”

    我正在使用 jQuery 1 4 2 并尝试执行一个简单的 AJAX 请求 目标 URL 返回一个 JSON 字符串 我使用 jslint 对其进行了验证 该请求在 Firefox 和 Chrome 中有效 但不想在 IE8 中工作 我无法
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • 为什么HashMap不能保证map的顺序随着时间的推移保持不变

    我在这里阅读有关 Hashmap 和 Hashtable 之间的区别 http javarevisited blogspot sg 2010 10 difference Between hashmap and html http javar
  • 如何格式化 Json 输出

    请帮助我如何获取 JSON 输出 如下所示 costMethod Average fundingDate 2008 10 02 fundingAmount 2510959 95 代替 type sma costMethod Average
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • C# - OutOfMemoryException 在 JSON 文件上保存列表

    我正在尝试保存压力图的流数据 基本上我有一个压力矩阵定义为 double pressureMatrix new double e Data GetLength 0 e Data GetLength 1 基本上 我得到了其中之一pressur

随机推荐

  • 基于HAL库stm32f1的DS18B20温度传感器

    由于做比赛 所以写了温度传感器 记录一下 一 介绍 DS18B20是常用的数字温度传感器 其输出的是数字信号 具有体积小 硬件开销低 抗干扰能力强 精度高的特点 1 DS18B20数字温度传感器接线方便 封装成后可应用于多种场合 如管道式
  • 让AI来告诉你什么叫幽灵堵车

    使用环境参考 CocosCreator v3 7 2 ChatGPT 正文 什么是幽灵堵车 堵车 大家都不陌生 堵车时我就思维发散 用 CocosCreator 模拟下堵车应该挺好玩 网上总说高速上最前面如果有个龟速的车 后面能堵车堵个两三
  • OpenCV仪表数据识别(二):数字区域自动定位

    下载和配置Opencv在网上和书上有很多的讲解 这里不再赘述 此处附上Opencv的下载链接 想要对图片中的数字信息进行识别首先要对图片进行预处理 排除干扰的因素 只留下有价值的信息 这里需要两张图 一张为有数据的图片 一张为仪表关闭时没有
  • 无线传感网课后习题(书本名称无线传感器网络基础 : 理论和实践 : Fundamentals of wireless sensor networks : theory and practice)二

    7 4 Describe a WSN application for each of the following categories time driven eventdriven and query driven 译 为以下每一种类型描
  • 系统安装部署系列教程(五):精简母盘和集成补丁

    所谓母盘就是我们平时说的官方原版镜像 在封装系统的过程中常常需要精简某些功能 有些功能在系统封装阶段是没办法彻底精简的 所以我们首先需要对官方镜像进行精简操作 以去除某些不需要的功能 减小镜像大小 精简母盘的工具有很多 Dism MSMG
  • Ovirt 基本介绍

    Ovirt 介绍 ovrit 介绍 部署架构 整体架构 虚拟化实现 CPU虚拟化 内存虚拟化 MMU virtualization 内存硬件辅助虚拟化 I O设备虚拟化 设备接口完全模拟 前端 后端模拟 直接划分 学习文档 在详细学习ovi
  • Math&System

    Math 概述 Math 包含执行基本数字运算的方法 调用方式 Math类中无构造方法 但内部的方法都是静态的 可以通过 类名 进行调用 常用方法 方法名 方法名 说明 public static int abs int a 返回参数的绝对
  • 【WORD】宏

    1 录制宏 2 选择快捷方式 2 1 按钮 2 2 键盘 输入的时候按CTRL ALT等辅助键 3 录制操作 改变字体颜色 改变字体大小 改变字体样式 4停止操作 5 访问宏 点击快速访问栏 快捷键输入
  • 循环中如果有某一次循环抛出异常了整个循环就停止的解决办法

    Transactional propagation Propagation REQUIRES NEW 转载于 https www cnblogs com handsomejunhong p 9318180 html
  • spring-boot-configuration-processor不生效问题

  • 电信业客户流失分析

    参考 https www kuxiao cn course pc course learning html cid C59a91e40e27ce170e7af714c 一 商业理解 QUESTION 1 预测哪些客户可能会流失 QUESTI
  • 精确时钟同步协议ptp/IEEE-1588v2协议-------(2)主从时钟之间的消息交互与时钟同步过程

    本文目录 1 主时钟和从时钟之间的消息交互流 2 延时delay和偏移offset的计算 2 1 延时delay的计算 2 2 偏移offset的计算 主时钟和从时钟之间 通过sync follow up delay request del
  • [黑盾杯]2020之Misc篇刷题记录(16)

    黑盾杯 2020之Misc篇刷题记录 16 黑盾杯 2020 encrypt 黑盾杯 2020 Blue 黑盾杯 2020 blind 黑盾杯 2020 Trees UTCTF 2020 Spectre UTCTF 2020 Observe
  • (四)SQL

    本篇介绍SQL的数据库定义语言DDL 主要有数据库的增删改用 表的增删改查 并且配套有MySQL实操练习 01 数据库的增删查用 1 1 基本语法 查询数据库 SHOW DATABASES 查询所有数据库 SELECT DATABASE 查
  • matlab折线图折点加数值

    1 只显示y轴数值 x 0 8 acc 100 100 100 100 100 99 9 97 45 73 95 39 59 plot x acc LineWidth 1 5 xlabel 噪声等级 FontSize 16 ylabel 聚
  • cmd输出彩色字体(win10 cmd控制台支持ANSI转义序列)

    这是啥 ANSI转义序列是一种带内信号的转义序列标准 用于控制视频文本终端上的光标位置 颜色和其他选项 在文本中嵌入确定的字节序列 大部分以ESC转义字符和 字符开始 终端会把这些字节序列解释为相应的指令 而不是普通的字符编码 ANSI序列
  • CSDN-markdown编辑器使用详细

    这里写自定义目录标题 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题 有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容居中 居左 居右 Sma
  • 网易2018校招编程题集合4

    小易有一个长度为n的整数序列 a 1 a n 然后考虑在一个空序列b上进行n次以下操作 1 将a i放入b序列的末尾 2 逆置b序列 小易需要你计算输出操作n次之后的b序列
  • 登录Windows自动映射磁盘

    最近项目客户要求做一个功能 内容是希望希望用户在登录windows的时候 根据用户自己负责的项目来自动创建网络映射驱动器 用户需要映射磁盘的信息都存在数据库中 经过调查研究 打算使用powershell实现 实现方式大体如下 1 先创建po
  • 一次性解决打日志时的4个重复低效场景(日志脱敏、日期格式化、json序列化)...

    关注公众号 1024个为什么 及时接收最新推送文章 本篇文章是对之前的两篇文章的一个总结 补充 日志里打出来的都是时间戳 教你一行代码搞定它https blog csdn net JiuQianWan article details 127