一种使用Logback进行日志脱敏的方法和实现

2018/09/13 16:57:57 No Comments

本文描述了一种使用logback进行项目中打印的日志进行脱敏的一种处理方法,通过底层消息转换,字段过滤,以及数据加密,对象lazy化多种方法来完成整个实现体系。

参考文章:https://blog.csdn.net/lrcxl/article/details/78723756 关于实现log4j2日志脱敏的一种方案

整个实现分为以下4个步骤

  1. 底层消息转换 logback层提供扩展方法,允许注入额外的参数填充实现
  2. 字段过滤 参数toString化工具处理
  3. 数据加密 信息加/解密处理
  4. 对象lazy化, 多种信息转换实现

1 logback层提供扩展方法,允许注入额外的参数填充实现

以下实现中修改了logback-classic的源码, 修改点为 LoggingEvent#getFormattedMessage

我们在logback-classic版本中提供了用于处理消息和参数进行格式化的处理类接口如下

public interface LogbackMessageFormat {
    String format(String message, Object[] argumentArray);
}

其默认实现为 

public class Slf4jLogbackMessageFormat implements LogbackMessageFormat {
    @Override
    public String format(String message, Object[] argumentArray) {
        return MessageFormatter.arrayFormat(message, argumentArray).getMessage();
    }
}

此即为原logback-classic的默认实现。 为了让外部注入额外的实现,提供了一个工厂类,以让外部设置新的处理实现,如下参考所示:

public class LogbackMessageFormatFactory {
    @Getter
    @Setter
    private static LogbackMessageFormat INSTANCE = new Slf4jLogbackMessageFormat();
}

通过此工厂方法,即可设置自己的实现了,一个用于脱敏的参考实现如下

public class SelfImpl implements LogbackMessageFormat {
    @Override
    public String format(String message, Object[] argumentArray) {
        ......
        //以下代码为将参数对象toString化,相应的脱敏即隐藏在此方法中
        for(int i = 0; i < size; i++) {
            argumentArray[i] = ToStringUtils.toString(argumentArray[i]);
        }

        //调用原始格式化信息为字符串
        return StringUtils.format(message, argumentArray);
    }
}

(更多…)

开发可正确序列化和反序列化的guava table组件

2016/06/20 15:32:17 No Comments

首先,什么是Table,可以理解为前端使用的html table组件,它具有行头,列头,以及具体的数据.即描述有多少行,多少列,在行为X,列为Y的二维格子中所对应的值是多少.

在使用google guava组件同时,也在讨论其组件的一些局限性,比如它的不变性,对序列化支持的不友好等.
其中table就是一个例子,作为一个表格组件的后端对应数据结构,其自带的table实现完全不能满足实际的需要.其大部分实现都是不可变的,即意味着必须要使用类似builder的模式来进行创建,一旦创建好之后,就不能再次修改.那么,作为一个实际可用的table实现,它需要哪些特性呢.以下例出我们所需要的要求:

1. 查找 按行,按列,快速定位
2. 快速迭代,按行,按列,支持稀疏迭代
3. 泛型支持(通用化)
4. 支持排序
5. 行,列动态增长,削减
6. 反序列化支持(json)

也就是说,我们期望像使用java的集合一样,创建出相应的数据之后,一直都是可变的,并且可以随意地进行传递,然后再任意地进行处理.无论按行,按列,都能够很好地使用.
本篇即描述了如何实现这样一个table组件,以及它最终的效果.

已实现的github地址:https://github.com/flym/array-tree-table

(更多…)

fastjson 反序列化源码解析

2015/08/16 13:02:18 No Comments

fastjson从0.X版本到最新版本,由于其特殊的编码(即特别编码优化),让人对其实现思路很难处理,加上没有注释,因此在学习了解时也很困难。因此,本篇即从基本概念入手,忽略其一些特殊处理点(对实际结果无意义),了解其反序列化实现思路。
本文基于fastjson版本 1.2.4

1 基本概念

  • token-词法标记      用于标识当前在解析过程中解析到的对象的一个标记,具体的值参考 JSONToken。比如 {,即表示当前正在解析的是一个类似map或对象的格式,而},则表示当前对象已经到底了。
  • ch-当前字符    用于表示当前已经读取到的字符是什么,如 abc,当位置为1时,则当前字符为 b
  • bp-解析字符位置    用于表示当前字符所位于原始字符串中的哪一个位置,与ch是相对应的,它始终表示最新的一个位置,如果需要记录一些历史位置。如字符串起始位置,数字起始位置等,则需要使用其它标记,如np。
  • sbuf-字符缓冲    在解析字符串时的特殊存储区域,主要是用于解析转义字符时的临时存储区。即如果原字符串为 a\\t,则实际解析的字符串应该为a\t,那么原字符串为3位长,解析之后为2位长。即需要另行存储。字符缓冲区如名所示,为一个字符数组,需要需要单独的定义来存储长度信息,如使用sp。
  • sp-字符缓冲区位置    这个用于表示在字符缓冲区之间记录当前字符串(或数字串)等的长度信息,同时也等同于当前的一个位置(如果坐标从0开始)。
  • np-数字解析位置    用于实际表示在解析到常量信息时起始点的标记位置。通过np + sp,即计算得出相应的区间值了。

(更多…)

alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化B

2011/06/20 08:30:41 No Comments

    上篇地址:http://www.iflym.com/index.php/code/alibaba-fastjson-serializer-source-analyse-2-performence-optimize-a.html
    前面讲了进行对象解析的两个方面,并讲了针对outWriter将不同类型的数据信息写到buf字符数组。本篇讲解对象解析的过程,即如何将不同类型的对象解析成outWriter所需要的序列信息。并考虑其中的性能优化。

    取得解析器    
    首先我们需要取得指定对象的json序列化器,以便使用特定的序列化器来序列化对象。因此,需要有一个方法来取得相对应的序列化器。在fastjson中,使用了一个类似map的结构来保存对象类型和及对应的解析器。对于对象类型,在整个fastjson中,分为以下几类:

    1    基本类型以及其包装类型,字符串
    2    基本类型数组以及包装类型数组
    3    Atomic类型
    4    JMX类型
    5    集合类型以及子类
    6    时间类型
    7    json类型
    8    对象数组类型
    9    javaBean类型

    对于第1,2,3,4类型,在fastjson中使用了一个全局的单态实例来保存相对应的解析器;第5类型,处理集合类型,对于集合类型及其,由于其处理逻辑均是一样,所以只需要针对子类作一些的处理,让其返回相对应的集合类型解析器即可;第6类型,时间处理器,将时间转化为类似yyyy-MM-ddTHH:mm:ss.SSS的格式;第7类型,处理fastjson专有jsonAwre类型;第8类型,处理对象的数组形式,即处理数组时,需要考虑数组中的统一对象类型;第9,即处理我们最常使用的对象,javaBean类型,这也是在项目中解析得最多的类型。

(更多…)

alibaba fastjson(json序列化器)序列化部分源码解析-2-性能优化A

2011/06/19 11:45:01 No Comments

    上篇地址:http://www.iflym.com/index.php/code/alibaba-fastjson-json-serializer-chapter-source-analyse-one-global-analyse.html
    接上篇,在论述完基本概念和总体思路之后,我们来到整个程序最重要的部分-性能优化。之所以会有fastjson这个项目,主要问题是为了解决性能这一块的问题,将序列化工作提高到一个新的高度。我们提到,性能优化主要有两个方面,一个如何将处理后的数据追加到数据储存器,即outWriter中;二是如何保证处理过程中的速度。
    本篇从第一个性能优化方面来进行解析,主要的工作集中在类SerializeWriter上。

    首先,类的声明,继承了Writer类,实现了输出字符的基本功能,并且提供了拼接数据的基本功能。内部使用了一个buf数组和count来进行计数。这个类的实现结果和StringBuilder的工作模式差不多。但我们说为什么不使用StringBuilder,主要是因为StringBuilder没有针对json序列化提出更加有效率的处理方式,而且单就StringBuilder而言,内部是为了实现字符串拼接而生,因为很自然地使用了更加能够读懂的方式进行处理。相比,serializeWriter单处理json序列化数据传输,功能单一,因此在某些方面更加优化一些。
    在类声明中,这里有一个优化措施(笔者最开始未注意到,经作者指出之后才明白)。即是对buf数组的缓存使用,即在一次处理完毕之后,储存的数据容器并不销毁,而是留在当前线程变量中。以便于在当前线程中再次序列化json时使用。源码如下:

public SerializeWriter(){
        buf = bufLocal.get(); // new char[1024];
        if (buf == null) {
            buf = new char[1024];
        } else {
            bufLocal.set(null);
        }
    }

    在初始构造时,会从当前线程变量中取buf数组并设置在对象属性buf中。而在每次序列化完成之后,会通过close方法,将此buf数组再次绑定在线程变量当中,如下所示:

/**
     * Close the stream. This method does not release the buffer, since its contents might still be required. Note:
     * Invoking this method in this class will have no effect.
     */
    public void close() {
        bufLocal.set(buf);
    }

    当然,buf重新绑定了,肯定计数器count应该置0。这是自然,count是对象属性,每次在新建时,自然会置0。

    在实现过程当中,很多具体的实现是借鉴了StringBuilder的处理模式的,在以下的分析中会说到。

    总体分类
   
    接上篇而言,我们说outWriter主要实现了五个方面的输出内容。
        1,提供writer的基本功能,输出字符,输出字符串
        2,提供对整形和长整形输出的特殊处理
        3,提供对基本类型数组输出的支持
        4,提供对整形+字符的输出支持
        5,提供对字符串+双(单)引号的输出方式
    五个方面主要体现在不同的作用域。第一个提供了最基本的writer功能,以及在输出字符上最基本的功能,即拼接字符数组(不是字符串);第二个针对最常用的数字进行处理;第三个,针对基本类型数组类处理;第四个针对在处理集合/数组时,最后一位的特殊处理,联合了输出数字和字符的双重功能,效率上比两个功能的实现原理上更快一些;第四个,针对字符串的特殊处理(主要是特殊字符处理)以及在json中,字符串的引号处理(即在json中,字符串必须以引号引起来)。

    实现思想

    数据输出最后都变成了拼接字符的功能,即将各种类型的数据转化为字符数组的形式,然后将字符数组拼接到buf数组当中。这中间主要逻辑如下:
        1    对象转化为字符数组
        2    准备装载空间,以容纳数据
        2.1    计数器增加
        2.2    扩容,字符数组扩容
        3    装载数据
        4    计数器计数最新的容量,完成处理
    这里面主要涉及到一个buf数组扩容的概念,其使用的扩容函数expandCapacity其内部实现和StringBuilder中一样。即(当前容量 + 1)* 2,具体可以见相应函数或StringBuilder.ensureCapacityImpl函数。

(更多…)

alibaba fastjson(json序列化器)序列化部分源码解析-1-总体分析

2011/06/19 00:52:17 No Comments

    fastjson官方地址: http://code.alibabatech.com/wiki/display/FastJSON/Home
    从javaeye上看到了阿里一位人士写的fastjson,特别是其中如何将java对象序列化成json字符串这段。笔者比较关注,因为在笔者的项目中就用了一个json序列化器(造的轮子)。就下载下来看了一看,先不说和笔者所用的轮子有何区别,单就用了一个简单的测试器,来测试一下两者的处理速度。测试代码就不贴了,简单地说下测试结果。在jvm充分优化的情况下(for循环执行了很多次之后),笔者所使用的java序列化器处理速度不是很均匀,在结尾有短暂的变化(可能与虚拟机回收有关系);而fastjson在后面的处理过程当中,一般很均匀(后来发现与使用的buf分配方式有关)。最主要的区别莫在于,fastjson的速度那是不能对比了。
    经过分析源码之后,发现fastjson在处理json优化上面还是下了很大的工夫的。笔者准备从以下几个方面对fastjson作一个简单的解析,也让使用fastjson的同学对fastjson有一个简单的认识。
        1    总体分析    分析json序列化的总体思路和解析过程
        2    性能分析A  针对字符生产部分(即outWriter)对不同类型数据的处理和与性能相关处理部分
        3    性别分析B  针对序列化过程部分(即objectSerializer)对不同类型的序列化过程处理和与性能相关处理部分
        4    对象解析分析    对javaBean解析部分和针对字段输出部分的处理和解析
    源码分析基于1.0.5版本。

    总体分析,首先上图,即fastjson的总体处理思想,其实也是所有json序列化器需要考虑的问题。
    在这里,需要考虑的主要有两个部分,一是临时保存在序列化过程中用于储存数据的容器,二是处理对象序列化的序列化器。
    在fastjson中,保存数据的容器使用了wirter,字符输出流,而且是自实现的一个字符输出流。相对原来的writer,追加了很多需要输出的信息的实现,比如输出一个字符串,输出一个字符,输出一个long类型数据等。而处理对象序列化的序列化器,而使用了责任链模式和工厂模式,将不同类型的java对象分散到不同的序列化器当中。而每个序列化器只处理与自身类型相对应的数据信息,这样就避免了在处理时,各种情况交织在一块,逻辑混乱的问题。
    下面就源码本身作一个分析,其中结合两个部分进行分析。

(更多…)