Logback 手册 - 第六章:布局 - 1

366 阅读21分钟

来源:logback.qos.ch/manual/layo… 作者:Ceki Gülcü、Sébastien Pennec、Carl Harris 版权所有 © 2000-2022 QOS.ch Sarl

本文档采用 知识共享署名 - 非商业性使用 - 相同方式共享 2.5 许可协议


TCP implementations will follow a general principle of robustness: be conservative in what you do, be liberal in what you accept from others.

TCP 实现会遵循一个稳健性的一般原则:在你所做的事情上要保守,对于来自他人的接受则要宽容。

——JON POSTEL,RFC 793


什么是布局?

为了运行本章中的示例,您需要确保类路径中存在某些 jar 文件。请参考 设置页面 获取更多详细信息。

如果您曾经想知道,布局与佛罗里达州的大庄园无关。布局是 logback 组件,负责将传入事件转换为字符串。Layout 接口中的 format() 方法接受代表事件(任何类型)的对象并返回一个字符串。下面是 Layout 接口的概要。

public interface Layout<E> extends ContextAware, LifeCycle {

  String doLayout(E event);
  String getFileHeader();
  String getPresentationHeader();
  String getFileFooter();
  String getPresentationFooter();
  String getContentType();
}

该接口非常简单,但足以满足许多格式化需求。来自得克萨斯州的德克萨斯州开发人员,您可能从约瑟夫·海勒的《第二十二条军规》中认识他,可能会惊叹道:实现一个布局只需要五个方法!!?

Logback-classic

Logback-classic 只能处理类型为 ch.qos.logback.classic.spi.ILoggingEvent 的事件。这一事实将贯穿本节。

编写您自己的自定义布局

让我们为 logback-classic 模块实现一个简单而功能强大的布局,它打印自应用程序启动以来经过的时间、日志事件的级别、调用线程(用方括号括起来)、其记录器名称、一个破折号,然后是事件消息和一个换行符。

示例输出可能如下所示:

10489 DEBUG [main] com.marsupial.Pouch - Hello world.

以下是一个可能的实现,由得克萨斯州的开发人员编写:

示例:布局的示例实现 (logback-examples/src/main/java/chapters/layouts/MySampleLayout.java)

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout extends LayoutBase<ILoggingEvent> {

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    sbuf.append(" [");
    sbuf.append(event.getThreadName());
    sbuf.append("] ");
    sbuf.append(event.getLoggerName();
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(CoreConstants.LINE_SEP);
    return sbuf.toString();
  }
}

请注意,MySampleLayout 扩展了 LayoutBase。这个类管理所有布局实例共有的状态,比如布局是否已启动或已停止、头部、尾部和内容类型数据。它允许开发人员专注于他 / 她的 Layout 预期的格式。请注意,LayoutBase 类是泛型的。在其类声明中,MySampleLayout 扩展了 LayoutBase<ILoggingEvent>

doLayout(ILoggingEvent event) 方法,即 MySampleLayout 中唯一的方法,首先实例化一个 StringBuffer。随后添加事件参数的各个字段。得克萨斯州的人小心地打印了消息的格式化形式。如果伴随着日志请求传递了一个或多个参数,这是很重要的。

在将这些各种字符添加到字符串缓冲区后,doLayout() 方法将缓冲区转换为一个 String 并返回结果值。

在上面的示例中,doLayout 方法忽略了事件中可能包含的任何异常。在一个真实的布局实现中,您很可能也想打印异常的内容。

配置您自定义的布局

自定义布局像配置其他组件一样进行配置。如前所述,FileAppender 及其子类需要一个编码器。为了满足这一要求,我们传递一个 LayoutWrappingEncoder 实例给 FileAppender,该实例包装了我们的 MySampleLayout。以下是配置文件:

示例:MySampleLayout 的配置 (logback-examples/src/main/resources/chapters/layouts/sampleLayoutConfig.xml)

传统格式

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout" />
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

规范格式 (1.3)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>

<configuration>
  <import class="ch.qos.logback.core.ConsoleAppender"/>
  <import class="chapters.layouts.MySampleLayout"/>
  <import class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"/>

  <appender name="STDOUT" class="ConsoleAppender">
    <encoder class="LayoutWrappingEncoder">
      <layout class="MySampleLayout"/>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT"/>
  </root>
</configuration>

样例应用程序 chapters.layouts.SampleLogging 使用作为第一个参数传递的配置脚本配置 logback,然后记录调试消息,接着是错误消息。

要运行此示例,请在 logback-examples 目录中执行以下命令。

java chapters.layouts.SampleLogging src/main/java/chapters/layouts/sampleLayoutConfig.xml

这将产生以下输出:

0 DEBUG [main] chapters.layouts.SampleLogging - 一切顺利
0 ERROR [main] chapters.layouts.SampleLogging - 或许不太顺利...

那很简单。持怀疑态度的伊利亚学派的皮罗,坚称除了不确定性外没有任何确定的东西,而这也没有被确定的可能,他可能会问:有关带有选项的布局怎么样?读者将在 MySampleLayout2.java 中找到我们自定义布局的略微修改版本。正如本手册中所述,向布局或任何其他 logback 组件添加属性只需要声明该属性的 setter 方法即可。

MySampleLayout2 类包含两个属性。第一个是可以添加到输出的前缀。第二个属性用于选择是否显示发送日志请求的线程的名称。

这是 MySampleLayout2 类的副本:

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout2 extends LayoutBase<ILoggingEvent> {

  String prefix = null;
  boolean printThreadName = true;

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  public void setPrintThreadName(boolean printThreadName) {
    this.printThreadName = printThreadName;
  }

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    if (prefix != null) {
      sbuf.append(prefix + ": ");
    }
    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    if (printThreadName) {
      sbuf.append(" [");
      sbuf.append(event.getThreadName());
      sbuf.append("] ");
    } else {
      sbuf.append(" ");
    }
    sbuf.append(event.getLoggerName());
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(LINE_SEP);
    return sbuf.toString();
  }
}

只需添加相应的 setter 方法即可启用属性的配置。注意,PrintThreadName 属性是一个 boolean 而不是 String。logback 组件的配置在 配置章节 中详细介绍。Joran 章节 提供了更多细节。这是为 MySampleLayout2 量身定制的配置文件。

传统格式

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout2">
        <prefix>MyPrefix</prefix>
        <printThreadName>false</printThreadName>
      </layout>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

规范格式 (1.3)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>

<configuration>
  <import class="chapters.layouts.MySampleLayout2"/>
  <import class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"/>
  <import class="ch.qos.logback.core.ConsoleAppender"/>

  <appender name="STDOUT" class="ConsoleAppender">
    <encoder class="LayoutWrappingEncoder">
      <layout class="MySampleLayout2">
        <prefix>MyPrefix</prefix>
        <printThreadName>false</printThreadName>
      </layout>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT"/>
  </root>
</configuration>

PatternLayout

Logback 经典版附带了一个灵活的布局,称为 PatternLayout。与所有布局一样,PatternLayout 接收日志事件并返回一个 String。但是,通过调整 PatternLayout 的转换模式,可以自定义此 String

PatternLayout 的转换模式与 C 编程语言中的 printf() 函数的转换模式密切相关。转换模式由文字文本和称为 转换说明符 的格式控制表达式组成。您可以在转换模式中插入任何文字文本。每个转换说明符以百分号 '%' 开头,后面是可选的 格式修饰符,转换词和大括号之间的可选参数。转换词控制要转换的数据字段,例如日志记录器名称、级别、日期或线程名称。格式修饰符控制字段宽度、填充以及左右对齐。

如前所述,FileAppender 和其子类期望一个编码器。因此,当与 FileAppender 或其子类一起使用时,必须将 PatternLayout 包装在编码器内。考虑到 FileAppender/PatternLayout 组合非常常见,logback 附带了一个名为 PatternLayoutEncoder 的编码器,专门用于包装 PatternLayout 实例,以便将其视为编码器。以下是一个示例,演示了如何以程序方式配置带有 PatternLayoutEncoderConsoleAppender

示例:使用 PatternLayout 的示例用法 (logback-examples/src/main/java/chapters/layouts/PatternSample.java)

package chapters.layouts;

import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;

public class PatternSample {

  static public void main(String[] args) throws Exception {
    Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    LoggerContext loggerContext = rootLogger.getLoggerContext();
    // we are not interested in auto-configuration
    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");
  }
}

在上面的示例中,转换模式被设置为 "%-5level [%thread]: %message%n"。logback 中包含的转换词的概要将很快给出。运行 PatternSample 应用程序:

java java chapters.layouts.PatternSample

将在控制台上产生以下输出。

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

请注意,在转换模式 "%-5level [%thread]: %message%n" 中,文字文本和转换说明符之间没有显式的分隔符。在解析转换模式时,PatternLayout 能够区分文字文本(空格字符、括号、冒号字符)和转换说明符。在上面的示例中,转换说明符 %-5level 表示日志事件的级别应该左对齐到五个字符的宽度。格式说明符将在下面解释。

PatternLayout 中,括号可以用于分组转换模式。因此,'('')' 具有特殊含义,如果要作为字面值使用,则需要转义。 括号的特殊性质在下面的 解释

如前所述,某些转换说明符可能包含在大括号中传递的可选参数。一个带有选项的示例转换说明符可以是 %logger{10}。这里的 "logger" 是转换词,10 是选项。选项将在下面 进一步讨论

下表描述了识别的转换词及其选项。当在同一个表格单元中列出多个转换词时,它们被视为别名。

Conversion WordEffect
c{length}
lo{length}
logger{length}
输出记录事件起源的记录器名称。
该转换词接受一个整数作为首个和唯一选项。转换器的缩写算法会缩短记录器名称,通常不会造成重大含义的丢失。将 length 选项的值设置为零属于例外情况。这将导致转换词返回记录器名称中最右边的点字符右侧的子字符串。下表提供了缩写算法的示例。

转换说明符记录器名称结果
%loggermainPackage.sub.sample.BarmainPackage.sub.sample.Bar
%logger{0}mainPackage.sub.sample.BarBar
%logger{5}mainPackage.sub.sample.Barm.s.s.Bar
%logger{10}mainPackage.sub.sample.Barm.s.s.Bar
%logger{15}mainPackage.sub.sample.Barm.s.sample.Bar
%logger{16}mainPackage.sub.sample.Barm.sub.sample.Bar
%logger{26}mainPackage.sub.sample.BarmainPackage.sub.sample.Bar

请注意,记录器名称中最右边的部分永远不会被缩写,即使它的长度比 length 选项更长。其他部分可以被缩短至最多一个字符,但不会被删除。
C{length}
class{length}
输出发出日志请求的调用者的完全限定类名。

与上面的 %logger 转换词一样,此转换接受一个整数选项来缩短类名。零具有特殊含义,将导致仅打印简单的类名,而不包括包名前缀。默认情况下,将完整类名打印输出。

生成调用类信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。
contextName
cn
输出与产生事件的记录器关联的记录器上下文的名称。
d{pattern}
date{pattern}
d{pattern, timezone}
date{pattern,timezone}
d{pattern, timezone,locale}
date{pattern,timezone,locale}
用于输出日志事件的日期。日期转换词允许将模式字符串作为参数。模式语法与 java.text.SimpleDateFormat(在 logback 1.2.x 中)和 java.time.format.DateTimeFormatter(在 logback 1.3.x 中)所接受的格式兼容。

您可以指定字符串 "ISO8601" 以获取 ISO8601 日期格式。请注意,如果没有模式参数,%date 转换词默认为 ISO 8601 日期格式

以下是一些示例参数值。它们假定实际日期为 2006 年 10 月 20 日星期五,作者在午餐后返回处理该文档。

转换模式结果
%d2006-10-20 14:06:49,812
%date2006-10-20 14:06:49,812
%date{ISO8601}2006-10-20 14:06:49,812
%date{HH:mm:ss.SSS}14:06:49.812
%date{dd MMM yyyy;HH:mm:ss.SSS}20 oct. 2006;14:06:49.812

第二个参数指定时区。例如,模式 '%date{HH:mm:ss.SSS,Australia/Perth}' 将以澳大利亚珀斯时区的时间打印。请注意,在没有时区参数的情况下,默认使用主机 Java 平台的默认时区。如果指定的时区标识符未知或拼写错误,则假定为 GMT 时区,这是由 TimeZone.getTimeZone(String) 方法规定的。

==自 1.3.6/1.4.6 起== 第三个参数指定了语言环境。例如,编写 '%date{HH:mm:ss.SSS,Australia/Perth,en-AU}' 将使用澳大利亚英语语言环境打印日期。如果省略了语言环境参数,则使用系统的默认语言环境。

如果选项包括大括号、空格或逗号等特殊字符,可以将其括在单引号 双引号之间。

==常见错误== 鉴于逗号 ',' 字符被解释为参数分隔符,模式 HH:mm:ss,SSS 将被解释为模式 HM:mm:ss 和时区 SSS。如果您希望在日期模式中包含逗号,只需将模式放在单引号或双引号之间。例如,%date{"HH:mm:ss,SSS"}%date{'HH:mm:ss,SSS'}
micros / ms==自 1.3 版起== 输出事件中包含的时间戳的微秒数。

出于性能原因,微秒需要单独指定,并且需要额外使用 %date
F / file输出发出日志请求的 Java 源文件的文件名。

生成文件信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。
caller{depth}
caller{depthStart..depthEnd}
caller{depth, evaluator-1, ... evaluator-n}
caller{depthStart..depthEnd, evaluator-1, ... evaluator-n}
输出生成日志事件的调用者的位置信息。

位置信息取决于 JVM 实现,但通常包括调用方法的完全限定名称,后跟调用者的源、文件名和行号,括号括起来。

可以在 caller 转换说明符的选项中添加整数,以配置要显示的信息深度。

例如,%caller{2} 将显示以下摘录:
0    [main] DEBUG - logging statement
Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)
%caller{3} 将显示以下另一个摘录:
16   [main] DEBUG - logging statement
Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)
Caller+2   at mainPackage.ConfigTester.main(ConfigTester.java:38)
可以在 caller 转换说明符的选项中添加范围说明符,以配置要显示的信息深度范围。

例如,%caller{1..2} 将显示以下摘录:
0    [main] DEBUG - logging statement
Caller+0   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)
此转换词还可以使用评估器,在计算调用者数据之前测试日志事件是否符合给定条件。例如,使用 %caller{3, CALLER_DISPLAY_EVAL} 将仅在称为 CALLER_DISPLAY_EVAL 的评估器返回 负数 答案时显示三行堆栈跟踪。有关评估器的描述,请参见本文档后面的内容。
kvp{NONE,SINGLE,DOUBLE}输出日志事件中包含的键值对。默认情况下,值部分将用双引号括起。您可以通过指定 NONESINGLE 来覆盖默认设置,以获得无引号字符或单引号字符 '

事件中包含的值键对 {k1, v1}{k2, v2} 将按如下方式输出:
NONEv1=k1 v2=k2
DOUBLEv1="k1" v2="k2"
SINGLEv1='k1' v1='k1'
默认与 DOUBLE 指定相同
L / line输出发出日志请求的行号。

生成行号信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。
m / msg / message输出与日志事件相关联的应用程序提供的消息。
M / method输出发出日志请求的方法名称。

生成方法名称并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。
n输出与平台相关的换行符字符或字符。

此转换词的性能几乎与使用非可移植换行符字符串(如 "\n""\r\n") 相同。因此,这是指定换行符的首选方式。
p / le / level输出日志事件的级别。
r / relative输出从应用程序启动到创建日志事件经过的毫秒数。
t / thread输出生成日志事件的线程名称。
X{key:-defaultVal}
mdc{key:-defaultVal}
输出生成日志事件的线程关联的 MDC(映射诊断上下文)。

如果 mdc 转换词后面跟着大括号中的键,如 %mdc{userid},则将输出与键 'userid' 对应的 MDC 值。如果该值为 null,则将输出在 :- 运算符之后指定的 默认值。如果未指定默认值,则输出为空字符串。

如果未给出键,则将以 "key1=val1, key2=val2" 的格式输出 MDC 的全部内容。

有关有关主题的更多详细信息,请参阅 MDC 章节
ex{depth}
exception{depth}
throwable{depth}

ex{depth, evaluator-1, ..., evaluator-n}
exception{depth, evaluator-1, ..., evaluator-n}
throwable{depth, evaluator-1, ..., evaluator-n}
输出与日志事件相关联的异常的堆栈跟踪(如果有)。默认情况下,将输出完整的堆栈跟踪。

throwable 转换关键字后面可以跟以下选项之一:

  • short : 打印堆栈跟踪的第一行
  • full : 打印完整的堆栈跟踪
  • 任意整数: 打印给定行数的堆栈跟踪

以下是一些示例:

转换模式结果
%ex
mainPackage.foo.​bar.TestException: Houston we have a problem
  at mainPackage.foo.​bar.TestThrower.​fire(TestThrower.​java:22)
  at mainPackage.foo.​bar.TestThrower.​readyToLaunch(TestThrower.​java:17)
  at mainPackage.ExceptionLauncher.​main(ExceptionLauncher.​java:38)
%ex{short}
mainPackage.foo.​bar.TestException: Houston we have a problem
  at mainPackage.foo.​bar.TestThrower.​fire(TestThrower.​java:22)
%ex{full}
mainPackage.foo.​bar.TestException: Houston we have a problem
  at mainPackage.foo.​bar.TestThrower.​fire(TestThrower.​java:22)
  at mainPackage.foo.​bar.TestThrower.​readyToLaunch(TestThrower.​java:17)
  at mainPackage.ExceptionLauncher.​main(ExceptionLauncher.​java:38)
%ex{2}
mainPackage.foo.​bar.TestException: Houston we have a problem
    at mainPackage.foo.​bar.TestThrower.​fire(TestThrower.​java:22)
    at mainPackage.foo.​bar.TestThrower.​readyToLaunch(TestThrower.​java:17)
此转换关键字还可以使用评估器来测试日志事件针对特定标准的输出。例如,使用 %ex{full, EX_DISPLAY_EVAL} 将仅在称为 EX_DISPLAY_EVAL 的评估器返回 否定 答案时显示异常的完整堆栈跟踪。有关评估器的详细信息,请参阅本文档后面的描述。

如果在转换模式中未指定 %throwable 或另一个与 throwable 相关的转换关键字,PatternLayout 会自动将其添加为最后一个转换关键字,因为堆栈跟踪信息非常重要。如果您不希望显示堆栈跟踪信息,可以用 $nopex 转换关键字替代 %throwable。另请参阅 %nopex 转换关键字。
xEx{depth}
xException{depth}
xThrowable{depth}

xEx{depth, evaluator-1, ..., evaluator-n}
xException{depth, evaluator-1, ..., evaluator-n}
xThrowable{depth, evaluator-1, ..., evaluator-n}
与上述 %throwable 转换关键字相同,但附加了类包信息。

在异常的每个堆栈帧末尾,将添加一个字符串,其中包含包含相关类的 jar 文件,后跟该 jar 文件 MANIFEST.MF 中找到的 "Implementation-Version"。这种创新技术最初是由 James Strachan 提出的。如果信息不确定,那么类包装数据将以波浪线 '~' 字符开头。

以下是一个示例:

java.lang.NullPointerException
    at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]
    at  com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06]
    at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06]
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na]
    at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na]
    ...etc 
Logback 会竭尽全力确保其显示的类包装信息是正确的,即使在任意复杂的类加载器层次结构中也是如此。但是,当无法保证信息的绝对正确性时,它将使用波浪线 '~' 字符作为数据前缀。因此,在上述示例中,鉴于 Wombat 类的包装数据以波浪线开头,实际上可能正确的包装数据是 [wombat.jar:1.7]

请注意,鉴于其潜在成本,默认情况下禁用了包装数据的计算。启用包装数据计算时,PatternLayout 将自动假定模式字符串末尾的 %xThrowable 后缀,而不是 %throwable 后缀。来自用户的 反馈 表明,Netbeans 对包装信息存在问题。
nopex
nopexception
尽管 假装 处理堆栈跟踪数据,但此转换关键字不会输出任何数据,因此实际上忽略异常。

%nopex 转换关键字允许用户覆盖 PatternLayout 的内部安全机制,后者会在没有其他处理异常的转换关键字的情况下默默添加 %xThrowable 转换关键字。
marker输出与日志请求相关联的标记。

如果标记包含子标记,则转换器将按照下面显示的格式显示父标记以及子标记的名称。

parentName [ child1, child2 ]
property{key}输出名为 key 的属性关联的值。有关如何定义变量的相关文档,请参见 定义变量变量范围。如果 key 不是记录器上下文的属性,那么将在系统属性中查找 key

key 没有默认值。如果省略,返回的值将是 "Property_HAS_NO_KEY",显式表示错误条件。
replace(p){r, t}在由子模式 'p' 生成的字符串中,将正则表达式 'r' 的出现替换为其替换项 't'。例如,"%replace(%msg){'\s', ''}" 将删除事件消息中包含的所有空格。

模式 'p' 可以是任意复杂的,特别是可以包含多个转换关键字。例如,"%replace(%logger %msg){'\.', '/'}" 将使用正斜杠替换事件的记录器或消息中的所有点。
prefix(p)对模式 'p' 中包含的所有子转换器的输出,都添加转换器名称的前缀。在需要分析日志内容的环境中,通常希望用前缀标记模式的内容。

例如,您可能希望使用以下模式来便于解析日志文件:

%d thread=%thread level=%level logger=%logger user=%X{user} %message

%prefix 复合转换器可以为您处理前缀:

%d %prefix(%thread %level %logger %X{user}) %message

前两个模式将生成等效的输出。%prefix 转换器的实用性随着模式 'p' 中包含的子转换器数量的增加而增加。
rEx{depth}
rootException{depth}

rEx{depth, evaluator-1, ..., evaluator-n}
rootException{depth, evaluator-1, ..., evaluator-n}
输出与日志事件相关联的异常的堆栈跟踪(如果有)。首先输出根本原因,而不是标准的“最后的根本原因”。以下是一个样本输出(已编辑以节省空间):

java.lang.NullPointerException
    at com.xyz.Wombat(Wombat.java:57) ~<span class="hljs-string">[wombat-1.3.jar:1.3]
    at com.xyz.Wombat(Wombat.java:76) ~<span class="hljs-string">[wombat-1.3.jar:1.3]
  Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat':
    at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) <span class="hljs-string">[spring-2.0.jar:2.0]
    at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) <span class="hljs-string">[spring-2.0.jar:2.0]
    at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) <span class="hljs-string">[tomcat-6.0.26.jar:6.0.26]
%rootException 转换器接受与上述 %xException 转换器相同的可选参数,包括深度和评估器。它还输出包装信息。简而言之,%rootException%xException 非常相似,只是异常输出的顺序相反。

作者 %rootException 转换器的 Tomasz Nurkiewicz 在一篇名为 "Logging exceptions root cause first" 的博客文章中记录了他的贡献。

% 字符具有特殊含义

在转换模式的上下文中,百分号具有特殊含义,如果要将其作为文字包含在内,需要使用反斜杠进行转义,例如 "%d %p \% %m%n"

转换词后立即跟随的文字的限制

在大多数情况下,文字自然包含空格或其他分隔符,以免与转换词混淆。例如,模式 "%level[%thread]-%message%n" 包含字符串文字 "[""]-"。然而,如果在转换词后紧跟一个可以作为 Java 标识符一部分的字符,logback 的模式解析器会被误认为该文字是转换词的一部分。例如,模式 "%date%nHello" 将被解释为两个转换词 %date%nHello,因为 %nHello 不是已知的转换词,所以 logback 会输出 %PARSER_ERROR[nHello] 用于 %nHello。如果希望字符串文字 "Hello" 立即将 %nHello 分开,可以将空参数列表传递给 %n。例如,"%date%n{}Hello" 将被解释为 %date 后跟 %n 后跟文字 "Hello"

格式修饰符

默认情况下,相关信息会按原样输出。然而,通过格式修饰符的帮助,可以更改每个数据字段的最小和最大宽度以及对齐方式。

可选的格式修饰符放置在百分号和转换字符或词之间。

第一个可选的格式修饰符是 左对齐标志,即减号(-)字符。然后是可选的 最小字段宽度 修饰符。这是一个十进制常量,表示要输出的最少字符数。如果数据项包含的字符少于这个宽度,它将在左侧或右侧填充,直到达到最小宽度。默认情况是在左侧填充(右对齐),但可以使用左对齐标志指定右填充。填充字符是空格。如果数据项大于最小字段宽度,则该字段会扩展以容纳数据。该值永远不会被截断。

可以使用 最大字段宽度 修饰符来更改此行为,该修饰符由一个小数点和一个十进制常量组成。如果数据项长于最大字段宽度,则会从数据项的 开头 删除多余的字符。例如,如果最大字段宽度是 8,数据项长度为 10 个字符,则会删除数据项的前两个字符。这种行为与 C 语言中的 printf 函数不同,C 语言中的截断是从末尾进行的。

通过在小数点后面直接添加一个减号字符,可以实现从末尾进行截断。在这种情况下,如果最大字段宽度为 8,数据项长度为 10 个字符,则会删除数据项的最后两个字符。

下面是 logger 转换说明符的各种格式修饰符示例。

格式修饰符左对齐最小宽度最大宽度注释
%20loggerfalse20如果日志记录器名称少于 20 个字符,则左填充空格。
%-20loggertrue20如果日志记录器名称少于 20 个字符,则右填充空格。
%.30loggerNA30如果日志记录器名称超过 30 个字符,则从开头截断。
%20.30loggerfalse2030如果日志记录器名称较短,则左填充空格。但是,如果日志记录器名称超过 30 个字符,则从开头截断。
%-20.30loggertrue2030如果日志记录器名称较短,则右填充空格。但是,如果日志记录器名称超过 30 个字符,则从 开头 截断。
%.-30loggerNA30如果日志记录器名称超过 30 个字符,则从 末尾 截断。

下表列出了格式修饰符截断的示例。请注意,方括号,即一对 "[]" 字符,不是输出的一部分。它们用于界定输出的宽度。

格式修饰符日志记录器名称结果
[%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]

仅输出一个字母表示级别

在级别的位置上,不要打印 TRACE、DEBUG、WARN、INFO 或 ERROR,而是只打印 T、D、W、I 和 E。你可以编写一个用于此目的的自定义转换器,或者简单地利用格式修饰符(刚刚讨论过的)将级别值缩短为一个字符。合适的转换说明符将是 "%.-1level"

版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:www.liujiajia.me/2023/12/7/l…