Exploring
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
首页
  • Java

    • 面向对象的思想OOP
    • 浅谈Java反射原理
    • endorsed覆盖JDK中的类
  • 认证与授权

    • LDAP概念和原理介绍
    • OAuth2介绍
  • Impala

    • Impala 介绍
  • MySQL

    • 关于MySQL的一些面试题
    • 解决MySQL不到中文数据
    • 数据库之事务与实现原理
  • Oracle

    • oracle的表空间,用户管理,表操作,函数
    • oracle的查询、视图、索引
    • plsql简单入门
  • Redis

    • 数据类型详解
    • 跳越表
    • 数据持久化的两种方式
  • 共识算法

    • gossip
  • RPC

    • GRPC初识与快速入门
    • ProtocolBuffer基本语法
  • RabbitMQ

    • RabbitMQ入门程序之HelloWorld
    • RabbitMQ之工作模式
  • Zookeeper

    • Zookeeper一文入门
  • Docker

    • Docker入门初体验
  • Maven

    • 把自己的包到Maven中央仓库
    • Maven之自定义插件
  • Nginx

    • nginx的安装
    • nginx的配置文件
    • nignx 的变量
  • Tomcat

    • Servlet3通过SPI进行注册组件
  • Vagrant

    • vagrant 初始化
    • vagrant 常用配置
    • vagrant 自己制作 box
  • Linux

    • 启动方式 Systemd
    • 后台服务
    • 防火墙与 Iptables
  • 设计模式

    • 设计模式-代理
    • 设计模式-单例模式
    • 设计模式-迭代器
  • 分布式

    • CAP 理论
  • 数据结构

    • 数据结构之堆Heap
    • 数据结构之哈希表
    • 数据结构之队列
  • 计算机网络

    • HTTP与HTTPS详解
    • 浅谈DNS协议
    • ISP中的网络层
  • 算法

    • 常用查找算法及Java实现
    • 常用排序算法及Java实现
    • 迪杰斯特拉算法
  • 操作系统

    • 操作系统之进程调度算法
    • 操作系统之进程通讯IPC
    • 操作系统之内存管理
  • 抓包

    • 生成安卓系统证书
  • 加解密

    • 常见加密算法
    • 公开秘钥基础知识
    • RSA 解析
  • Windows

    • scoop 包管理
    • windows-terminal 配置
    • 增强 PowerShell
归档
Github (opens new window)
  • Java

    • 基础

    • 并发与多线程

    • 日志系统

      • 初识Java中的日志
      • 两种常用日志框架搭配快速上手
      • logback介绍
      • logback的架构
      • logback的配置
      • logback配置文件语法
      • logback中的Appender
      • logback配置中的Encoder
      • logback中的Layouts
        • 简介
        • 自定义Layout
        • 本文代码
        • PatternLayout
        • 格式化日志的写法
        • 对齐方式
        • 转义字符的选项
        • 特殊的圆括号
        • 高亮彩色日志
        • Evaluators
        • 自定义转换说明符
        • 其他
        • 了解更多
      • logback中的Filter
      • logback中使用MDC
      • logback源码分析
      • 好看的彩色日志输出
    • 单元测试

    • JVM

    • Spring

    • SpringBoot

    • 一些工具

  • 语言
  • Java
  • 日志系统
unclezs
2020-12-04
0
目录

logback中的Layouts

# 简介

Layout就是负责将事件对象转换为String对象的,而且支持自定义,你可以把日志转换为其他格式html,json等等。

public interface Layout<E> extends ContextAware, LifeCycle {

  String doLayout(E event);
  String getFileHeader();
  String getPresentationHeader();
  String getFileFooter();
  String getPresentationFooter();
  String getContentType();
}
1
2
3
4
5
6
7
8
9

# 自定义Layout

public class MyLayout extends LayoutBase<ILoggingEvent> {
  @Override
  public String doLayout(ILoggingEvent event) {
    return "自定义布局器:"+ (event.getTimeStamp() - event.getLoggerContextVO().getBirthTime())
        + " "
        + event.getLevel()
        + " ["
        + event.getThreadName()
        + "] "
        + event.getLoggerName()
        + " - "
        + event.getFormattedMessage()
        + CoreConstants.LINE_SEPARATOR;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<configuration debug="false" scan="false" scanPeriod="1 second" packagingData="false">
    <property name="pattern"
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) - %cyan(%msg%n)"/>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.unclezs.samples.log.slf4j.logback.layouts.MyLayout"/>
            <!--立即刷新到流-->
            <immediateFlush>true</immediateFlush>
        </encoder>
    </appender>
    <logger name="com.unclezs.samples.log.slf4j.logback" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
    <root level="off">
    </root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 本文代码

log-slf4j-logback (opens new window)

# PatternLayout

logback 配备了一个更加灵活的 layout 叫做 PatternLayout。跟所有的 layout 一样,PatternLayout 接收一个日志事件并返回一个字符串。但是,可以通过调整 PatternLayout 的转换模式来进行定制。

PatternLayout 中的转换模式与 C 语言中 printf() 方法中的转换模式密切相关。转换模式由字面量与格式控制表达式也叫转换说明符组成。你可以在转换模式中自由的插入字面量。每一个转换说明符由一个百分号开始 '%',后面跟随可选的格式修改器,以及用综括号括起来的转换字符与可选的参数。转换字符需要转换的字段。如:logger 的名字,日志级别,日期以及线程名。格式修改器控制字段的宽度,间距以及左右对齐。

正如我们已经在其它地方提到过的,FileAppender 及其子类需要一个 encoder。因为,当将 FileAppender 及其子类与 PatternLayout 结合使用时,PatternLayout 必须用 encoder 包裹起来。鉴于 FileAppender/PatternLayout 结合使用很常见,因此 logback 单独设计了一个名叫 PatternLayoutEncoder 的 encoder,包裹了一个 PatternLayout,因此它可以被当作一个 encoder。下面是通过代码配置 ConsoleAppender 与 PatternLayoutEncoder 使用的例子:

public class PatternSample {

  static public void main(String[] args) throws Exception {
    Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    LoggerContext loggerContext = rootLogger.getLoggerContext();
    loggerContext.reset();

    PatternLayoutEncoder encoder = new PatternLayoutEncoder();
    encoder.setContext(loggerContext);
    encoder.setPattern("%-5level [%thread]: %message%n");
    encoder.start();

    ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
    appender.setContext(loggerContext);
    appender.setEncoder(encoder); 
    appender.start();

    rootLogger.addAppender(appender);

    rootLogger.debug("Message 1"); 
    rootLogger.warn("Message 2");
  } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

转化后输出:

DEBUG [main]: Message 1 
WARN  [main]: Message 2
1
2

# 格式化日志的写法

格式化字符 含义
%logger{length} logger的名字,长度是最大长度,如果全限定类名超过长度了就进行简写,但是类名不会被简写 eg: %logger{3}: com.unclezs.Test=>c.u.Test
%class{length} 调用者的全限定类名,通过计算的,比价慢,一般不用
%contextName 输出日志事件附加到的 logger 上下文的名字
%date{pattern,[timezone]} 时区可选,显示当前时间
caller{depth}、caller{depthStart..depthEnd}、caller{depth, evaluator-1, ... evaluator-n}、caller{depthStart..depthEnd, evaluator-1, ... evaluator-n} %caller{2} 显示2层调用栈
%line 行号,效率低,不建议使用
%n 换行
%level 日志级别
%relative 打日志消耗时间毫秒数
%thread 输出生成日志事件的线程名。
m / msg / message 日志信息
method 调用打印日志的所在方法名字,比较慢,不建议用
X/mdc 如果 MDC 转换字符后面跟着用花括号括起来的 ,例 %mdc{userid},那么 'userid' 所对应 MDC 的值将会输出。如果该值为 null,那么通过 :- 指定的默认值 将会输出。如果没有指定默认值,那么将会输出空字符串。 如果没有指定的 key,那么 MDC 的整个内容将会以 "key1=val1, key2=val2" 的格式输出。
exception{depth}/throwable{depth} depth: short第一行、full全部、数字指定几行。
%xException /xE/xThrowbale 和exception一样,不过会在每行后面显示jar包名字和版本号
%rootException 和xeception一样,反向输出
marker 输出相关的标签
property{key} 输出属性值
replace(p){r, t} 在子模式 'p' 产生的字符中,将所有出现正则表达式 'r' 的地方替换为 't'。例如,"%replace(%msg){'\s', ''}" 将会移除事件消息中所有空格。

# 对齐方式

默认情况下,相关信息按照原样输出。但是,在格式修改器的帮助下,可以对每个数据字段进行对齐,以及更改最大最小宽度。

可选的格式修改器放在百分号跟转换字符之间。

第一个可选的格式修改器是左对齐标志,也就是减号 (-) 字符。接下来的是最小字段宽度修改器,它是一个十进制常量,表示输出至少多少个字符。如果字段包含很少的数据,它会选择填充左边或者右边,直到满足最小宽度。默认是填充左边 (右对齐),但是你可以通过左对齐标志来对右边进行填充。填充字符为空格。如果字段的数据大于最小字段的宽度,会自动扩容去容纳所有的数据。字段的数据永远不会被截断。

这个行为可以通过使用最大字段宽度修改器来改变,它通过一个点后面跟着一个十进制常量来指定。如果字段的数据长度大于最大字段的宽度,那么会从数据字段的开头移除多余的字符。举个🌰,如果最大字段的宽度是 8,数据长度是十个字符的长度,那么开头的两个字符将会被丢弃。这个行为跟 C 语言中 printf 函数从后面开始截断的行为相违背。

如果想从后面开始截断,可以在点后面增加一个减号。如果是这样的话,最大字段宽度是 8,数据长度是十个字符的长度,那么最后两个字符将会被丢弃。

下面是各种格式修改器的例子:

格式修改器 左对齐 最小宽度 最大宽度 备注
%20logger false 20 none 如果 logger 的名字小于 20 个字符的长度,那么会在左边填充空格
%-20logger true 20 none 如果 logger 的名字小于 20 个字符的长度,那么会在右边填充空格
%.30logger NA none 30 如果 logger 的名字大于 30 个字符的长度,那么从前面开始截断
%20.30logger false 20 30 如果 logger 的名字大于 20 个字符的长度,那么会从左边填充空格。但是如果 logger 的名字大于 30 字符,将会从前面开始截断
%-20.30logger true 20 30 如果 logger 的名字小于 20 个字符的长度,那么从右边开始填充空格。但是如果 logger 的名字大于 30 个字符,将会从前面开始截断
%.-30logger NA none 30 如果 logger 的名字大于 30 个字符的长度,那么从后面开始截断

下面的表格列出了格式修改器截断的例子。但是请注意综括号 "[]" 不是输出结果的一部分,它只是用来区分输出的长度:

格式修改器 logger 的名字 结果
[%20.20logger] main.Name [           main.Name]
[%-20.20logger] main.Name [main.Name           ]
[%10.10logger] main.foo.foo.bar.Name [o.bar.Name]
[%10.-10logger] main.foo.foo.bar.Name [main.foo.f]

# 转义字符的选项

<pattern>%-5level - %replace(%msg){'\d{14,16}', 'XXXX'}%n</pattern>
1

我们传递 \d{16} 与 XXXX 给 replace 转换字符。它将消息中 14,15 或者 16 位的数字替换为 XXXX,用来混淆信用卡号码。在正则表达式中,"\d" 表示一个数字的简写。"{14,16}" 会被解析成 "{14,16}",也就是说前一个项将会被重复至少 14 次,至多 16 次。

# 特殊的圆括号

在 logback 里,模式字符串中的圆括号被看作为分组标记。因此,它能够对子模式进行分组,并且直接对子模式进行格式化。在 0.9.27 版本,logback 开始支持综合转换字符,例如 %replace 可以对子模式进行转换。

例如一下模式:

%-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n
1

将会对子模式 "%d{HH:mm:ss.SSS} [%thread]" 进行分组输出,为了在少于 30 个字符时进行右填充。

13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
13:09:38 [btpool0-7]       INFO  c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7]       DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Found factor 2
1
2
3
4
5
6
7
8

# 高亮彩色日志

如上所述的圆括号分组,允许对子模式进行着色。在 1.0.5 版本,PatternLayout 可以识别 "%black","%red","%green","%yellow","%blue","%magenta","%cyan", "%white", "%gray", "%boldRed","%boldGreen", "%boldYellow", "%boldBlue", "%boldMagenta""%boldCyan", "%boldWhite" 以及 "%highlight" 作为转换字符。这些转换字符都还可以包含一个子模式。任何被颜色转换字符包裹的子模式都会通过指定的颜色输出。

然后在pattern中使用即可。

# Evaluators

可以用来动态判断是否需要显示某些信息,比如异常信息,调用信息。

由于 XML 的编码规则,& 符号需要被转义为 &

判断打印的消息中是否包含different字符串:

<configuration debug="false" scan="false" scanPeriod="1 second" packagingData="false">
    <evaluator name="isError">
        <expression>message.contains("different")</expression>
    </evaluator>
    <property name="pattern"
              value=" %-4relative [%thread] %-5level - %msg%n%caller{2, isError}"/>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
            <!--在头部打印出pattern-->
            <outputPatternAsHeader>true</outputPatternAsHeader>
        </encoder>
        <!--立即刷新到流-->
        <immediateFlush>true</immediateFlush>
    </appender>
    <logger name="com.unclezs.samples.log.slf4j.logback" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
    <root level="off">
    </root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
741  [main] INFO  - test message by different level
Caller+0	 at com.unclezs.samples.log.slf4j.logback.utils.LoggerHelper.logMsg(LoggerHelper.java:46)
Caller+1	 at com.unclezs.samples.log.slf4j.logback.utils.LoggerHelper.logMsg(LoggerHelper.java:57)
1
2
3

# 自定义转换说明符

我们可以在 PatternLayout 中使用内置的转换字符。我们也可以使用自己新建的转换字符。

新建一个自定义的转换字符需要两步。

1. 自定义

首先,你必须继承 ClassicConverter 类。ClassicConverter 对象负责从 ILoggingEvent 实例中抽取信息并输出字符串。例如,%logger 对应的转换器 LoggerConverter,可以从 ILoggingEvent 从抽取 logger 的名字,返回一个字符串。它可以缩写 logger 的名字。

下面是一个自定义的转换器,获取当前时间戳。

/**
 * @author blog.unclezs.com
 * @since 2020/12/04 17:03
 */
public class CustomNowTimeConverter extends ClassicConverter {

  @Override
  public String convert(ILoggingEvent event) {
    return String.valueOf(System.currentTimeMillis());
  }
}
1
2
3
4
5
6
7
8
9
10
11

2. 配置

<configuration debug="false" scan="false" scanPeriod="1 second" packagingData="false">
    <conversionRule conversionWord="nowTime"
                    converterClass="com.unclezs.samples.log.slf4j.logback.converter.CustomNowTimeConverter" />
    <property name="pattern"
              value="%nowTime - %cyan(%msg%n)"/>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
            <!--在头部打印出pattern-->
            <outputPatternAsHeader>false</outputPatternAsHeader>
        </encoder>
        <!--立即刷新到流-->
        <immediateFlush>true</immediateFlush>
    </appender>
    <logger name="com.unclezs.samples.log.slf4j.logback" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
    <root level="off">
    </root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

3. 输出

1607072991810 - test message by different level
1607072991810 - test message by different level
1607072991810 - test message by different level
1
2
3

# 其他

  • HTMLLayout
  • XMLLayout
  • 还有Logback Access包中也有提供

# 了解更多

Chapter 6: Layouts (opens new window) 示例代码 (opens new window)

在 GitHub 编辑此页 (opens new window)
上次更新: 2024/02/25, 12:11:11
logback配置中的Encoder
logback中的Filter

← logback配置中的Encoder logback中的Filter→

Theme by Vdoing | Copyright © 2018-2024 unclezs
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式