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


来源: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 接口的概要。

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

示例输出可能如下所示:

accesslog 复制代码
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)

传统格式

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 复制代码
<?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 目录中执行以下命令。

shell 复制代码
java chapters.layouts.SampleLogging src/main/java/chapters/layouts/sampleLayoutConfig.xml

这将产生以下输出:

accesslog 复制代码
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 量身定制的配置文件。

传统格式

xml 复制代码
<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 复制代码
<?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)

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

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

accesslog 复制代码
DEBUG [main]: Message 1 WARN [main]: Message 2

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

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

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

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

Conversion Word Effect
c{length} lo{length} logger{length} 输出记录事件起源的记录器名称。 该转换词接受一个整数作为首个和唯一选项。转换器的缩写算法会缩短记录器名称,通常不会造成重大含义的丢失。将 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 日星期五,作者在午餐后返回处理该文档。
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} 将显示以下摘录: less 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} 将显示以下另一个摘录: less 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} 将显示以下摘录: less 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} 将按如下方式输出:
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 : 打印完整的堆栈跟踪 * 任意整数: 打印给定行数的堆栈跟踪 以下是一些示例:
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 提出的。如果信息不确定,那么类包装数据将以波浪线 '~' 字符开头。 以下是一个示例: scss 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 不是记录器上下文的属性,那么将在系统属性中查找 keykey 没有默认值。如果省略,返回的值将是 "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} 输出与日志事件相关联的异常的堆栈跟踪(如果有)。首先输出根本原因,而不是标准的"最后的根本原因"。以下是一个样本输出(已编辑以节省空间): ini 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 转换说明符的各种格式修饰符示例。

格式修饰符 左对齐 最小宽度 最大宽度 注释
%20logger false 20 如果日志记录器名称少于 20 个字符,则左填充空格。
%-20logger true 20 如果日志记录器名称少于 20 个字符,则右填充空格。
%.30logger NA 30 如果日志记录器名称超过 30 个字符,则从开头截断。
%20.30logger false 20 30 如果日志记录器名称较短,则左填充空格。但是,如果日志记录器名称超过 30 个字符,则从开头截断。
%-20.30logger true 20 30 如果日志记录器名称较短,则右填充空格。但是,如果日志记录器名称超过 30 个字符,则从 开头 截断。
%.-30logger NA 30 如果日志记录器名称超过 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...

相关推荐
ac-er88881 小时前
如何在Flask中实现国际化和本地化
后端·python·flask
Adolf_19931 小时前
Flask-WTF的使用
后端·python·flask
林太白4 小时前
❤Node09-用户信息token认证
数据库·后端·mysql·node.js
骆晨学长5 小时前
基于Springboot的助学金管理系统设计与实现
java·spring boot·后端
蒙娜丽宁5 小时前
深入理解Go语言中的接口定义与使用
开发语言·后端·golang·go
AskHarries5 小时前
java使用ByteBuffer进行多文件合并和拆分
java·后端
不染_是非6 小时前
Django学习实战篇六(适合略有基础的新手小白学习)(从0开发项目)
后端·python·学习·django
代码对我眨眼睛6 小时前
springboot从分层到解耦
spring boot·后端
The Straggling Crow6 小时前
go 战略
开发语言·后端·golang
ai安歌7 小时前
【JavaWeb】利用IDEA2024+tomcat10配置web6.0版本搭建JavaWeb开发项目
java·开发语言·后端·tomcat·web·intellij idea