第六章节介绍核心对象Layout,不同于Encoder,Appender等其他核心对象,它的核心不是类的职责和其类体系结构,而是它的格式化表达式,它是Layout的pattern属性。
类似于正则表达式,其实无论是Java语言,JS语言,它的难度在于表达式本身,而不是与之关联的对象。
本章的主要内容如下:
- 自定义Layout,并在其中添加自定义属性。
- Layout的日志格式。
- 其他Layout对象,HTMLLayout,XMLLayout,这些对象的使用频率很低。待完善
- Logback-access模块的格式化表达式。待完善
1、自定义Layout
1.1 类结构
1.2 概念
在编写自定义Layout之前,首先需要理解编写配置文件与Java对象之间的对象。
在配置文件中配置encoder标签,本质是在配置Appender对象的encoder属性,其中该标签的class属性为Encoder的类型,当类型为PatternLayoutEncoder时,可以省略。
在配置encoder子标签时,它本质是配置Encoder类的属性,所以根据不同的Encoder类型,它的子标签也是不一样的。
假设class为PatternLayoutEncoder时
- 配置pattern标签,相当于给对象的pattern属性赋值。
- 配置layout标签,相当于给对象的layout属性赋值,其中标签的class属性为Layout的类型。
自定义Layout就是编写自定义的Layout接口实现类。
1.3 步骤
在编写自定义Layout时,首先需要了解Layout的类结构和概念。它的具体步骤如下:
- 编写自定义MyLayout,它可以实现Layout接口,也可以继承抽象类LayoutBase。本例中选择继承LayoutBase,它提供了接口的默认实现。MtLayout只需要实现doAppend方法即可。
- 在MyLayout添加0..N个自定义属性,这些属性必须有set方法。
- 在配置文件中,设置encoder标签的class属性值为LayoutWrapperEncoder,这个是适配器类。
- 在配置文件中,设置encoder子标签layout,它的class属性值为MyLayout,如果存在自定义属性,可以将其配置为layout子标签。
1.4 代码
自定义Layout类MyLayout
/** * * @File Name: MyLayout.java * @Description: 自定义layout类 * @version 1.0 * @since JDK 1.8 */ public class MyLayout extends LayoutBase<ILoggingEvent> { // 添加自定义属性prefix private String prefix; // 添加自定义属性suffix; private String suffix; // private static final String DEFAULT_SPLIT_STR = "****"; public String doLayout(ILoggingEvent event) { StringBuffer sbuf = new StringBuffer(); // 添加前缀 if (!StringUtils.isEmpty(prefix)) { sbuf.append(prefix).append(DEFAULT_SPLIT_STR); } // 时间戳 sbuf.append(event.getTimeStamp()).append(DEFAULT_SPLIT_STR); // 级别 sbuf.append(event.getLevel().levelStr).append(DEFAULT_SPLIT_STR); // 线程 sbuf.append(event.getThreadName()).append(DEFAULT_SPLIT_STR); // logger name sbuf.append(event.getLoggerName()).append(DEFAULT_SPLIT_STR); // line separator sbuf.append(event.getFormattedMessage()).append(DEFAULT_SPLIT_STR); // 添加后缀 if (!StringUtils.isEmpty(suffix)) { sbuf.append(suffix).append(DEFAULT_SPLIT_STR); } // 分隔符 sbuf.append(" "); return sbuf.toString(); } public void setPrefix(String prefix) { this.prefix = prefix; } public void setSuffix(String suffix) { this.suffix = suffix; } }
配置文件,只给出appender部分的内容
<!-- 配置ConsoleAppender --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 默认值,可以省略,此处只是为了学习 --> <target>System.out</target> <!-- 默认值,可以省略,它的作用是为了给不同级别的日志添加不同的颜色背景 --> <withJansi>false</withJansi> <!-- 配置encoder,大部分学习的都是logback-classic模块的内容 --> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <!-- 添加自定义Layout,由于配置了Layout,不在需要配置pattern,它是PatternLayoutEncoder的一个属性 --> <layout class="learn.logback.chapter6.MyLayout"> <!-- 配置自定义属性 --> <prefix>[single log start]</prefix> <suffix>[single log end]</suffix> </layout> </encoder> </appender>
单条日志的效果如下:
[single log start] ****1582122070664****DEBUG****main****learn.logback.chapter1.HelloWorld****Hello张三Welcome to our website**** [single log end]****
2、日志格式
当在配置日志格式时,此时它对应的类必须是PatternLayoutEncoder,表现在配置文件上为encoder标签中class属性值为默认值。配置日志格式本质是给PatternLayoutEncoder对象的pattern属性赋值。该属性的主要作用是控制日志信息的格式。
日志格式由一个个小单元组成,每个小单元由三个部分组成,
- Format modifier:它的作用是控制日志信息的长度。功能有补充空白字符,截取字符。其中符号”-”表示方向,它表示从末尾补充,或从末尾截取。
- Conversion Name:它是日志表达式的核心,对应ILoggingEvent中的某一个具体信息,可以是线程,可以是时间,可以是日志内容等等,这部分是小单元的核心。它的格式通常为% + conversionName,conversion Name可以是全称,也可以是缩写
- Optional parameters:可选参数,它与conversionName密切相关,例如当conversionName为时间时,参数为日期格式,当conversionName为logger名称时,参数为名称的最大长度。
2.1 Conversion Name
Conversion name很多,我将它分为五类。
- 基本:date(时间),logger(名称),level(级别),thread(线程),message(日志信息),%n(换行符),这些是最基本的,也是必备的一些信息。
- 变量:contextName(上下文名称),marker(标签),Mdc(MDC对象的key-value对),Property(property属性,环境变量等)。
- 杂项:line(行号),replace(字符串的替换方法),class(类全名),file(类文件名)
- 方法:method(方法名称),caller(方法调用层次),使用频率低,本文省略。
- 异常:exception,nopexception,rootException,xException,使用默认值即可,本文省略。
2.1.1 date
描述 |
date,缩写为d |
参数 |
名称:pattern 说明:日期的格式, 值:yyyy-MM-dd HH:mm:ss SSS任意组合。 是否必填:否,默认格式为yyyy-MM-dd HH:mm:ss SSS |
|
名称:timezone 说明: 时区, 值:任意合理的时区,例如Asia/shanghai。 是否必填:否,默认为系统的时区。 |
性能 |
使用频率很高,不影响性能。 |
示例 |
%d{yyyy-MM-dd HH:mm:ss SSS},// 指定日期格式 %date,// 使用默认的格式 %date{yyyy-MM-dd HH:mm:ss SSS,Asia/shanghai},// 带有时区参数。 |
2.1.2 logger & class & file
描述 |
Logger,缩写为c,lo。个人不建议使用缩写,缩写的语义不太明显 |
参数 |
名称:length 说明:Logger名称的最大长度,当logger的name长度大于length时,会对name进行截取,它总是name开始处截取。 值:任何整数。 是否必填:否, |
性能 |
使用频率很高,不影响性能。 |
示例 |
%logger{30}:logger名称的最大长度为30。 |
关联的conversion name |
它们基本不会用到,因为logger的名称一般都是类全名,而Java中类名和Java的文件名是一致的。所以class,file都可以从logger名称中推测出来,有点多余。 |
示例如下:
当pattern的格式为:%d{HH:mm:ss.SSS} [%logger] [%class] [%file] %n
它对应的效果图为:
15:43:51.438 [learn.logback.chapter1.HelloWorld] [learn.logback.chapter1.HelloWorld] [HelloWorld.java]
三者的值基本都是相同的。
2.1.3 Thread
描述 |
Thread,缩写为t,表示当前线程的名称 |
参数 |
无 |
性能 |
使用频率很高,不影响性能。 |
示例 |
%thread |
2.1.4 Level
描述 |
Logger request的日志级别,它不是在logger标签对应的level,缩写为l |
参数 |
无 |
性能 |
使用频率很高,不影响性能。 |
示例 |
%level |
2.1.5 message
描述 |
Logging request的信息内容,缩写为m |
参数 |
无 |
性能 |
使用频率很高,不影响性能。 |
示例 |
%message,%msg |
默认的日志格式由上述5个部分组成,最后的%n表示换行符。%d [%thread] %-5level %logger{36} - %msg%n
2.1.6 ContextName
描述 |
日志框架上下文的名称,即LoggerContext对象的name属性,在使用时需要配置contextName标签。缩写为cn |
参数 |
无 |
性能 |
使用频率一般,不影响性能。 |
示例 |
%contextName,%cn |
示例如下:
当配置<contextName>Learn_Logback</contextName>时
// 日志格式: %d [%contextName] [%marker] %n // 日志 2020-02-20 16:09:38,841 [Learn_Logback] []
2.1.7 Marker
描述 |
日志请求的标记,在调用请求相关方法时,第一个参数为marker,第二个参数为message。使用MarkerFactory的getMarker创建Marker对象。 |
参数 |
无 |
性能 |
使用频率一般,不影响性能。 |
示例 |
%Marker |
示例如下:
当代码为:
Marker test =MarkerFactory.getMarker("Marker test"); logger.debug(test, "Hello World");
它的效果如下:
// 日志格式: %d [%contextName] [%marker] %n // 日志 2020-02-20 16:16:40,901 [Learn_Logback] [Marker test]
2.1.8 Mdc
描述 |
org.slf4j.MDC保存着key-value键值对,该标签根据key值,访问value值。使用时,需要在之前设置MDC.put(key,value)方法 |
参数 |
Key值 |
性能 |
使用频率一般,不影响性能。 |
示例 |
%mdc{key} |
示例如下:
代码为:
MDC.put("mdc_key", "mdc_value"); Marker test =MarkerFactory.getMarker("Marker test"); logger.debug(test, "Hello World");
它的效果如下:
// 日志格式: %d [%contextName] [%marker] [%mdc{mdc_key}] %n // 日志 2020-02-20 16:26:33,814 [Learn_Logback] [Marker test] [mdc_value]
2.1.9 Property
描述 |
根据property中的key值获取value值。property它的值有四种,配置文件中的property标签,引入的properties文件,JVM变量,操作系统的环境变量。 |
参数 |
变量的Key值 |
性能 |
使用频率一般,不影响性能。 |
示例 |
%property{key} |
示例如下:
此例中获取JVM中的os.name变量。
它的效果如下:
// 日志格式: %d [%mdc{mdc_key}] [%property{os.name}] %n// 日志 2020-02-20 16:26:33,814 [mdc_value] [Windows 10]
2.1.10 Line
描述 |
日志显示行号,缩写为L |
参数 |
无 |
性能 |
使用频率一般,不影响性能。 |
示例 |
%line,%L |
2.2 Formatting
格式化指定日志中某个部分的长度。它有两种形式:
格式一:% + num + conversionName
- %固定字符串。
- 当num为正整数时,当conversionName的长度小于num时,会在开头填充空格。大于时什么都不做,当num为负数时,在末尾填充空格。
- ConversionName:日志的任何组成部分,可以使用括号将一组conversionName括起来。
示例:
%20logger:当logger的长度小于20时,会在开头填充空格。
%20(%logger %level %thread),将logger,level,thread当成一组,一个整体,当整体部分小于20时,会在开头填充空格。
格式二:% + dot + num + conversionName
- %固定字符串,dot表示点号(.)。
- 当num为正整数时,当conversionName的长度大于num时,会从开头截取字符串,小于时什么都不做。当num为负数时,会从末尾开始截取字符串。
示例:
%.1level:如果为INFO时,表示为O,被截取掉的字符串为INF。
%.-1level:如果为INFO时,表示为I,被截取掉的字符串为NFO。
2.3 Coloring
Coloring的主要功能是为不同级别的日志配置不同的背景色。要实现此功能有很多种方式,例如在Eclipse中安装logViewer插件,Idea可以使用JansiColor的插件。本文省略。
2.4 Evaluator
TODO
2.5 自定义conversionName
TODO
3、HtmlLayout
TODO
4、XMLLayout
TODO
5、Access模块
TODO