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

关注公众号【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

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

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

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

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 好的,我会尽力回答这个问题。 使用 D3.js 实现冒泡排序算法的可视,需要按照以下步骤进行: 1. 准备好 D3.js 的相关文件。使用 D3.js 实现可视,需要在 HTML 文件中引入 D3.js 库的文件,最简单的方法是使用 CDN(内容分发网络)引入 D3.js。 ```html <script src="https://d3js.org/d3.v6.min.js"></script> ``` 2. 准备好数据。冒泡排序算法的可视需要一组数字,这些数字可以直接写在 HTML 文件中,也可以从服务器获取。 3. 在 HTML 中添加一个容器,用于存放可视的内容。这个容器可以是一个 div 元素,也可以是其他元素。 4. 使用 D3.js 的 selection 和 data 函数将数据绑定到容器中的元素上。selection 函数用于选择容器中的元素,data 函数用于将数据绑定到元素上。 ```javascript var data = [10, 20, 30, 40, 50]; d3.select("#container") .selectAll("div") .data(data) .enter() .append("div") .text(function(d) { return d; }); ``` 5. 使用 D3.js 的 transition 函数实现动画效果。transition 函数可以用于改变元素的属性,如位置、大小等。 ```javascript d3.select("#container") .selectAll("div") .transition() .duration(1000) .style ### 回答2: D3.js是一个功能强大的JavaScript库,用于将数据转换为动态、交互式的可视图表。冒泡排序算法是一种简单但低效的排序算法,它重复遍历要排序的列表,比较相邻元素并交换位置,直到整个列表排序完毕。 首先,我们需要使用D3.js创建一个空的SVG容器,用于容纳我们的可视效果。可以使用以下代码来创建一个宽度为500像素、高度为300像素的SVG区域: ``` const svg = d3.select("body") .append("svg") .attr("width", 500) .attr("height", 300); ``` 接下来,我们需要生成一组随机数作为要排序的数据。可以使用以下代码来生成一个长度为10的随机数数组: ``` const data = d3.range(10).map(() => Math.floor(Math.random() * 100)); console.log(data); ``` 然后,我们需要使用D3.js创建可视的柱状图表示这些数据。可以使用以下代码来创建柱状图: ``` svg.selectAll("rect") .data(data) .enter() .append("rect") .attr("x", (d, i) => i * 50) .attr("y", (d) => 300 - d) .attr("width", 40) .attr("height", (d) => d); ``` 在生成的柱状图中,每个柱代表一个随机数,高度表示该随机数的大小。 最后,我们需要实现冒泡排序算法的可视效果。可以使用以下代码来实现冒泡排序算法: ``` function bubbleSort(data) { const len = data.length; for (let i = 0; i < len - 1; i++) { for (let j = 0; j < len - i - 1; j++) { if (data[j] > data[j + 1]) { const temp = data[j]; data[j] = data[j + 1]; data[j + 1] = temp; } } } } ``` 然后,在调用冒泡排序算法之前,我们可以在每次交换元素后使用D3.js更新柱状图的位置。可以使用以下代码来更新柱状图: ``` function updateChart(data) { svg.selectAll("rect") .data(data) .transition() .duration(500) .attr("y", (d) => 300 - d); } bubbleSort(data); updateChart(data); ``` 在调用冒泡排序算法后,我们调用`updateChart`函数以更新柱状图的位置,使得柱状图随着排序的进行而变。 综上所述,使用D3.js实现冒泡排序算法的可视,首先创建一个SVG容器,然后生成一组随机数作为要排序的数据,并使用D3.js创建柱状图表示这些数据。接着,实现冒泡排序算法,并在交换元素后使用D3.js更新柱状图的位置,以实现可视效果。 ### 回答3: 冒泡排序是一种简单的排序算法,其基本思想是通过不断交换相邻的元素,将较大的元素逐渐“冒泡”到数组的末尾。使用D3.js可以将冒泡排序算法的执行过程可视,使我们更加直观地理解算法的执行过程。 首先,我们可以通过D3.js生成一个包含随机数的数组,并将每个元素转为矩形。这样,我们可以通过矩形的高度来表示元素的大小。然后,我们将这些矩形按照数组元素的顺序排列,并使用不同的颜色区分已排序和未排序的部分。 接下来,我们使用冒泡排序算法对数组进行排序。算法从数组的第一个元素开始,比较相邻的两个元素的大小。如果前一个元素大于后一个元素,则交换它们的位置。通过不断交换相邻的元素,最大的元素逐渐“冒泡”到数组的末尾。 在每一次交换元素的过程中,我们可以通过D3.js动态地更新矩形的高度和位置,使我们可以看到算法的执行过程。被交换的两个元素会改变颜色,以便我们更好地区分出它们。 最后,当排序完成后,我们可以通过改变矩形的颜色或者添加其他标识,将已排序部分和未排序部分区分开来,使排序结果更加明显。 通过D3.js实现冒泡排序算法的可视,可以帮助我们更加直观地理解算法的执行过程,以及各个元素之间的比较和交换。同,通过可视,我们还可以将算法的执行过程呈现给他人,使他们也能够理解和学习这个算法。 D3.js提供了强大的数据可视功能,有助于我们更好地展示和解释算法的执行过程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值