• 四、encoder与layout分析


    一、何为encoder?何为layout

    我们的日志要输出到不同的appender,有点时候有编码要求,比如GBK,UTF-8等,就需要encoder,另外,我们的日志是需要一定的格式的,而不是随意
    输出的,那么就需要用到layout,接下来我们先来分析一些比较通用的一个编码器 -》PatternLayoutEncoder

    二、PatternLayoutEncoder

    2.1 类图

    在这里插入图片描述

    现在我们对这个类图做个简单的介绍

    • LifeCycle:定义了start与stop方法,在使用某对象之前需要先start,关闭时使用stop,通常一些
      初始化准备操作会写在这个start方法中,而释放资源,关闭流会写在stop方法中
    • ContextAware:日志上下文aware,类似spring中的各种aware,比如ApplicationContextAware,继承该接口的类,会在使用前
      注入日志上下文对象,除此之外还有一些addInfo,addWarn等添加状态的接口,在具体的实现中会记录到一个集合中,超过大小之后会
      缓存在环形缓存中,另外还会除非状态监听器
    • ContextAwareBase:ContextAware的基本实现
    • Encoder:定义了三个接口,headerBytes 写入页眉,footerBytes写入页脚,比如说我们的日志写入的是html文本,那么页头与页脚通常是写入
    啊,之类的标签,encode方法说对我们输入的日志消息进行解析 - EncoderBase:实现了基本的 LifeCycle 的接口方法 - LayoutWrappingEncoder:实现了基本的页脚,页眉,日志信息编码方法 - PatternLayoutEncoderBase:设置pattern - PatternLayoutEncoder:start方法设置 PatternLayout

    我们首先来看到LayoutWrappingEncoder的编码方法

    
    public byte[] encode(E event) {
        // 调用 PatternLayout 的 doLayout 方法,对消息进行格式化
        String txt = layout.doLayout(event);
        // 通过字符集进行编码
        return convertToBytes(txt);
    }
    
    private byte[] convertToBytes(String s) {
        if (charset == null) {
            return s.getBytes();
        } else {
            return s.getBytes(charset);
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以看到上面的代码调用layout对消息进行了格式化,那么整个layout的对象是从哪里设置进来的呢?我们可以看到PatternLayoutEncoder的start方法

    @Override
    public void start() {
        PatternLayout patternLayout = new PatternLayout();
        patternLayout.setContext(context);
        patternLayout.setPattern(getPattern());
        patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
        patternLayout.start();
        this.layout = patternLayout;
        super.start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    从上面的代码可以看出,它使用的是 PatternLayout,那么它是怎么对消息进行格式化的呢?我们可以看下它的start方法,不过在看这个方法之前我们
    先来看下它的类图

    在这里插入图片描述

    我们对这个类图先做个简单介绍

    • LifeCycle:定义了start与stop方法,在使用某对象之前需要先start,关闭时使用stop,通常一些
      初始化准备操作会写在这个start方法中,而释放资源,关闭流会写在stop方法中
    • ContextAware:日志上下文aware,类似spring中的各种aware,比如ApplicationContextAware,继承该接口的类,会在使用前
      注入日志上下文对象,除此之外还有一些addInfo,addWarn等添加状态的接口,在具体的实现中会记录到一个集合中,超过大小之后会
      缓存在环形缓存中,另外还会除非状态监听器
    • ContextAwareBase:ContextAware的基本实现
    • Layout:主要定义格式化的接口
    • PatternLayoutBase:主要在start方法中定义了如何解析格式pattern,额外的设置了一个模板方法,用于获取转换器
    • patternlayout:设置默认的转换器

    下面我们在来看到layout的start方法

    public void start() {
        if (pattern == null || pattern.length() == 0) {
            addError("Empty or null pattern.");
            return;
        }
        try {
            // 解析消息格式pattern,通过TokenStream对字符进行词法解析,形成token
            Parser<E> p = new Parser<E>(pattern);
            if (getContext() != null) {
                p.setContext(getContext());
            }
            // 根据解析出来的token形成语法树
            Node t = p.parse();
            // 编译成 Conventer,getEffectiveConverterMap()方法返回一个词法与Conventer的映射关系,比如
            // %d{HH:mm}中的 d =》DateConventer
            this.head = p.compile(t, getEffectiveConverterMap());
            if (postCompileProcessor != null) {
                postCompileProcessor.process(context, head);
            }
            // 批量设置Context上下文对象
            ConverterUtil.setContextForConverters(getContext(), head);
            // 批量调用start方法
            ConverterUtil.startConverters(this.head);
            super.start();
        } catch (ScanException sce) {
            StatusManager sm = getContext().getStatusManager();
            sm.add(new ErrorStatus("Failed to parse pattern \"" + getPattern() + "\".", this, sce));
        }
    }
    
    • 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

    我们可以看到layout的start方法对消息pattern进行了词法解析,最终编译成了 Converter ,假设我们现在的消息格式pattern是这样的
    %d{yyyy-MM-dd HH:mm:ss.SSS}-----%logger,那么解析出来的Converter是这样的 DateConventer -> LiteralConverter -> LoggerConverter

    DateConventer 用来解析 %d{yyyy-MM-dd HH:mm:ss.SSS} ,LiteralConverter是字面量转换器,用来输出 ----- , LoggerConverter用来输出
    logger输出类。

  • 相关阅读:
    Vue系列之数组更新检测
    Elasticsearch搜索功能的实现(五)-- 实战
    【密码学】DES 介绍
    jenkins-pipeline集成sonarqube代码扫描
    开源的java视频处理库介绍
    误删 performance_schema 后恢复
    C语言-求一个整数储存在内存中的二进制中1的个数
    idea代码快捷键Mac版
    互联网摸鱼日报(2023-10-19)
    利用CSRF或XSS攻击网站的例子
  • 原文地址:https://blog.csdn.net/qq_27785239/article/details/126293419