Skip to content

Logback 手册 - 第六章:布局

🏷️ Logback 手册


来源:https://logback.qos.ch/manual/layouts.html
作者: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


什么是布局?

WARNING

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

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

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

示例输出可能如下所示:

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

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

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

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)

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

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

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

这将产生以下输出:

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

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

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

这是 MySampleLayout2 类的副本:

java
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 量身定制的配置文件。

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)

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 应用程序:

bash
java java chapters.layouts.PatternSample

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

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

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

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

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

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

Conversion Word

logger

  • 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 选项更长。其他部分可以被缩短至最多一个字符,但不会被删除。

class

  • C{length}
  • class{length}

输出发出日志请求的调用者的完全限定类名。

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

生成调用类信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用

contextName

  • contextName
  • cn

输出与产生事件的记录器关联的记录器上下文的名称。

date

  • 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

  • micros / ms

自 1.3 版起

输出事件中包含的时间戳的微秒数。

出于性能原因,微秒需要单独指定,并且需要额外使用 %date

file

  • F / file

输出发出日志请求的 Java 源文件的文件名。

生成文件信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。

caller

  • caller{depth}
  • caller{depthStart..depthEnd}
  • caller{depth, evaluator-1, ... evaluator-n}
  • caller{depthStart..depthEnd, evaluator-1, ... evaluator-n}

输出生成日志事件的调用者的位置信息。

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

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

例如,%caller{2} 将显示以下摘录:

txt
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} 将显示以下另一个摘录:

txt
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} 将显示以下摘录:

txt
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

  • kvp{NONE,SINGLE,DOUBLE}

输出日志事件中包含的键值对。默认情况下,值部分将用双引号括起。您可以通过指定 NONESINGLE 来覆盖默认设置,以获得无引号字符或单引号字符 '

事件中包含的值键对 {k1, v1}{k2, v2} 将按如下方式输出:

NONEv1=k1 v2=k2
DOUBLEv1="k1" v2="k2"
SINGLEv1='k1' v1='k1'
默认与 DOUBLE 指定相同

line

  • L / line

输出发出日志请求的行号。

生成行号信息并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。

message

  • m / msg / message

输出与日志事件相关联的应用程序提供的消息。

method

  • M / method

输出发出日志请求的方法名称。

生成方法名称并不是特别快速的。因此,除非执行速度不是问题,否则应避免使用。

newline

  • n

输出与平台相关的换行符字符或字符。

此转换词的性能几乎与使用非可移植换行符字符串(如 "\n""\r\n" )相同。因此,这是指定换行符的首选方式。

level

  • p / le / level

输出日志事件的级别。

relative

  • r / relative

输出从应用程序启动到创建日志事件经过的毫秒数。

thread

t / thread

输出生成日志事件的线程名称。

mdc

  • X{key:-defaultVal}
  • mdc{key:-defaultVal}

输出生成日志事件的线程关联的 MDC(映射诊断上下文)。

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

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

有关有关主题的更多详细信息,请参阅 MDC 章节

ex

  • 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

    txt
    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}

    txt
    mainPackage.foo.​bar.TestException: Houston we have a problem
      at mainPackage.foo.​bar.TestThrower.​fire(TestThrower.​java:22)
  • %ex{full}

    txt
    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}

    txt
    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 转换关键字。

xThrowable

  • 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 提出的。如果信息不确定,那么类包装数据将以波浪线 '~' 字符开头。

以下是一个示例:

txt
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

  • nopex
  • nopexception

尽管 假装 处理堆栈跟踪数据,但此转换关键字不会输出任何数据,因此实际上忽略异常。

%nopex 转换关键字允许用户覆盖 PatternLayout 的内部安全机制,后者会在没有其他处理异常的转换关键字的情况下默默添加 %xThrowable 转换关键字。

marker

  • marker

输出与日志请求相关联的标记。

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

parentName [ child1, child2 ]

property

  • property{key}

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

key 没有默认值。如果省略,返回的值将是 "Property_HAS_NO_KEY",显式表示错误条件。

replace

  • replace(p){r, t}

在由子模式 'p' 生成的字符串中,将正则表达式 'r' 的出现替换为其替换项 't'。例如,"%replace(%msg){'\s', ''}" 将删除事件消息中包含的所有空格。

模式 'p' 可以是任意复杂的,特别是可以包含多个转换关键字。例如,"%replace(%logger %msg){'\.', '/'}" 将使用正斜杠替换事件的记录器或消息中的所有点。

prefix

  • prefix(p)

对模式 'p' 中包含的所有子转换器的输出,都添加转换器名称的前缀。在需要分析日志内容的环境中,通常希望用前缀标记模式的内容。

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

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

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

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

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

rootException

  • rEx{depth}
  • rootException{depth}
  • rEx{depth, evaluator-1, ..., evaluator-n}
  • rootException{depth, evaluator-1, ..., evaluator-n}

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

txt
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]
  Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat':
    at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) \ [spring-2.0.jar:2.0]
    at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) \ [spring-2.0.jar:2.0]
    at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) \ [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"

转换词选项

转换说明符后面可以跟随选项。它们总是在大括号之间声明。我们已经看到了一些选项提供的可能性,例如与 MDC 转换说明符结合使用,如:%mdc{someKey}

一个转换说明符可能有多个选项。例如,一个使用评估器的转换说明符(即将介绍)可以将评估器名称添加到选项列表中,如下所示:

perl
<pattern>%-4relative [%thread] %-5level - %msg%n \
  %caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</pattern>

如果选项包含特殊字符,如大括号、空格或逗号,可以用单引号或双引号将其括起来。例如,考虑下一个模式。

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

我们将选项 \d{16}XXXX 传递给 replace 转换词。它将用 XXXX 替换消息中包含的任何 14、15 或 16 位数字序列,从而有效地混淆信用卡号码。注意,\d 是常规表达式中表示单个数字的简写。"{14,16\}" 被解释为 "{14, 16}",即重复前面的项至少 14 次但最多 16 次。

使用括号进行分组

在 logback 中,模式字符串中的括号被视为分组标记。因此,可以对子模式进行分组并对该子模式应用格式化指令。

例如,模式

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

将对由子模式 "%d{HH:mm:ss.SSS} [%thread]" 生成的输出进行分组,如果少于 30 个字符则右对齐。

假设没有分组时的输出是

txt
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

使用 "%-30()" 分组后,输出将变为

txt
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

后一种形式更容易阅读。

如果需要将括号字符视为字面量,则需要在每个括号之前加上反斜杠进行转义,如 \(%d{HH:mm:ss.SSS} [%thread]\)

【颜色设置】

按照上面所解释的方式,使用括号进行分组可以对子模式进行着色。从版本 1.0.5 开始,PatternLayout 可以识别 "%black""%red""%green""%yellow""%blue""%magenta""%cyan""%white""%gray""%boldRed""%boldGreen""%boldYellow""%boldBlue""%boldMagenta""%boldCyan""%boldWhite""%highlight" 作为转换词。这些转换词旨在包含子模式。任何被染色词括起来的子模式都将以指定的颜色输出。

下面是一个演示如何进行着色的配置文件例子。请注意,"%logger{15}"%cyan 转换说明符所包含。这将输出缩写为 15 个字符的记录器名称,并以青色显示。%highlight 转换说明符会以粗体红色打印其子模式,其中 ERROR 级别的事件以红色、WARN 级别的事件以 BLUE 色、INFO 级别的事件以默认颜色打印。

演示:高亮显示级别(logback-examples/src/main/resources/chapters/layouts/highlighted.xml)

withJansi 设置为 true 可以启用 Jansi 库的 ANSI 颜色代码解释,该库会在底层终端不兼容时透明地过滤掉 ANSI 转义序列。这是跨平台部署的最安全选择,但在类路径中需要具有 org.fusesource.jansi:jansi:1.17 或更高版本。请注意,基于 Unix 的操作系统(如 Linux 和 Mac OS X)本地支持 ANSI 颜色代码,通常不需要启用 Jansi 库,但这样做是无害的。然而,在 Windows 上,建议启用 Jansi 以便在 DOS 命令提示符上从颜色代码解释中受益,否则会发送 ANSI 转义序列,它们无法解释。

以下是对应的输出:

txt
[main] WARN  c.l.TrivialMain - a warning message 0
[main] DEBUG c.l.TrivialMain - hello world number1
[main] DEBUG c.l.TrivialMain - hello world number2
[main] INFO  c.l.TrivialMain - hello world number3
[main] DEBUG c.l.TrivialMain - hello world number4
[main] WARN  c.l.TrivialMain - a warning message 5
[main] ERROR c.l.TrivialMain - Finish off with fireworks

创建一个彩色转换词只需要很少的代码。章节 创建自定义转换说明符 讨论了在配置文件中注册转换词所需的步骤。

【评估器】

如上所述,当需要一个转换说明符根据一个或多个 EventEvaluator 对象动态地进行处理时,选项列表非常有用。EventEvaluator 对象负责确定给定的日志事件是否符合评估器的条件。

让我们回顾一个涉及 EventEvaluator 的例子。下一个配置文件将日志事件输出到控制台,显示日期、线程、级别、消息和调用数据。鉴于提取记录器的调用者数据会比较昂贵,因此只有在日志请求来源于特定记录器并且消息包含某个字符串时,我们才会这样做。因此,我们确保仅特定的日志请求将生成并显示其调用者信息。在其他情况下,其中调用方数据是多余的,我们将不会惩罚应用程序性能。

评估器,尤其是 评估表达式,在 过滤器章节 中专门介绍,如果您想以任何有意义的方式使用评估器,则必须阅读该章节。还要注意,以下示例隐式基于 JaninoEventEvaluator,它需要 Janino 库。请参阅设置文档中的 相应部分

演示:EventEvaluators 的使用示例(logback-examples/src/main/resources/chapters/layouts/callerEvaluatorConfig.xml)

上述评估表达式匹配来自名称包含字符串 "chapters.layouts" 的记录器发出的事件,并且消息包含字符串 "who calls thee"。由于 XML 编码规则,& 字符不能直接写入,需要转义为 &amp;

以下类利用了上述配置文件中提到的一些特性。

示例:EventEvaluators 的示例用法 (logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java)

java
package chapters.layouts;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class CallerEvaluatorExample {

  public static void main(String[] args)  {
    Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class);
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(lc);
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
      // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

    for (int i = 0; i < 5; i++) {
      if (i == 3) {
        logger.debug("who calls thee?");
      } else {
        logger.debug("I know me " + i);
      }
    }
  }
}

上述应用程序没有特别花哨的地方。发出五个日志记录请求,第三个请求发出消息 "who calls thee?"

命令

bash
java chapters.layouts.CallerEvaluatorExample src/main/java/chapters/layouts/callerEvaluatorConfig.xml

将产生以下输出:

txt
0    [main] DEBUG - I know me 0
0    [main] DEBUG - I know me 1
0    [main] DEBUG - I know me 2
0    [main] DEBUG - who calls thee?
Caller+0   at chapters.layouts.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28)
0    [main] DEBUG - I know me 4

当发出日志记录请求时,相应的日志事件被评估。只有第三个日志事件符合评估标准,导致其调用者数据被显示。对于其他日志事件,评估标准不匹配,不会打印调用者数据。

可以更改表达式以对应实际情况。例如,可以结合记录器名称和请求级别。因此,从应用程序的敏感部分(例如财务交易模块)发出的级别为 WARN 及以上的日志记录请求将显示其调用者数据。

重要提示: 使用 caller 转换词,当 表达式评估为 true 时输出调用者数据。

让我们考虑另一种情况。当异常包含在日志记录请求中时,它们的堆栈跟踪也会输出。但是,有时可能希望对某些特定异常抑制堆栈跟踪。

下面显示的 Java 代码创建了三个具有异常的日志请求。第二个异常与其他异常不同:它包含字符串“do not display this”,并且其类型为 chapters.layouts.TestException。现在,让我们阻止第二个异常被打印。

示例:EventEvaluators 的示例用法 (logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java)

java
package chapters.layouts;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class ExceptionEvaluatorExample {

  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class);
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(lc);
      lc.reset();
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
       // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

    for (int i = 0; i < 3; i++) {
      if (i == 1) {
        logger.debug("logging statement " + i, new TestException(
            "do not display this"));
      } else {
        logger.debug("logging statement " + i, new Exception("display"));
      }
    }
  }
}

在下一个配置文件中,评估表达式匹配包含 chapters.layouts.TextException 类型的可抛出事件,这恰好是我们希望抑制的异常类型。

示例:EventEvaluators 的用法示例(logback-examples/src/main/resources/chapters/layouts/exceptionEvaluatorConfig.xml)

通过这个配置,每当 chapters.layouts.TestException 的实例被包含在日志请求中时,堆栈跟踪将被抑制。

运行以下命令

bash
java chapters.layouts.ExceptionEvaluatorExample src/main/java/chapters/layouts/exceptionEvaluatorConfig.xml

会产生以下结果

txt
logging statement 0
java.lang.Exception: display
  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]
logging statement 1
logging statement 2
java.lang.Exception: display
  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]

注意第二个日志语句没有堆栈跟踪。我们成功地抑制了 TextException 的堆栈跟踪。每个堆栈跟踪行末尾的方括号中的文本是之前讨论过的 打包信息

注意

使用 %ex 转换说明符时,当 表达式评估为 false 时显示堆栈跟踪

创建自定义转换说明符

到目前为止,我们已经介绍了 PatternLayout 中内置的转换词。但是,您也可以添加自己制作的转换词。

构建自定义转换说明符包括两个步骤。

第一步

首先,您必须扩展 ClassicConverter 类。ClassicConverter 对象负责从 ILoggingEvent 实例中提取信息并生成字符串。例如,LoggerConverter,即 %logger 转换词的基础转换器,从 ILoggingEvent 中提取 Logger 的名称并将其作为字符串返回。它可能在此过程中缩写 Logger 名称。

下面是一个返回自创建以来经过的纳秒数的自定义转换器的示例:

示例:自定义转换器示例(src/main/java/chapters/layouts/MySampleConverter.java)

java
public class MySampleConverter extends ClassicConverter {

  long start = System.nanoTime();

  @Override
  public String convert(ILoggingEvent event) {
    long nowInNanos = System.nanoTime();
    return Long.toString(nowInNanos-start);
  }
}

这个实现非常简单。MySampleConverter 类扩展了 ClassicConverter,并实现了 convert 方法,该方法返回自创建以来经过的纳秒数的字符串。

第二步

在第二步中,我们必须让 logback 知道新的 Converter。为此,我们需要在配置文件中声明新的转换词,如下所示:

示例:示例转换器示例(src/main/java/chapters/layouts/mySampleConverterConfig.xml)

一旦在配置文件中声明了新的转换词,我们就可以像其他转换词一样在 PatternLayout 模式中引用它。

以下命令:

bash
java chapters.layouts.SampleLogging src/main/java/chapters/layouts/mySampleConverterConfig.xml

应该产生类似于以下的输出:

txt
4868695 [main] DEBUG - Everything's going well
5758748 [main] ERROR - maybe not quite...

读者可能想看看其他 Converter 的实现,比如 MDCConverter,以了解更复杂的行为,比如选项处理。要创建自己的着色方案,请查看 HighlightingCompositeConverter

HTMLLayout

HTMLLayout(包含在 logback-classic 中)以 HTML 格式生成日志。HTMLLayout 将日志事件输出为 HTML 表格,其中表的每一行对应一个日志事件。

下面是使用默认 CSS 样式表生成的 HTMLLayout 的示例输出:

HTML Layout 示例图像

表列的内容通过转换模式指定。有关转换模式的文档,请参见 PatternLayout。因此,您可以完全控制表的内容和格式。您可以选择和显示 PatternLayout 所知道的任何转换器的任意组合。

关于在 HTMLLayout 中使用 PatternLayout 的一个显着例外是,转换说明符不应由空格字符或更一般地由文本文字分隔。模式中找到的每个说明符都将生成一个单独的列。同样,对于模式中找到的每个文本块,将生成一个单独的列,可能会浪费宝贵的屏幕空间。

下面是一个简单但功能齐全的配置文件示例,演示了如何使用 HTMLLayout

示例:HTMLLayout 示例(src/main/java/chapters/layouts/htmlLayoutConfig1.xml)

TrivialMain 应用程序记录了一些以异常结束的消息。以下命令:

bash
java chapters.layouts.TrivialMain src/main/java/chapters/layouts/htmlLayoutConfig1.xml

将在当前文件夹中创建名为 test.html 的文件。test.html 的内容应类似于以下内容:

HTML Layout Sample Image

堆栈跟踪

如果您使用 %em 转换词来显示堆栈跟踪,将创建一个表列来显示堆栈跟踪。在大多数情况下,该列将为空,浪费屏幕空间。此外,在单独的列上打印堆栈跟踪不会得到非常可读的结果。幸运的是,%ex 转换词并不是显示堆栈跟踪的唯一方法。

通过实现 IThrowableRenderer 接口,可以提供更好的解决方案来管理与异常相关的显示数据,并将其分配给 HTMLLayout。默认情况下,每个 HTMLLayout 实例都分配了一个 DefaultThrowableRenderer,它以易于阅读的方式将异常和其堆栈跟踪写在 新表行 中,如上图所示。

如果出于某种原因,您仍然希望使用 %ex 模式,则可以在配置文件中指定 NOPThrowableRenderer,以禁用为堆栈跟踪显示单独的行。我们不知道为什么您会这样做,但如果您希望,您可以这样做。

CSS

通过级联样式表(CSS)来控制 HTMLLayout 创建的 HTML 的呈现方式。如果没有特定的指令,HTMLLayout 将默认使用内部 CSS。但是,您可以指示 HTMLLayout 使用外部 CSS 文件。为此,可以在 <layout> 元素中嵌套一个 cssBuilder 元素,如下所示:

xml
<layout class="ch.qos.logback.classic.html.HTMLLayout">
  <pattern>%relative...%msg</;pattern>
  <cssBuilder class="ch.qos.logback.classic.html.UrlCssBuilder">
    <!-- css 文件所在的 url -->
    <url>http://...</url>
  </cssBuilder>
</layout>

HTMLLayout 通常与 SMTPAppender 结合使用,以便以 HTML 格式良好地格式化发送的电子邮件。

Log4j XMLLayout

XMLLayout(属于 logback-classic)生成符合 log4j.dtd 格式的输出,以与 ChainsawVigilog 等工具进行交互,这些工具可以处理由 log4j 的 XMLLayout 生成的文件。

与 log4j 版本 1.2.15 中的原始 XMLLayout 一样,logback-classic 中的 XMLLayout 具有两个布尔属性:locationInfoproperties。将 locationInfo 设置为 true 可在每个事件中包含位置信息(调用者数据)。将 properties 设置为 true 可包含 MDC 信息。这两个选项默认情况下都设置为 false

以下是一个示例配置:

示例:Log4jXMLLayout 示例(src/main/java/chapters/layouts/log4jXMLLayout.xml)

Logback access

logback-access 对日志的布局大多是基于 logback-classic 进行调整的。logback-classic 和 logback-access 模块满足不同的需求,但总体上提供了类似的功能。

编写自定义布局

编写 logback access 的自定义 Layout 与 logback-classic 中的兄弟类 Layout 几乎完全相同。

PatternLayout

在 logback-access 中,可以通过配置 PatternLayout 来实现与经典模式相似的功能。然而,它还包含了适用于记录仅在 HTTP Servlet 请求和 HTTP Servlet 响应中可用的特定信息的额外转换说明符。

下面是 logback-access 中 PatternLayout 的转换说明符列表。

转换说明符效果
a / remoteIP远程 IP 地址。
A / localIP本地 IP 地址。
b / B / bytesSent响应的内容长度。
h / clientHost远程主机。
H / protocol请求协议。
l远程日志名称。在 logback-access 中,此转换器始终返回值为 "-"
reqParameter{paramName}响应的参数。

此转换词获取花括号中的第一个选项,并在请求中查找相应的参数。

%reqParameter{input_data} 显示相应的参数。
i{header} / header{header}请求头。

此转换词获取花括号中的第一个选项,并在请求中查找相应的头部。

%header{Referer} 显示请求的引用页。

如果未指定选项,则显示所有可用的头部。
m / requestMethod请求方法。
r / requestURL请求的 URL。
s / statusCode请求的状态码。
D / elapsedTime为服务请求所花费的时间,以毫秒为单位。
T / elapsedSeconds为服务请求所花费的时间,以秒为单位。
t / date输出日志事件的日期。日期转换说明符后面可以跟随一个括号,其中包含 java.text.SimpleDateFormat 使用的日期和时间模式字符串。ISO8601 也是一个有效的值。

例如,%t{HH:mm:ss,SSS}%t{ddMMMyyyy;HH:mm:ss,SSS}。如果没有指定日期格式说明符,则假定为通用日志格式的日期格式,即 %t{dd/MMM/yyyy:HH:mm:ssZ}
u / user远程用户。
q / queryString请求查询字符串,以'?'开头。
U / requestURI请求的 URI。
S / sessionID会话 ID。
v / server服务器名称。
I / threadName处理请求的线程名称。
localPort本地端口。
reqAttribute{attributeName}请求属性。

此转换词获取花括号中的第一个选项,并在请求中查找相应的属性。

%reqAttribute{SOME_ATTRIBUTE} 显示相应的属性。
reqCookie{cookie}请求 Cookie。

此转换词获取花括号中的第一个选项,并在请求中查找相应的 Cookie。

%cookie{COOKIE_NAME} 显示相应的 Cookie。
responseHeader{header}响应头。

此转换词获取花括号中的第一个选项,并在响应中查找相应的头部。

%header{Referer} 显示响应的引用页。
requestContent此转换词显示请求的内容,即请求的 InputStream。它与 TeeFilter 一起使用,TeeFilter 是一个 javax.servlet.Filter,它将原始的 HttpServletRequest 替换为 TeeHttpServletRequest。后者允许多次访问请求的 InputStream 而不丢失任何数据。
fullRequest此转换器输出与请求相关的所有数据,包括所有头部和请求内容。
responseContent此转换词显示响应的内容,即响应的 InputStream。它与 TeeFilter 一起使用,TeeFilter 是一个 javax.servlet.Filter,它将原始的 HttpServletResponse 替换为 TeeHttpServletResponse。后者允许多次访问请求的 InputStream 而不丢失任何数据。
fullResponse此转换词获取与响应相关的所有可用数据,包括响应的所有头部和响应内容。

logback-access 的 PatternLayout 还识别三个关键字,它们像快捷方式一样起作用。

关键字等效的转换模式
commonCLF*%h %l %u [%t] "%r" %s %b*
combined%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"

common 关键字对应的模式是 '%h %l %u [%t] "%r" %s %b',显示客户端主机、远程日志名称、用户、日期、请求的 URL、状态码和响应的内容长度。

combined 关键字是 '%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"' 的快捷方式。该模式与 common 模式类似,但还显示了两个请求头,即引用页和用户代理。

HTMLLayout

logback-access 中的 HTMLLayout 类与 logback-classic 中的 HTMLLayout 类相似。

默认情况下,它会创建一个包含以下数据的表格:

  • 远程 IP
  • 日期
  • 请求的 URL
  • 状态码
  • 内容长度

以下是 logback-access 中的 HTMLLayout 生成的示例输出:

Access HTML Layout Sample Image

有什么比一个真实世界的例子更好呢?我们自己的 log4j 日志配置文件中使用了 logback-access 来演示带有 HTMLLayoutRollingFileAppender 的实时输出。

每当用户在我们的 translator 网络应用程序上发起新的请求时,将向访问日志添加新条目,您可以通过 点击此链接 查看。