https://logback.qos.ch/setup.html
为了运行文档中提供的示例,您需要将以下 jar 添加到类路径中:
对于针对 Jakarta EE(jakarta.* 命名空间)的 logback 1.5.x
- logback-core-1.5.6.jar
- logback-classic-1.5.6.jar
- logback-examples-1.5.6.jar
- slf4j-api-2.0.13.jar
对于面向 Java EE(javax.* 命名空间)的 logback 1.3
- logback-core-1.3.14.jar
- logback-classic-1.3.14.jar
- logback-examples-1.3.14.jar
- slf4j-api-2.0.13.jar
logback-*.jar 文件可以从 Maven 中心的 ch.qos.logback groupId 下下载,而 slf4j-api-2.0.13.jar 可以从 org.slf4j groupId 下载。
从命令行运行
您可以使用以下命令启动第一个示例应用程序chapters.introduction.HelloWord1。假设您的当前目录是 $LOGBACK_HOME/logback-examples,其中 $LOGBACK_HOME 代表安装 logback 的目录:
java -cp lib/slf4j-api-2.0.13.jar;../logback-core-1.5.6.jar;\ ../logback-classic-1.5.6.jar;logback-examples-1.5.6.jar\ chapters.introduction.HelloWorld1
在运行示例之前一次性设置 CLASSPATH 环境变量会更方便。
位于 $LOGBACK_HOME/logback-examples 文件夹中的 setClasspath.cmd 脚本将配置 MS Windows 平台的类路径。对于 Unix,您可以使用 setClasspath.sh。
请编辑脚本以使 LB_HOME 变量适应您的本地环境。
请注意,许多示例将启动 Java 类以及配置文件。要使用文档中编写的相同命令访问这些文件,您需要从 $LOGBACK_HOME/logback-examples 目录中发出命令。
Maven 依赖声明
要在 Maven 项目中使用 logback-classic,请在项目的 pom 文件中声明以下依赖项。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
请注意,除了 logback-classic.jar 之外,上述声明还会根据 Maven 的传递性规则自动将 slf4j-api.jar 和 logback-core.jar 拉入您的项目中。
要在 Maven 项目中包含 logback-access,请在项目的 pom 文件中声明以下依赖项。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.5.6</version>
</dependency>
可选依赖项
SMTPAppender 需要 JavaMail API
SMTPAppender 相关示例需要 JavaMail API 1.4 或更高版本。下载 JavaMail 后,您需要将 mail.jar 放在类路径中。
为了方便您使用,这里给出了相应的Maven依赖声明。
<!-- The javax.activation:activation:1.1 dependency will be -->
<!-- automatically pulled in by Maven's transitivity rules -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.6.2</version>
</dependency>
条件处理和 JaninoEventEvaluator 需要 Janino 库
配置文件中的条件处理需要 Janino 库。此外,基于 JaninoEventEvaluator 的评估器示例也需要 Janino。下载 Janino 后,只需将 commons-compiler.jar 和 janino.jar 放在应用程序的类路径中即可。
不要忘记从 Janino 版本 2.6.0 开始,除了 janino.jar 之外,commons-compiler.jar 也需要位于类路径中。
为了方便您使用,这里给出了相应的Maven依赖声明。
<!-- The org.codehaus.janino:commons-compiler:3.1.8 dependency -->
<!-- will be automatically pulled in by Maven's transitivity rules -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.1.8</version>
</dependency>
构建日志记录
从版本 1.5.x 开始,logback 需要 Java 21 来构建,但在运行时以 Java 11 或更高版本为目标。
如果您希望为该项目做出贡献或只是为了好玩而进行黑客攻击,您可能希望将 logback 作为项目导入到您最喜欢的 IDE 中。 Logback 使用 Maven 作为其构建工具。
运行时唯一强制的 logback 依赖项是 JRE,另请注意,从命令行构建相当简单,从 $LOGBACK_HOME 文件夹给出的命令“mvn install”就足够了。
寻求帮助 尽管有下面的说明,如果您在从源代码构建 logback 时遇到困难,只需在 logback-dev 邮件列表上寻求帮助即可。
https://logback.qos.ch/manual/introduction.html
第一步
为了运行本章中的示例,您需要确保类路径中存在某些 jar 文件。请参阅设置页面了解更多详细信息。
要求
Logback-classic 模块除了 logback-classic.jar 之外,还要求类路径上存在 slf4j-api.jar 和 logback-core.jar。
logback-*.jar 文件是 logback 发行版的一部分,而 slf4j-api-2.0.13.jar 随 SLF4J(一个单独的项目)一起提供。
现在让我们开始尝试 logback。
示例 1.1:日志记录的基本模板 ( logback-examples/src/main/java/chapters/introduction/HelloWorld1.java)
package chapters.introduction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld1 {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
logger.debug("Hello world.");
}
}
HelloWorld1 类定义在 chapters.introduction 包中。它首先导入 SLF4J API 中定义的 Logger 和 LoggerFactory 类,特别是在 org.slf4j 包中。
在 main() 方法的第一行,名为 logger 的变量被分配了一个 Logger 实例,该实例是通过从 LoggerFactory 方法来检索的。 类。该记录器名为“chapters.introduction.HelloWorld1”。 main 方法继续调用此记录器的 debug 方法,并传递“Hello World”作为参数。我们说 main 方法包含一条 DEBUG 级别的日志语句,其中包含消息“Hello world”。
请注意,上面的示例没有引用任何 logback 类。在大多数情况下,就日志记录而言,您的类只需要导入 SLF4J 类。
因此,绝大多数(如果不是全部)类将使用 SLF4J API,并且不会注意到 logback 的存在。
您可以使用以下命令启动第一个示例应用程序chapters.introduction.HelloWorld1:
java chapters.introduction.HelloWorld1
Logback 可以使用内置状态系统报告有关其内部状态的信息。 logback 生命周期内发生的重要事件可以通过名为 StatusManager 的组件来访问。现在,让我们通过调用 StatusPrinter 类的静态 print() 方法来指示 logback 打印其内部状态。
示例:打印记录器状态 ( logback-examples/src/main/java/chapters/introduction/HelloWorld2.java)
package chapters.introduction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
public class HelloWorld2 {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");
logger.debug("Hello world.");
// print internal state
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
}
}
运行 HelloWorld2 应用程序将产生以下输出:
12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
12:49:22,076 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:49:22,078 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Setting up default configuration.
Logback 解释说,由于找不到 logback-test.xml 和 logback.xml 配置文件(稍后讨论),它使用默认策略(基本的 ConsoleAppender )进行自身配置。 Appender 是一个可以视为输出目标的类。 Appender 存在于许多不同的目的地,包括控制台、文件、系统日志、TCP 套接字、JMS 等等。用户还可以根据自己的具体情况轻松创建自己的 Appender。
第 2 章:架构
Logback的架构
Logback的基本架构足够通用,可以适用于不同的情况。目前,logback分为三个模块,logback-core、logback-classic和logback-access。
核心模块为其他两个模块奠定了基础。经典模块扩展了核心。经典模块对应于 log4j 的显着改进版本。 Logback-classic 原生实现了 SLF4J API,因此您可以轻松地在 logback 和其他日志记录系统(例如 JDK 1.4 中引入的 log4j 或 java.util.logging (JUL))之间来回切换。第三个模块称为 access,它与 Servlet 容器集成以提供 HTTP 访问日志功能。一个单独的文档涵盖了访问模块文档。
记录器、附加器和布局
Logback 基于三个主要类: Logger 、 Appender 和 Layout 。这三种类型的组件协同工作,使开发人员能够根据消息类型和级别记录消息,并在运行时控制这些消息的格式和报告位置。
Logger 类是 logback-classic 模块的一部分。另一方面, Appender 和 Layout 接口是 logback-core 的一部分。作为一个通用模块,logback-core 没有记录器的概念。
记录器上下文
与普通 System.out.println 相比,任何日志记录 API 的首要优势在于它能够禁用某些日志语句,同时允许其他日志语句不受阻碍地打印。此功能假设日志记录空间(即所有可能的日志记录语句的空间)根据开发人员选择的某些标准进行分类。
在 logback-classic 中,这种分类是记录器的固有部分。每个记录器都附加到一个 LoggerContext ,它负责制造记录器并将它们排列在树状层次结构中。
记录器是命名实体。它们的名称区分大小写,并遵循分层命名规则:
命名层次结构
如果一个记录器的名称后跟一个点是后代记录器名称的前缀,则称该记录器是另一个记录器的祖先。如果记录器与后代记录器之间没有祖先,则称该记录器是子记录器的父记录器。
例如,名为 "com.foo" 的记录器是名为 "com.foo.Bar" 的记录器的父级。类似地, "java" 是 "java.util" 的父级,也是 "java.util.Vector" 的祖先。大多数开发人员应该熟悉这种命名方案。
根记录器位于记录器层次结构的顶部。它的特殊之处在于它从一开始就是每个等级制度的一部分。与每个记录器一样,可以通过名称检索它,如下所示:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
所有其他记录器也可以使用 org.slf4j.LoggerFactory 类中的类静态 getLogger 方法进行检索。此方法将所需记录器的名称作为参数。下面列出了 Logger 接口中的一些基本方法。
package org.slf4j;
public interface Logger {
// Printing methods:
public void trace(String message);
public void debug(String message);
public void info(String message);
public void warn(String message);
public void error(String message);
}
有效级别又名级别继承
记录器可以被分配级别。可能的级别集(TRACE、DEBUG、INFO、WARN 和 ERROR)在 ch.qos.logback.classic.Level 类中定义。请注意,在 logback 中, Level 类是最终类,不能进行子类化,因为 Marker 对象形式存在更灵活的方法。
如果给定的记录器没有分配级别,那么它会从其最接近的祖先那里继承一个已分配级别的级别。更正式地说:
给定记录器 L 的有效级别等于其层次结构中的第一个非空级别,从 L 本身开始,并在层次结构中向上向根记录器前进。
为了确保所有记录器最终都能继承级别,根记录器始终具有指定的级别。默认情况下,该级别为 DEBUG。
下面是四个示例,其中具有各种分配的级别值以及根据级别继承规则生成的有效(继承)级别。
实施例1
| 记录器名称 |
指定级别 |
有效水平 |
| 根 |
DEBUG |
DEBUG |
| X |
没有任何 |
DEBUG |
| X.Y |
没有任何 |
DEBUG |
| X.Y.Z |
没有任何 |
DEBUG |
在上面的示例 1 中,仅为根记录器分配了级别。此级别值 DEBUG 由其他记录器 X 、 X.Y 和 X.Y.Z 继承
实施例2
| 记录器名称 |
指定级别 |
有效水平 |
| 根 |
ERROR |
ERROR |
| X |
INFO |
INFO |
| X.Y |
DEBUG |
DEBUG |
| X.Y.Z |
WARN |
WARN |
在上面的示例 2 中,所有记录器都有指定的级别值。级别继承不起作用。
实施例3
| 记录器名称 |
指定级别 |
有效水平 |
| 根 |
DEBUG |
DEBUG |
| X |
INFO |
INFO |
| X.Y |
没有任何 |
INFO |
| X.Y.Z |
ERROR |
ERROR |
在上面的示例 3 中,记录器 root 、 X 和 X.Y.Z 被分配级别 DEBUG 、 INFO 和分别为 ERROR 。 Logger X.Y 从其父级 X 继承其级别值。
实施例4
| 记录器名称 |
指定级别 |
有效水平 |
| 根 |
DEBUG |
DEBUG |
| X |
INFO |
INFO |
| X.Y |
没有任何 |
INFO |
| X.Y.Z |
没有任何 |
INFO |
在上面的示例 4 中,记录器 root 和 X 分别被分配级别 DEBUG 和 INFO 。记录器 X.Y 和 X.Y.Z 从最近的父级 X 继承其级别值,该父级具有指定的级别。
印刷方式及基本选择规则
根据定义,打印方法决定日志记录请求的级别。例如,如果 L 是记录器实例,则语句 L.info("..") 是 INFO 级别的日志记录语句。
如果日志请求的级别高于或等于其记录器的有效级别,则称该日志请求已启用。否则,该请求被认为是被禁用的。如前所述,没有分配级别的记录器将从其最近的祖先继承级别。该规则总结如下。
基本选择规则
如果 p >= q,则启用向有效级别 q 的记录器发出级别 p 的日志请求。
这条规则是 logback 的核心。它假设级别的顺序如下: TRACE < DEBUG < INFO < WARN < ERROR 。
以更形象的方式,以下是选择规则的工作原理。在下表中,垂直标头显示记录请求的级别,用 p 指定,而水平标头显示记录器的有效级别,用 q 指定。行(级别请求)和列(有效级别)的交集是由基本选择规则产生的布尔值。
水平 请求 p |
有效水平q |
| TRACE |
DEBUG |
INFO |
WARN |
ERROR |
OFF |
| TRACE |
YES |
NO |
NO |
NO |
NO |
NO |
| DEBUG |
YES |
YES |
NO |
NO |
NO |
NO |
| INFO |
YES |
YES |
YES |
NO |
NO |
NO |
| WARN |
YES |
YES |
YES |
YES |
NO |
NO |
| ERROR |
YES |
YES |
YES |
YES |
YES |
NO |
这是基本选择规则的示例。
import ch.qos.logback.classic.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
....
// get a logger instance named "com.foo". Let us further assume that the
// logger is of type ch.qos.logback.classic.Logger so that we can
// set its level
ch.qos.logback.classic.Logger logger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.foo");
//set its Level to INFO. The setLevel() method requires a logback logger
logger.setLevel(Level. INFO);
Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");
// This request is enabled, because WARN >= INFO
logger.warn("Low fuel level.");
// This request is disabled, because DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO.
barlogger.info("Located nearest gas station.");
// This request is disabled, because DEBUG < INFO.
barlogger.debug("Exiting gas station search");
检索记录器
使用相同名称调用 LoggerFactory.getLogger 方法将始终返回对完全相同的记录器对象的引用。
例如,在
Logger x = LoggerFactory.getLogger("wombat");
Logger y = LoggerFactory.getLogger("wombat");
x 和 y 引用完全相同的记录器对象。
因此,可以配置记录器,然后在代码中的其他位置检索相同的实例,而无需传递引用。
与亲生父母(父母总是在孩子之前)的根本矛盾是,logback 记录器可以按任何顺序创建和配置。特别是,“父”记录器将找到并链接到其后代,即使它是在它们之后实例化的。
logback 环境的配置通常在应用程序初始化时完成。首选方法是读取配置文件。我们将很快讨论这种方法。
Logback 可以轻松地通过软件组件命名记录器。这可以通过在每个类中实例化一个记录器来完成,记录器名称等于类的完全限定名称。这是定义记录器的一种有用且简单的方法。
由于日志输出带有生成记录器的名称,因此这种命名策略可以轻松识别日志消息的来源。然而,这只是一种可能的(尽管很常见)命名记录器的策略。 Logback 不限制可能的记录器集。
作为开发人员,您可以随意命名记录器。
尽管如此,按照记录器所在的类来命名记录器似乎是迄今为止已知的最佳通用策略。
追加器和布局
根据记录器有选择地启用或禁用记录请求的能力只是其中的一部分。 Logback 允许将日志记录请求打印到多个目的地。在 logback 中,输出目标称为追加器。
目前,控制台、文件、远程套接字服务器、MySQL、PostgreSQL、Oracle 和其他数据库、JMS 和远程 UNIX Syslog 守护进程都存在附加程序。
一个记录器可以附加多个附加器。
addAppender 方法将附加程序添加到给定的记录器。给定记录器的每个启用的日志记录请求都将转发到该记录器中的所有附加程序以及层次结构中更高的附加程序。
换句话说,追加器是从记录器层次结构中附加继承的。例如,如果将控制台附加程序添加到根记录器,则所有启用的日志记录请求至少将打印在控制台上。
如果另外将文件附加程序添加到记录器(例如 L),则 L 和 L 的子级的启用日志记录请求将打印在文件和控制台上。通过将记录器的可加性标志设置为 false,可以覆盖此默认行为,以便附加程序累积不再是可加的。
管理附加器可加性的规则总结如下。
附加器可加性
记录器 L 的日志语句的输出将发送到 L 及其祖先中的所有附加程序。这就是术语“appender相加性”的含义。
但是,如果记录器 L 的祖先(例如 P)将可加性标志设置为 false,则 L 的输出将定向到 L 及其祖先中的所有附加程序,直到并包括 P,但不会定向到任何祖先中的附加程序P。
默认情况下,记录器的可加性标志设置为 true。
下表显示了一个示例:
| 记录器名称 |
附加附加程序 |
可加性标志 |
输出目标 |
评论 |
| 根 |
A1 |
不适用 |
A1 |
由于根记录器位于记录器层次结构的顶部,因此可加性标志不适用于它。 |
| x |
A-x1、A-x2 |
真的 |
A1、A-x1、A-x2 |
“x”和根的追加者。 |
| x.y |
没有任何 |
真的 |
A1、A-x1、A-x2 |
“x”和根的追加者。 |
| 坐标 |
A-xyz1 |
真的 |
A1、A-x1、A-x2、A-xyz1 |
“x.y.z”、“x”和根的追加者。 |
| 安全 |
一秒 |
错误的 |
一秒 |
由于可加性标志设置为 false ,因此没有附加器累积。仅使用附加器 A-sec。 |
| 安全访问 |
没有任何 |
真的 |
一秒 |
仅“security”的附加程序,因为“security”中的可加性标志设置为 false 。 |
通常,用户不仅希望自定义输出目的地,还希望自定义输出格式。这是通过将布局与附加程序关联来完成的。布局负责根据用户的意愿格式化日志请求,而附加程序负责将格式化的输出发送到其目的地。 PatternLayout 是标准 logback 发行版的一部分,允许用户根据类似于 C 语言 printf 函数的转换模式指定输出格式。
例如,具有转换模式“%-4relative [%thread] %-5level %logger{32} - %msg%n”的 PatternLayout 将输出类似于:
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
第一个字段是自程序启动以来经过的毫秒数。
第二个字段是发出日志请求的线程。第三个字段是日志请求的级别。第四个字段是与日志请求关联的记录器的名称。 “-”之后的文本是请求的消息。
参数化日志记录
鉴于 logback-classic 中的记录器实现了 SLF4J 的 Logger 接口,某些打印方法允许多个参数。这些打印方法变体主要是为了提高性能,同时最大限度地减少对代码可读性的影响。
对于某些 Logger logger ,写入,
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
会产生构造消息参数的成本,即将整数 i 和 entry[i] 转换为字符串,并连接中间字符串。这与消息是否被记录无关。
避免参数构造成本的一种可能方法是通过测试包围日志语句。这是一个例子。
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
这样,如果 logger 禁用调试,您将不会产生参数构造的成本。另一方面,如果为 DEBUG 级别启用了记录器,您将需要两次评估记录器是否启用的成本:一次在 debugEnabled 中,一次在 debug .实际上,这种开销是微不足道的,因为评估记录器所花费的时间不到实际记录请求所需时间的 1%。
更好的选择
存在一种基于消息格式的便捷替代方案。假设 entry 是一个对象,你可以这样写:
Object entry = new SomeObject();
logger.debug("The entry is {}.", entry);
仅在评估是否记录后,并且仅当决定是肯定的时,记录器实现才会格式化消息并将“{}”对替换为 entry 的字符串值。换句话说,这种形式在禁用日志语句时不会产生参数构造的成本。
以下两行将产生完全相同的输出。但是,在禁用日志记录语句的情况下,第二个变体的性能将比第一个变体至少高出 30 倍。
logger.debug("The new entry is "+entry+".");
logger.debug("The new entry is {}.", entry);
还可以使用两个参数的变体。例如,你可以写:
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
如果需要传递三个或更多参数,还可以使用 varargs (Object...) 变体。例如,你可以写:
logger.debug("Value {} was inserted between {} and {}.", newVal, below, above);
请注意,varags 变体会产生创建 Object[] 实例的成本。
一睹引擎盖下的风采
在介绍了基本的 logback 组件之后,我们现在准备好描述当用户调用记录器的打印方法时 logback 框架所采取的步骤。现在让我们分析一下当用户调用名为 com.wombat 的记录器的 info() 方法时 logback 执行的步骤。
1. 获取过滤器链决策
如果存在,则调用 TurboFilter 链。 Turbo 过滤器可以设置上下文范围的阈值,或根据 Marker 、 Level 、 Logger 、消息或 Throwable 与每个日志记录请求相关联。如果过滤器链的回复是 FilterReply.DENY ,则日志请求将被丢弃。如果是 FilterReply.NEUTRAL ,那么我们继续下一步,即步骤 2。如果回复是 FilterReply.ACCEPT ,我们跳过下一步,直接跳到步骤 3。
2. 应用基本选择规则
在这一步,logback将记录器的有效级别与请求的级别进行比较。如果根据此测试禁用日志记录请求,则 logback 将丢弃该请求而不进行进一步处理。否则,继续下一步。
3.创建 LoggingEvent 对象
如果请求在前面的过滤器中幸存下来,logback 将创建一个 ch.qos.logback.classic.LoggingEvent 对象,其中包含请求的所有相关参数,例如请求的记录器、请求级别、消息本身、可能发生的异常与请求一起传递,当前时间,当前线程,有关发出日志记录请求的类的各种数据和 MDC 。请注意,其中一些字段是延迟初始化的,即仅在实际需要时才初始化。 MDC 用于用附加上下文信息来修饰日志请求。 MDC 将在后续章节中讨论。
4. 调用appender
创建 LoggingEvent 对象后,logback 将调用所有适用的 Appender(即从 logger 上下文继承的 Appender)的 doAppend() 方法。
logback 发行版附带的所有附加程序都扩展了 AppenderBase 抽象类,该类在同步块中实现 doAppend 方法,确保线程安全。 AppenderBase 的 doAppend() 方法还会调用附加到附加程序的自定义过滤器(如果存在任何此类过滤器)。自定义过滤器可以动态附加到任何附加程序,在单独的章节中介绍。
5. 格式化输出
被调用的附加程序负责格式化日志记录事件。但是,一些(但不是全部)附加程序将格式化日志记录事件的任务委托给布局。布局格式化 LoggingEvent 实例并将结果作为字符串返回。请注意,某些附加程序(例如 SocketAppender )不会将日志记录事件转换为字符串,而是将其序列化。因此,它们没有也不需要布局。
6. 发送 LoggingEvent
日志记录事件完全格式化后,每个附加程序会将其发送到其目的地。
这是一个序列 UML 图,展示了一切是如何工作的。您可能需要单击图像以显示其更大的版本。

表现
经常被引用的反对日志记录的论点之一是其计算成本。这是一个合理的担忧,因为即使是中等大小的应用程序也可能生成数千个日志请求。我们的大部分开发工作都花在了测量和调整 logback 的性能上。
除了这些努力之外,用户仍然应该注意以下性能问题。
1.完全关闭日志记录时的日志性能
您可以通过将根记录器的级别设置为 Level.OFF (可能的最高级别)来完全关闭日志记录。当日志记录完全关闭时,日志请求的成本包括方法调用加上整数比较。在 3.2Ghz Pentium D 机器上,此成本通常约为 20 纳秒。
然而,任何方法调用都涉及参数构造的“隐藏”成本。例如,对于一些 logger x 的写法,
x.debug("Entry number: " + i + "is " + entry[i]);
会产生构造消息参数的成本,即将整数 i 和 entry[i] 转换为字符串,并连接中间字符串,无论消息是否会被记录。
参数构造的成本可能相当高,并且取决于所涉及参数的大小。为了避免参数构造的成本,您可以利用 SLF4J 的参数化日志记录:
x.debug("Entry number: {} is {}", i, entry[i]);
该变体不会产生参数构造的成本。与之前调用 debug() 方法相比,速度会大大加快。仅当日志请求发送到附加的附加程序时,消息才会被格式化。此外,格式化消息的组件也经过了高度优化。
尽管上面将日志语句放置在紧密循环中,即非常频繁调用的代码,但这是一个双输的建议,可能会导致性能下降。
即使日志记录已关闭,紧密循环中的日志记录也会减慢应用程序的速度,如果日志记录打开,则会生成大量(因此无用的)输出。
2、开启日志记录时决定是否记录日志的性能。
在 logback 中,无需遍历记录器层次结构。记录器在创建时就知道其有效级别(即,一旦考虑级别继承,其级别)。
如果父记录器的级别发生更改,则会联系所有子记录器以注意更改。
因此,在根据有效级别接受或拒绝请求之前,记录器可以做出准瞬时的决定,而无需咨询其祖先。
3.实际记录(格式化并写入输出设备)
这是格式化日志输出并将其发送到目标目的地的成本。在这里,我们再次做出了认真的努力,以使布局(格式化程序)尽快执行。对于附加器来说也是如此。
当记录到本地计算机上的文件时,实际记录的典型成本约为 9 到 12 微秒。当登录到远程服务器上的数据库时,它会长达几毫秒。
尽管功能丰富,但 logback 最重要的设计目标之一是执行速度,这是仅次于可靠性的要求。一些 logback 组件已被重写多次以提高性能。
第3章:Logback配置
将日志请求插入到应用程序代码中需要大量的规划和工作。观察表明,大约百分之四的代码专用于日志记录。
因此,即使是中等大小的应用程序也会在其代码中包含数千条日志记录语句。考虑到它们的数量,我们需要工具来管理这些日志语句。
Logback 可以通过编程方式或使用以 XML、Groovy 或序列化模型格式表示的配置脚本进行配置。顺便说一下,现有的 log4j 用户可以使用我们的 PropertiesTranslator Web 应用程序将其 log4j.properties 文件转换为 logback.xml。
让我们首先讨论 logback 尝试配置自身所遵循的初始化步骤:
Logback 将使用服务提供者加载工具搜索任何自定义 Configurator 提供者。如果找到任何此类自定义提供程序,它将优先于 logback 自己的配置程序,例如 DefaultJoranConfigurator (见下文)。
自定义 Configurator 是 ch.qos.logback.classic.spi.Configurator 接口的实现。通过查找位于 META-INF/services/ch.qos.logback.classic.spi.Configurator 下的文件资源来搜索自定义配置器。该文件的内容应指定所需 Configurator 实现的完全限定类名。
从 1.3.9/1.4.9 开始,如果在上一步中未找到用户提供的自定义 Configurator ,logback 将实例化 SerializedModelConfigurator。
• 如果设置了系统属性“logback.scmoFile”,则 SerializedModelConfigurator 将尝试查找指定上述系统属性的文件。如果可以找到指定的文件,则将读取并解释该文件以进行配置。
• 如果未设置上述系统属性或找不到指定文件, SerializedModelConfigurator 将在类路径中搜索序列化配置模型文件logback-test.scmo。如果可以找到该文件,则会读取并解释该文件以进行配置。
• 如果找不到上述文件, SerializedModelConfigurator 将在类路径中搜索序列化配置模型文件logback.scmo。
• 如果找不到序列化配置模型文件, SerializedModelConfigurator 将返回一个执行状态,要求调用下一个可用配置器,即 DefaultJoranConfigurator 。
序列化模型文件的配置执行速度更快,并且不需要任何 XML 库。与 GraalVM 结合使用,这可能会产生更小的可执行文件,启动速度更快。
标准步骤 如果以前的配置器无法找到所需的资源,则将创建并调用 DefaultJoranConfigurator 的实例。
• 如果设置了系统属性“logback.configurationFile”,则 DefaultJoranConfigurator 将尝试查找指定上述系统属性的文件。如果可以找到该文件,则会读取并解释该文件以进行配置。
• 如果上一步失败, DefaultJoranConfigurator 将尝试在类路径上查找配置文件“logback-test.xml”。如果可以找到该文件,则会读取并解释该文件以进行配置。
• 如果没有找到这样的文件,它将尝试在类路径中查找配置文件“logback.xml”。如果可以找到该文件,则会读取并解释该文件以进行配置。请注意,这是名义上的配置步骤。
• 如果找不到配置文件, DefaultJoranConfigurator 将返回一个执行状态,要求调用下一个可用的配置器,即 BasicConfigurator 。
如果上述操作均不成功,logback-classic 将使用 BasicConfigurator 进行自身配置,这将导致日志输出定向到控制台。
最后一步是在没有配置文件的情况下提供默认(但非常基本)日志记录功能的最后努力。
GROOVY 鉴于 Groovy 是一种成熟的语言,为了保护无辜者,我们放弃了对 logback.groovy 的支持。然而,Tucker Pelletier 在 virtualdogbert/logback-groovy-config 中获得了 groovy 支持。
如果您使用的是符合 Maven 文件夹结构的构建工具,那么如果您将 logback-test.xml 放在 src/test/resources 文件夹下,Maven 将确保它不会包含在生成的工件中。因此,您可以在测试期间使用不同的配置文件(即 logback-test.xml),并在生产中使用另一个文件(即 logback.xml)。
快速启动 Joran 解析给定的 logback 配置文件大约需要 100 毫秒。为了减少应用程序启动时的毫秒数,您可以使用服务提供者加载工具(上面的第 1 项)来加载您自己的自定义 Configurator 类,并以 BasicConfigurator 作为良好的起点。
将 XML 文件转换为序列化模型格式是缩短配置时间的快速便捷的方法。
Automatically configuring logback
配置 logback 最简单的方法是让 logback 回退到其默认配置。让我们体验一下如何在名为 MyApp1 的虚构应用程序中完成此操作。
示例:简单示例
BasicConfigurator 用法(logback-examples/src/main/java/chapters/configuration/MyApp1.java)
package chapters.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyApp1 {
final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
public static void main(String[] args) {
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
此类定义了一个静态记录器变量。然后它实例化一个 Foo 对象。下面列出了 Foo 类:
示例:小班进行日志记录
(logback-examples/src/main/java/chapters/configuration/Foo.java)
package chapters.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
static final Logger logger = LoggerFactory.getLogger(Foo.class);
public void doIt() {
logger.debug("Did it again!");
}
}
为了运行本章中的示例,您需要确保类路径中存在某些 jar 文件。请参阅设置页面了解更多详细信息。
假设配置文件 logback-test.xml 或 logback.xml 不存在,logback 将默认调用 BasicConfigurator 这将设置最小配置。此最小配置由附加到根记录器的 ConsoleAppender 组成。输出使用设置为模式 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n 的 PatternLayoutEncoder 进行格式化。此外,默认情况下,根记录器被分配 DEBUG 级别。
因此,命令 java Chapters.configuration.MyApp1 的输出应类似于:
16:06:09.031 [main] INFO Chapters.configuration.MyApp1 -- 进入应用程序。
16:06:09.046 [main] DEBUG Chapters.configuration.Foo -- 又做了一次!
16:06:09.046 [main] INFO Chapters.configuration.MyApp1 -- 退出应用程序。
除了配置logback的代码(如果存在这样的代码)客户端代码不需要依赖logback。使用 logback 作为日志记录框架的应用程序将具有对 SLF4J 的编译时依赖,但不依赖于 logback。
MyApp1 应用程序通过调用 org.slf4j.LoggerFactory 和 org.slf4j.Logger 类链接到 logback,检索它希望使用的记录器,然后继续运行。请注意, Foo 类对 logback 的唯一依赖项是通过 org.slf4j.LoggerFactory 和 org.slf4j.Logger 导入。除了配置logback的代码(如果存在这样的代码)客户端代码不需要依赖logback。
由于 SLF4J 允许在其抽象层下使用任何日志框架,因此可以轻松地将大量代码从一个日志框架迁移到另一个日志框架。
使用 logback-test.xml 或 logback.xml 进行配置
如前所述,logback 将尝试使用文件 logback-test.xml 或 logback.xml(如果在类路径中找到)来配置自身。这是一个与我们刚刚看到的 BasicConfigurator 建立的配置文件等效的配置文件。
示例:基本配置文件
(logback-examples/src/main/resources/chapters/configuration/sample0.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
将sample0.xml 重命名为logback.xml(或logback-test.xml)后,将其放入可从类路径访问的目录中。运行 MyApp1 应用程序应该给出与之前运行相同的结果。
Automatic printing of status messages in case of warning or errors
如果在解析配置文件的过程中出现警告或错误,logback将自动在控制台上打印其内部状态消息。
如果在解析配置文件的过程中出现警告或错误,logback会自动在控制台上打印其内部状态数据。
请注意,为了避免重复,如果用户显式注册状态侦听器(如下所述),则会禁用自动状态打印。
在没有警告或错误的情况下,如果您仍然希望检查 logback 的内部状态,那么您可以通过调用 StatusPrinter 类的 print() 来指示 logback 打印状态数据。下面显示的 MyApp2 应用程序与 MyApp1 相同,只是添加了两行用于打印内部状态数据的代码。
示例:打印logback的内部状态
信息(logback-examples/src/main/java/chapters/configuration/MyApp2.java)
public static void main(String[] args) {
// assume SLF4J is bound to logback in the current environment
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
// print logback's internal status
StatusPrinter.print(lc);
...
}
如果一切顺利,您应该在控制台上看到以下输出
17:44:58,578 |-ch.qos.logback.classic 中的信息。LoggerContext[默认] - 找到资源 [logback-test.xml]
17:44:58,671 |-ch.qos.logback.classic.joran.action.ConfigurationAction 中的信息 - 未设置调试属性
17:44:58,671 |-ch.qos.logback.core.joran.action.AppenderAction 中的信息 - 将要实例化 [ch.qos.logback.core.ConsoleAppender] 类型的附加程序
17:44:58,687 |-ch.qos.logback.core.joran.action.AppenderAction 中的信息 - 将附加程序命名为 [STDOUT]
17:44:58,812 |-ch.qos.logback.core.joran.action.AppenderAction 中的信息 - 从对象堆栈中弹出名为 [STDOUT] 的附加程序
17:44:58,812 | ch.qos.logback.classic.joran.action.LevelAction 中的-INFO - 根级别设置为 DEBUG
17:44:58,812 |-ch.qos.logback.core.joran.action.AppenderRefAction 中的信息 - 将名为 [STDOUT] 的附加程序附加到 Logger[root]
17:44:58.828 [main] INFO Chapters.configuration.MyApp2 -- 进入应用程序。
17:44:58.828 [main] DEBUG Chapters.configuration.Foo -- 又做了一遍!
17:44:58.828 [main] INFO Chapters.configuration.MyApp2 -- 退出应用程序。
在此输出的末尾,您可以识别上一个示例中打印的行。您还应该注意到 logback 的内部消息,即 Status 对象,它允许方便地访问 logback 的内部状态。
Status data
启用状态数据的输出通常对诊断 logback 问题有很大帮助。
启用状态数据的输出通常对诊断 logback 问题有很大帮助。请注意,错误也可能在配置后发生,例如当磁盘已满或日志文件由于权限错误而无法归档时。
因此,强烈建议您注册 StatusListener ,如下所述。
下一个示例说明了 OnConsoleStatusListener 的安装。
示例:注册状态监听器
(logback-examples/src/main/resources/chapters/configuration/onConsoleStatusListener.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<!-- Recommendation: place status listeners towards the the top of the configuration file -->
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
<!-- ... the rest of the configuration file -->
</configuration>
可以使用配置文件安装 StatusListener ,假设:
找到配置文件
配置文件是格式良好的 XML。
如果这两个条件中的任何一个不满足,Joran 就无法解释配置文件,特别是 <statusListener/> 元素。
如果找到配置文件但格式错误,则 logback 将检测错误情况并自动在控制台上打印其内部状态。
但是,如果找不到配置文件,logback 将不会自动打印其状态数据,因为这不一定是错误情况。如上面的 MyApp2 应用程序所示,以编程方式调用 StatusPrinter.print() 可确保在每种情况下都打印状态信息。
强制状态输出 在没有状态消息的情况下,跟踪恶意 logback.xml 配置文件可能很困难,特别是在应用程序源无法轻松修改的生产环境中。为了帮助识别恶意配置文件的位置,您可以通过“logback.statusListenerClass”系统属性(如下所述)设置 StatusListener 以强制输出状态消息。 “logback.statusListenerClass”系统属性还可用于在出现错误时使自动生成的输出静音。
Shorthand
作为简写,可以通过在 元素中将调试属性设置为 true 来注册 OnConsoleStatusListener ,如下所示。
示例:使用 debug 的基本配置文件
模式
(logback-examples/src/main/resources/chapters/configuration/sample1.xml)
Legacy 规范 (1.3)Tyler
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are by default assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
顺便说一下,设置 debug="true" 严格等同于安装 OnConsoleStatusListener ,如前面所示。
"logback.statusListenerClass" system property
还可以通过将“logback.statusListenerClass”Java 系统属性设置为您希望注册的侦听器类的名称来注册状态侦听器。例如,
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
作为简写,字符串“ch.qos.logback.core.status.OnConsoleStatusListener”可以替换为不区分大小写的字符串“STDOUT”或“SYSOUT”。例如:
java -Dlogback.statusListenerClass=stdout ...
或等价地:
java -Dlogback.statusListenerClass=sysout ...
其他 StatusListener 实现
Logback 附带了多个状态侦听器实现。 OnConsoleStatusListener 在控制台(即 System.out)上打印传入的状态消息。 OnErrorConsoleStatusListener 在 System.err 上打印传入的状态消息。 NopStatusListener 会丢弃传入的状态消息。
请注意,如果在配置期间注册了任何状态侦听器,特别是如果用户通过“logback.statusListenerClass”系统指定了状态侦听器,则自动状态打印(如果出现错误)将被禁用。因此,通过将 NopStatusListener 设置为状态侦听器,您可以完全静音内部状态打印。
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...
Viewing status messages
Logback 在 StatusManager 对象中收集其内部状态数据,可通过 LoggerContext 访问。
给定 StatusManager 您可以访问与 logback 上下文关联的所有状态数据。为了将内存使用保持在合理的水平,默认的 StatusManager 实现将状态消息存储在两个单独的部分中:标头部分和尾部部分。头部分存储前 H 状态消息,而尾部分存储最后 T 消息。目前 H=T=150,尽管这些值在未来版本中可能会发生变化。
Logback-classic 附带了一个名为 ViewStatusMessagesServlet 的 servlet。此 Servlet 将与当前 LoggerContext 关联的 StatusManager 的内容打印为 HTML 表。这是示例输出。
要将此 servlet 添加到您的 Web 应用程序,请将以下行添加到其 WEB-INF/web.xml 文件中。
<servlet>
<servlet-name>ViewStatusMessages</servlet-name>
<servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ViewStatusMessages</servlet-name>
<url-pattern>/lbClassicStatus</url-pattern>
</servlet-mapping>
ViewStatusMessages servlet 可在 URL http://host/yourWebapp/lbClassicStatus 处查看
Listening to status messages via code
您还可以通过 Java 代码注册 StatusListener 。这是示例代码。
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusManager statusManager = lc.getStatusManager();
OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
statusManager.add(onConsoleListener);
请注意,已注册的状态侦听器将仅接收其注册后的状态事件。它不会接收之前的消息。
因此,通常最好将状态侦听器注册指令放置在配置文件顶部的其他指令之前。
Setting the location of the configuration file via a system property
您可以使用名为 "logback.configurationFile" 的系统属性指定默认配置文件的位置。该属性的值可以是 URL、类路径上的资源或应用程序外部文件的路径。
java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
请注意,文件扩展名必须是“.xml”或“.groovy”。其他扩展名将被忽略。显式注册状态侦听器可能有助于调试定位配置文件的问题。
鉴于 "logback.configurationFile" 是 Java 系统属性,它也可以在您的应用程序中设置。但是,必须在创建任何记录器实例之前设置系统属性。
import ch.qos.logback.classic.util.ContextInitializer;
public class ServerMain {
public static void main(String args[]) throws IOException, InterruptedException {
// must be set before the first call to LoggerFactory.getLogger();
// ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, "/path/to/config.xml");
...
}
}
Automatically reloading configuration file upon modification
Logback-classic 可以扫描其配置文件中的更改,并在配置文件更改时自动重新配置自身。
如果指示这样做,logback-classic 将扫描其配置文件中的更改,并在配置文件更改时自动重新配置自身。
为了指示 logback-classic 扫描其配置文件中的更改并自动重新配置自身,请将 <configuration> 元素的 scan 属性设置为 true,如下所示。
示例:扫描配置更改
文件和自动重新配置
(logback-examples/src/main/resources/chapters/configuration/scan1.xml)
<configuration scan="true">
...
</configuration>
默认情况下,每分钟都会扫描一次配置文件是否有更改。您可以通过设置 <configuration> 元素的scanPeriod属性来指定不同的扫描周期。值可以以毫秒、秒、分钟或小时为单位指定。这是一个例子:
示例:指定不同的扫描周期
(logback-examples/src/main/resources/chapters/configuration/scan2.xml)
<configuration scan="true" scanPeriod="30 seconds" >
...
</configuration>
注: 如果没有指定时间单位,则假定时间单位为毫秒,这通常是不合适的。如果更改默认扫描周期,请不要忘记指定时间单位。
在幕后,当您将扫描属性设置为 true 时,将安装 ReconfigureOnChangeTask 。此任务在单独的线程中运行,并将检查您的配置文件是否已更改。 ReconfigureOnChangeTask 也会自动监视任何包含的文件。
由于编辑配置文件时很容易出错,如果最新版本的配置文件有XML语法错误,它将回退到之前没有XML语法错误的配置文件。
Enabling packaging data in stack traces
虽然有用,但打包数据的计算成本很高,尤其是在经常出现异常的应用程序中。
注意 从版本 1.1.4 开始,默认情况下禁用打包数据。
如果指示这样做,logback 可以包含其输出的堆栈跟踪行的每一行的打包数据。打包数据由堆栈跟踪行的类源自的 jar 文件的名称和版本组成。
打包数据对于识别软件版本控制问题非常有用。然而,它的计算成本相当高,特别是在频繁抛出异常的应用程序中。这是一个示例输出:
14:28:48.835 [btpool0-7] INFO c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]
打包数据默认是禁用的,但可以通过配置启用:
<configuration packagingData="true">
...
</configuration>
或者,可以通过调用 LoggerContext 中的 setPackagingDataEnabled(boolean) 方法以编程方式启用/禁用打包数据,如下所示:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);
Invoking JoranConfigurator directly
Logback 依赖于一个名为 Joran 的配置库,它是 logback-core 的一部分。 Logback 的默认配置机制在类路径中找到的默认配置文件上调用 JoranConfigurator 。如果您出于某种原因希望覆盖 logback 的默认配置机制,可以通过直接调用 JoranConfigurator 来实现。下一个应用程序 MyApp3 对作为参数传递的配置文件调用 JoranConfigurator。
示例:调用 JoranConfigurator
直接(logback-examples/src/main/java/chapters/configuration/MyApp3.java)
package chapters.configuration;
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 MyApp3 {
final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
public static void main(String[] args) {
// assume SLF4J is bound to logback in the current environment
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
// Call context.reset() to clear any previous configuration, e.g. default
// configuration. For multi-step configuration, omit calling context.reset().
context.reset();
configurator.doConfigure(args[0]);
} catch (JoranException je) {
// StatusPrinter will handle this
}
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
此应用程序获取当前有效的 LoggerContext ,创建一个新的 JoranConfigurator ,设置它将操作的上下文,重置记录器上下文,然后最后要求配置器配置上下文使用作为参数传递给应用程序的配置文件。
如果出现警告或错误,则会打印内部状态数据。请注意,对于多步骤配置,应省略 context.reset() 调用。
Stopping logback-classic
为了释放 logback-classic 使用的资源,停止 logback 上下文始终是一个好主意。停止上下文将关闭附加到上下文定义的记录器的所有附加程序,并以有序的方式停止任何活动线程。
另请阅读下面有关“关闭挂钩”的部分。
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
// assume SLF4J is bound to logback-classic in the current environment
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
在 Web 应用程序中,可以从 ServletContextListener 的 contextDestroyed 方法中调用上述代码,以停止 logback-classic 并释放资源。从版本 1.1.10 开始,系统会自动为您安装相应的 ServletContextListener (请参见下文)。
Stopping logback-classic via a shutdown hook
安装 JVM 关闭钩子是关闭 logback 并释放相关资源的便捷方法。
Legacy 规范 (1.3)Tyler
<configuration debug="true">
<!-- in the absence of the class attribute, assume
ch.qos.logback.core.hook.DefaultShutdownHook -->
<shutdownHook/>
<!-- rest of the config file.. -->
</configuration>
请注意,您可以通过将类属性设置为与关闭挂钩的类名相对应来安装自己制作的关闭挂钩。
默认关闭钩子,即 DefaultShutdownHook,将在指定的延迟(默认为 0)后停止 logback 上下文。停止上下文将允许在后台运行的任何日志文件压缩任务最多需要 30 秒才能完成。在独立的 Java 应用程序中,向配置文件添加 <shutdownHook/> 指令是确保允许在 JVM 退出之前完成任何正在进行的压缩任务的简单方法。在 Web 服务器内的应用程序中,webShutdownHook 将自动安装,从而使 <shutdownHook/> 指令变得非常多余且不必要。
WebShutdownHook or stopping logback-classic in web-applications
自 1.1.10 起,Logback-classic 将自动要求 Web 服务器安装实现 ServletContainerInitializer 接口的 LogbackServletContainerInitializer (在 servlet-api 3.x 及更高版本中提供)。该初始化程序将依次安装 LogbackServletContextListener 的实例。当 Web 应用程序停止或重新加载时,此侦听器将停止当前的 logback-classic 上下文。
您可以通过在 Web 应用程序的 web.xml 文件中设置名为 logbackDisableServletContainerInitializer 的 来禁用 LogbackServletContextListener 的自动安装。这是相关的片段。
<web-app>
<context-param>
<param-name>logbackDisableServletContainerInitializer</param-name>
<param-value>true</param-value>
</context-param>
....
</web-app>
请注意, logbackDisableServletContainerInitializer 变量也可以设置为 Java 系统属性或操作系统环境变量。最本地的设置具有优先权,即首先是 Web 应用程序,其次是系统属性,最后是操作系统环境。
Configuration file syntax
正如您在手册中所看到的,还有大量示例可供参考,logback 允许您重新定义日志记录行为,而无需重新编译代码。
事实上,您可以轻松配置 logback,以便禁用应用程序某些部分的日志记录,或者直接输出到 UNIX Syslog 守护进程、数据库、日志可视化工具,或者将日志记录事件转发到远程 logback 服务器,该服务器将记录日志根据本地服务器策略,例如将日志事件转发到第二个 logback 服务器。
本节的其余部分介绍配置文件的语法。
正如将要反复演示的那样,logback 配置文件的语法非常灵活。因此,无法使用 DTD 文件或 XML 模式指定允许的语法。
尽管如此,配置文件的基本结构可以描述为 <configuration> 元素,包含零个或多个 <appender> 元素,后跟零个或多个 <logger> 元素,后跟至多一个 <root> 元素。下图说明了这个基本结构。
如果您不确定给定标签名称使用哪种大小写,只需遵循驼峰命名约定,这几乎总是正确的约定。
Case sensitivity of tag names
从 logback 版本 0.9.17 开始,与显式规则相关的标记名称不区分大小写。例如, <logger> 、 <Logger> 和 <LOGGER> 是有效的配置元素,将以相同的方式解释。请注意,XML 格式良好规则仍然适用,如果您以 <xyz> 形式打开标记,则必须以 </xyz> 形式将其关闭, </XyZ> 将不起作用。对于隐式规则,标记名称除第一个字母外均区分大小写。因此, <xyz> 和 <Xyz> 等效,但 <xYz> 不同。隐式规则通常遵循 Java 世界中常见的驼峰命名法约定。
由于很难判断标签何时与显式操作关联以及何时与隐式操作关联,因此判断 XML 标签对首字母是否区分大小写并非易事。
如果您不确定给定标签名称使用哪种大小写,只需遵循驼峰命名约定,这几乎总是正确的约定。
Configuring loggers, or the <logger> element
至此,您应该至少对级别继承和基本选择规则有一些了解。否则,除非您是埃及学家,否则 logback 配置对您来说并不比象形文字更有意义。
使用 <logger> 元素配置记录器。 <logger> 元素仅采用一个强制名称属性、一个可选级别属性和一个可选附加属性,并接受 true 或 false 值。 level 属性的值接受不区分大小写的字符串值 TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF 之一。不区分大小写的特殊值 INHERITED 或其同义词 NULL 将强制从层次结构中的更高层继承记录器的级别。如果您设置记录器的级别并稍后决定它应该继承其级别,这会派上用场。
请注意,与 log4j 不同,logback-classic 在配置给定记录器时不会关闭或删除任何先前引用的附加程序。
<logger> 元素可以包含零个或多个 <appender-ref> 元素;这样引用的每个附加程序都会添加到指定的记录器中。请注意,与 log4j 不同,logback-classic 在配置给定记录器时不会关闭或删除任何先前引用的附加程序。
Configuring the root logger, or the <root> element
<root> 元素配置根记录器。它支持单一属性,即level属性。它不允许任何其他属性,因为可加性标志不适用于根记录器。此外,由于根记录器已被命名为“ROOT”,因此它也不允许使用名称属性。
level 属性的值可以是不区分大小写的字符串 TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF 之一。请注意,根记录器的级别不能设置为 INHERITED 或 NULL。
请注意,与 log4j 不同,logback-classic 在配置根记录器时不会关闭或删除任何先前引用的附加程序。
与 <logger> 元素类似, <root> 元素可以包含零个或多个 <appender-ref> 元素;这样引用的每个附加程序都会添加到根记录器中。请注意,与 log4j 不同,logback-classic 在配置根记录器时不会关闭或删除任何先前引用的附加程序。
例子
设置记录器或根记录器的级别就像声明它并设置其级别一样简单,如下一个示例所示。假设我们不再有兴趣查看来自属于“chapters.configuration”包的任何组件的任何 DEBUG 消息。
以下配置文件显示了如何实现这一点。
示例:设置记录器的级别
(logback-examples/src/main/resources/chapters/configuration/sample2.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO"/>
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
当上述配置文件作为 MyApp3 应用程序的参数给出时,它将产生以下输出:
17:34:07.578 [main] INFO chapters.configuration.MyApp3 -- Entering application.
17:34:07.578 [main] INFO chapters.configuration.MyApp3 -- Exiting application.
请注意,“chapters.configuration.Foo”记录器生成的 DEBUG 级别消息已被抑制。另请参见 Foo 类。
您可以根据需要配置任意数量的记录器的级别。在下一个配置文件中,我们将chapters.configuration记录器的级别设置为INFO,但同时将chapters.configuration.Foo记录器的级别设置为 DEBUG 。
示例:设置多个记录器的级别
(logback-examples/src/main/resources/chapters/configuration/sample3.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO" />
<logger name="chapters.configuration.Foo" level="DEBUG" />
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
使用此配置文件运行 MyApp3 将在控制台上产生以下输出:
17:39:27.593 [main] INFO chapters.configuration.MyApp3 -- Entering application.
17:39:27.593 [main] DEBUG chapters.configuration.Foo -- Did it again!
17:39:27.593 [main] INFO chapters.configuration.MyApp3 -- Exiting application.
下表列出了 JoranConfigurator 使用sample3.xml配置文件配置logback后的记录器及其级别。
记录器名称 指定级别 有效水平
根 DEBUG DEBUG
章节.配置 INFO INFO
chapters.configuration.MyApp3 null INFO
chapters.configuration.Foo DEBUG DEBUG
由此可见, MyApp3 类中的两条 INFO 级别的日志语句以及 Foo.doIt() 中的DEBUG消息均已启用。请注意,根记录器的级别始终设置为非空值,默认为 DEBUG。
让我们注意,基本选择规则取决于被调用的记录器的有效级别,而不是附加附加程序的记录器的级别。
Logback 将首先确定日志记录语句是否启用,如果启用,它将调用记录器层次结构中找到的附加程序,无论其级别如何。配置文件sample4.xml就是一个很好的例子:
示例:记录器级别示例
(logback-examples/src/main/resources/chapters/configuration/sample4.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO" />
<!-- turn OFF all logging (children can override) -->
<root level="OFF">
<appender-ref ref="STDOUT" />
</root>
</configuration>
下表列出了应用sample4.xml 配置文件后的记录器及其级别。
记录器名称 指定级别 有效水平
根 OFF OFF
章节.配置 INFO INFO
chapters.configuration.MyApp3 null INFO
chapters.configuration.Foo null INFO
名为 STDOUT 的 ConsoleAppender(sample4.xml 中唯一配置的附加程序)附加到级别设置为 OFF 的根记录器。但是,使用配置脚本sample4.xml运行MyApp3将产生:
17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.
因此,根记录器的级别没有明显的影响,因为 chapters.configuration.MyApp3 和 chapters.configuration.Foo 类中的记录器都针对 INFO 级别启用。附带说明一下,chapters.configuration 记录器凭借其在配置文件中的声明而存在 - 即使 Java 源代码不直接引用它。
Configuring Appenders
附加程序由 <appender> 元素配置,该元素采用两个强制属性 name 和 class。 name 属性指定appender 的名称,而class 属性指定要实例化的appender 类的完全限定名称。 <appender> 元素可以包含零个或一个 <layout> 元素、零个或多个 <encoder> 元素以及零个或多个 <filter> 元素。除了这三个公共元素之外, <appender> 元素还可以包含与appender 类的JavaBean 属性相对应的任意数量的元素。无缝支持给定 logback 组件的任何属性是 Joran 的主要优势之一,这将在后面的章节中讨论。下图说明了常见的结构。请注意,下图中未显示对属性的支持。
<layout> 元素采用强制类属性,指定要实例化的布局类的完全限定名称。与 <appender> 元素一样, <layout> 可以包含与布局实例的属性相对应的其他元素。由于这种情况很常见,因此如果布局类是 PatternLayout ,则可以按照默认类映射规则的指定省略 class 属性。
<encoder> 元素采用强制类属性,指定要实例化的编码器类的完全限定名称。由于这种情况很常见,因此如果编码器类是 PatternLayoutEncoder ,则可以按照默认类映射规则的指定省略 class 属性。
记录到多个附加程序就像定义各种附加程序并在记录器中引用它们一样简单,如下一个配置文件所示:
示例:多个记录器
(logback-examples/src/main/resources/chapters/configuration/multiple.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
这些配置脚本定义了两个名为 FILE 和 STDOUT 的附加程序。 FILE 附加程序将日志记录到名为 myApp.log 的文件中。此附加程序的编码器是 PatternLayoutEncoder ,它输出日期、级别、线程名称、记录器名称、文件名和日志请求所在的行号、消息和行分隔符。第二个名为 STDOUT 的附加程序输出到控制台。此附加器的编码器仅输出消息字符串,后跟行分隔符。
通过在appender-ref 元素中按名称引用附加程序,将附加程序附加到根记录器。请注意,每个附加程序都有自己的编码器。编码器通常不设计为由多个附加程序共享。布局也是如此。因此,logback 配置文件不提供任何共享编码器或布局的语法方法。
Appenders accumulate
默认情况下,附加程序是累积的:记录器将记录附加到其自身的附加程序(如果有)以及附加到其祖先的所有附加程序。因此,将相同的附加程序附加到多个记录器将导致日志记录输出重复。
示例:重复的附加程序
(logback-examples/src/main/resources/chapters/configuration/duplicate.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration">
<appender-ref ref="STDOUT" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
使用duplicate.xml运行 MyApp3 将产生以下输出:
14:25:36.343 [main] INFO Chapters.configuration.MyApp3 -- 进入应用程序。
14:25:36.343 [main] INFO Chapters.configuration.MyApp3 -- 进入应用程序。
14:25:36.359 [main] DEBUG Chapters.configuration.Foo -- 又做了一次!
14:25:36.359 [main] DEBUG Chapters.configuration.Foo -- 又做了一次!
14:25:36.359 [main] INFO Chapters.configuration.MyApp3 -- 退出应用程序。
14:25:36.359 [main] INFO Chapters.configuration.MyApp3 -- 退出应用程序。
注意重复的输出。名为 STDOUT 的附加程序附加到两个记录器:根记录器和chapters.configuration。由于根记录器是所有记录器的祖先,而chapters.configuration是chapters.configuration.MyApp3和chapters.configuration.Foo的父级,因此使用这两个记录器发出的每个日志请求将输出两次,一次是因为附加了STDOUT到 Chapters.configuration 并一次,因为它附加到根目录。
Appender 的可添加性无意成为新用户的陷阱。这是一个相当方便的 logback 功能。
例如,您可以配置日志记录,使日志消息显示在控制台上(对于系统中的所有记录器),而仅来自某些特定记录器集的消息流入特定附加程序。
示例:多个附加程序
(logback-examples/src/main/resources/chapters/configuration/restricted.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
在此示例中,控制台附加程序将记录所有消息(对于系统中的所有记录器),而只有来自chapters.configuration记录器及其子项的记录请求才会进入myApp.log文件。
Overriding the default cumulative behaviour
如果默认的累积行为不适合您的需求,您可以通过将可加性标志设置为 false 来覆盖它。因此,记录器树中的分支可能会将输出定向到与树的其余部分不同的一组附加程序。
示例:可加性标志
(logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>foo.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file : %line] -%kvp- %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration.Foo" additivity="false">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
在此示例中,名为 FILE 的附加程序附加到chapters.configuration.Foo 记录器。此外,chapters.configuration.Foo 记录器的可加性标志设置为 false,以便其日志记录输出将发送到名为 FILE 的附加程序,但不会发送到层次结构中附加的任何附加程序。其他记录器仍然不知道chapters.configuration.Foo记录器的可加性设置。使用 additivityFlag.xml 配置文件运行 MyApp3 应用程序将从chapters.configuration.MyApp3 记录器在控制台上输出结果。但是,chapters.configuration.Foo 记录器的输出将出现在 foo.log 文件中,并且仅出现在该文件中。
Setting the context name
正如前面的章节中提到的,每个记录器都附加到一个记录器上下文。默认情况下,记录器上下文称为“默认”。但是,您可以借助 <contextName> 配置指令设置不同的名称。请注意,一旦设置,记录器上下文名称就无法更改。设置上下文名称是一种简单直接的方法,可以区分登录到同一目标的多个应用程序。
示例:设置上下文名称并显示
(logback-examples/src/main/resources/chapters/configuration/contextName.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<contextName>myAppName</contextName>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %contextName [%t] %level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
最后一个示例说明了记录器上下文的命名。在布局模式中添加 contextName 转换词将输出所述名称。
Variable substitution
注:本文档的早期版本使用术语“属性替换”而不是术语“变量”。请考虑这两个术语可以互换,尽管后一个术语传达了更清晰的含义。
与许多脚本语言一样,logback 配置文件支持变量的定义和替换。变量有一个范围(见下文)。此外,变量可以在配置文件本身、外部文件、外部资源中定义,甚至可以动态计算和定义。
变量替换可以发生在配置文件中可以指定值的任何位置。
变量替换可以发生在配置文件中可以指定值的任何位置。变量替换的语法与 Unix shell 类似。开头 ${ 和结尾 } 之间的字符串被解释为对属性值的引用。对于属性 aName,字符串“${aName}”将替换为 aName 属性所保存的值。
由于 HOSTNAME 和 CONTEXT_NAME 变量经常派上用场,因此它们会自动定义并具有上下文范围。鉴于在某些环境中计算主机名可能需要一些时间,因此它的值是延迟计算的(仅在需要时)。
此外,HOSTNAME 可以直接在配置中设置。
Defining variables
变量可以在配置文件本身中一次定义一个,也可以从外部属性文件或外部资源批量加载。由于历史原因,用于定义变量的 XML 元素是 <property> ,尽管在 logback 1.0.7 及更高版本中元素 <variable> 可以互换使用。
下一个示例显示在配置文件开头声明的变量。然后在文件中进一步使用它来指定输出文件的位置。
示例:简单变量替换
(logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<variable name="USER_HOME" value="/home/sebastien" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
下一个示例显示如何使用系统属性来实现相同的结果。该属性未在配置文件中声明,因此 logback 将在系统属性中查找它。可以在命令行上设置 Java 系统属性,如下所示:
java -DUSER_HOME="/home/sebastien" MyApp2
示例:系统变量替换
(logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
当需要多个变量时,创建一个包含所有变量的单独文件可能会更方便。以下是如何进行这样的设置。
示例:使用 a 进行变量替换
单独的文件
(logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<variable file="src/main/java/chapters/configuration/variables1.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
该配置文件包含对名为variables1.properties 的文件的引用。该文件中包含的变量将被读取,然后在本地范围内定义。这是variable.properties 文件的样子。
示例:变量文件(logback-examples/src/main/resources/chapters/configuration/variables1.properties)
USER_HOME=/home/sebastien
您还可以引用类路径上的资源而不是文件。
Legacy 规范 (1.3)Tyler
<configuration>
<variable resource="resource1.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${USER_HOME}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
Scopes
可以定义属性以插入本地范围、上下文范围或系统范围。本地范围是默认范围。尽管可以从操作系统环境读取变量,但无法写入操作系统环境。
本地范围 具有本地范围的属性从其在配置文件中的定义点开始一直存在到所述配置文件的解释/执行结束。
作为推论,每次解析和执行配置文件时,都会重新定义本地范围内的变量。
上下文范围 具有上下文范围的属性会插入到上下文中,并持续与上下文一样长或直到它被清除为止。一旦定义,上下文范围内的属性就是上下文的一部分。
因此,它可用于所有日志记录事件,包括通过序列化发送到远程主机的日志记录事件。
系统范围 具有系统范围的属性被插入到 JVM 的系统属性中,并且与 JVM 一样持续或直到被清除为止。
首先在本地范围中查找属性,其次在上下文范围中查找,第三在系统属性范围中查找,最后在操作系统环境中查找。
在替换期间,首先在本地范围中查找属性,其次在上下文范围中查找,第三次在系统属性范围中查找,第四次和最后一次在操作系统环境中查找。
<property> 元素、 <define> 元素或 <insertFromJNDI> 元素的scope 属性可用于设置属性的范围。范围属性允许“本地”、“上下文”和“系统”字符串作为可能的值。如果未指定,则始终假定范围是“本地”。
示例:在“上下文”范围内定义的变量
(logback-examples/src/main/resources/chapters/configuration/contextScopedVariable.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<variable scope="context" name="nodeId" value="firstNode" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/opt/${nodeId}/myApp.log</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
在上面的示例中,假设 nodeId 属性是在上下文范围中定义的,它将在每个日志记录事件中可用,甚至是通过序列化发送到远程主机的事件。
Default values for variables
在某些情况下,如果变量未声明或其值为 null,则可能希望变量具有默认值。与 Bash shell 中一样,可以使用“:-”运算符指定默认值。例如,假设未定义名为 aName 的变量, "${aName:-golden}" 将被解释为“golden”。
Nested variables
完全支持变量嵌套。变量的名称、默认值和值定义都可以引用其他变量。
值嵌套
变量的值定义可以包含对其他变量的引用。假设您希望使用变量不仅指定目标目录,还指定文件名,并将这两个变量组合在名为“destination”的第三个变量中。
下面显示的属性文件给出了一个示例。
示例:嵌套变量引用
(logback-examples/src/main/resources/chapters/configuration/variables2.properties)
USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}
请注意,在上面的属性文件中,“destination”由另外两个变量组成,即“USER_HOME”和“fileName”。
示例:使用单独文件进行变量替换(logback-examples/src/main/resources/chapters/configuration/variableSubstitution4.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<variable file="variables2.properties" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${destination}</file>
<encoder>
<pattern>%kvp %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
名称嵌套
当引用一个变量时,变量名可能包含对另一个变量的引用。例如,如果为名为“userid”的变量分配了值“alice”,则“${${userid}.password}”引用名为“alice.password”的变量。
默认值嵌套
一个变量的默认值可以引用另一个变量。例如,假设变量“id”未分配,并且变量“userid”被分配值“alice”,则表达式“${id:-${userid}}”将返回“alice”。
HOSTNAME property
由于 HOSTNAME 属性经常派上用场,因此它是在配置上下文范围时自动定义的。
CONTEXT_NAME property
正如其名称所示, CONTEXT_NAME 属性对应于当前日志记录上下文的名称。
Setting a timestamp
时间戳元素可以根据当前日期和时间定义属性。时间戳元素将在后续章节中解释。
Defining variables, aka properties, on the fly
您可以使用 <define> 元素动态定义属性。 Define 元素采用两个强制属性:名称和类。 name 属性指定要设置的属性的名称,而 class 属性指定实现 PropertyDefiner 接口的任何类。 PropertyDefiner 实例的 getPropertyValue () 方法返回的值将是指定属性的值。您还可以通过指定范围属性来指定命名属性的范围。
这是一个例子。
Legacy 规范 (1.3)Tyler
<configuration>
<define name="rootLevel" class="a.class.implementing.PropertyDefiner">
<shape>round</shape>
<color>brown</color>
<size>24</size>
</define>
<root level="${rootLevel}"/>
</configuration>
在上面的示例中,形状、颜色和大小是“a.class.implementing.PropertyDefiner”的属性。只要 PropertyDefiner 实例的实现中有给定属性的 setter,logback 就会注入配置文件中指定的适当值。
目前,logback 附带了一些相当简单的 PropertyDefiner 实现。
实施名称 描述
CanonicalHostNamePropertyDefiner 将命名变量设置为本地主机的规范主机名。请注意,获取规范主机名可能需要几秒钟的时间。
FileExistsPropertyDefiner 如果路径属性指定的文件存在,则将命名变量设置为“true”,否则设置为“false”。
ResourceExistsPropertyDefiner 如果用户指定的资源在类路径上可用,则将命名变量设置为“true”,否则设置为“false”。
Conditional processing of configuration files
开发人员经常需要在针对不同环境(例如开发、测试和生产)的多个 logback 配置文件之间切换。这些配置文件有很多共同点,只有少数地方不同。
为了避免重复,logback 在 <if> 、 <then> 和 <else> 元素的帮助下支持配置文件的条件处理,以便单个配置文件可以充分针对多个环境。请注意,条件处理需要 Janino 库。
条件语句的一般格式如下所示。
<!-- if-then form -->
<if condition="some conditional expression">
<then>
...
</then>
</if>
<!-- if-then-else form -->
<if condition="some conditional expression">
<then>
...
</then>
<else>
...
</else>
</if>
条件是一个 Java 表达式,其中只能访问上下文属性或系统属性。对于作为参数传递的键, property () 或其较短的等效 p () 方法返回属性的 String 值。例如,要访问带有键“k”的属性的值,您可以编写 property("k") 或等效的 p("k") 。如果键为“k”的属性未定义,则属性方法将返回空字符串而不是 null。这避免了检查空值的需要。
isDefined() 方法可用于检查属性是否已定义。例如,要检查属性“k”是否已定义,您可以编写 isDefined("k") 类似地,如果您需要检查属性是否为 null,则提供 isNull() 方法。示例: isNull("k") 。
Legacy 规范 (1.3)Tyler
<configuration debug="true">
<if condition='property("HOSTNAME").contains("torino")'>
<then>
<appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="CON" />
</root>
</then>
</if>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${randomOutputDir}/conditional.log</file>
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="ERROR">
<appender-ref ref="FILE" />
</root>
</configuration>
<configuration> 元素内的任何位置都支持条件处理。还支持嵌套的 if-then-else 语句。然而,XML 语法非常麻烦,不适合作为通用编程语言的基础。
因此,太多的条件很快就会导致后续读者(包括您自己)无法理解您的配置文件。
Obtaining variables from JNDI
在某些情况下,您可能希望使用 JNDI 中存储的环境条目。 <insertFromJNDI> 配置指令提取存储在 JNDI 中的 env-entry,并使用 as 属性指定的键将属性插入到本地范围中。与所有属性一样,可以借助 range 属性将新属性插入到不同的范围中。
示例:作为属性插入 env-entries
通过 JNDI 获取
(logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<insertFromJNDI env-entry-name="java:comp/env/appName" as="appName" />
<contextName>${appName}</contextName>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d ${CONTEXT_NAME} %level -%kvp- %msg %logger{50}%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
在最后一个示例中,“java:comp/env/appName”env 条目作为 appName 属性插入。请注意, <contextName> 指令根据先前 <insertFromJNDI> 指令插入的 appName 属性的值来设置上下文名称。
Serialize model
SINCE 1.3.9/1.4.9 对于1.3.9/1.4.9版本,logback-classic可以创建与XML配置文件匹配的配置模型的序列化版本。
这是通过添加 <serializeModel> 元素并指定序列化模型文件的路径来实现的。
<configuration debug="false">
<serializeModel file="path/to/logback.scmo"/>
...
</configuration>
请注意, SerializedModelConfigurator 的实例是在 logback-classic 初始化期间创建和调用的。 SerializedModelConfigurator 实例可以从序列化配置模型 (.scmo) 文件中读取和配置 logback-classic。请参阅前面有关初始化时配置的部分。
File inclusion
Joran 支持包含另一个文件中的部分配置文件。这是通过声明 <include> 元素来完成的,如下所示:
示例:文件包含
(logback-examples/src/main/resources/chapters/configuration/containingConfig.xml)
Legacy 规范 (1.3)Tyler
<configuration>
<include file="src/main/java/chapters/configuration/includedConfig.xml"/>
<root level="DEBUG">
<appender-ref ref="includedConsole" />
</root>
</configuration>
目标文件必须将其元素嵌套在 <included> 元素内。例如, ConsoleAppender 可以声明为:
示例:文件包含
(logback-examples/src/main/resources/chapters/configuration/includedConfig.xml)
<included>
<appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>"%d -%kvp- %m%n"</pattern>
</encoder>
</appender>
</included>
再次请注意强制性的 <included> 元素。
要包含的内容可以作为文件、资源或 URL 来引用。
作为文件:
要包含文件,请使用文件属性。您可以使用相对路径,但请注意,当前目录是由应用程序定义的,不一定与配置文件的路径相关。
作为资源:
要包含资源(即在类路径上找到的文件),请使用资源属性。
<include resource="includedConfig.xml"/>
作为网址:
要包含 URL 的内容,请使用 url 属性。
<include url="http://some.host.com/includedConfig.xml"/>
如果找不到要包含的文件,logback 将通过打印状态消息来抱怨。如果包含的文件是可选的,您可以通过在 <include> 元素中将可选属性设置为 true 来抑制警告消息。
<include optional="true" ..../>
Adding a context listener
LoggerContextListener 接口的实例侦听与记录器上下文生命周期相关的事件。
JMXConfigurator 是 LoggerContextListener 接口的一种实现。后续章节将对此进行描述。
LevelChangePropagator
从版本 0.9.25 开始,logback-classic 附带了 LevelChangePropagator,它是 LoggerContextListener 的实现,它将对任何 logback-classic 记录器级别所做的更改传播到 java.util.logging 框架。这种传播消除了禁用日志语句对性能的影响。 LogRecord 的实例将仅针对启用的日志语句发送到 logback(通过 SLF4J)。这使得现实应用程序可以合理地使用 jul-to-slf4j 桥接器。
contextListener 元素可用于安装 LevelChangePropagator ,如下所示。
Legacy 规范 (1.3)Tyler
<configuration debug="true">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
<!-- rest of the configuration file .... -->
</configuration>
设置 LevelChangePropagator 的 resetJUL 属性将重置所有 j.u.l. 的所有先前级别配置。伐木工。但是,之前安装的处理程序将保持不变。
Legacy 规范 (1.3)Tyler
<configuration debug="true">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<!-- rest of the configuration file .... -->
</configuration>
SequenceNumberGenerator
SINCE 1.3.0Logback 支持序列号字段,该字段在事件创建时自动填充。该字段由附加到日志记录上下文的序列号生成器提供。
序列号生成器设置如下:
Legacy 规范 (1.3)Tyler
<configuration>
<sequenceNumberGenerator class="ch.qos.logback.core.spi.BasicSequenceNumberGenerator"/>
<!-- rest of the configuration file .... -->
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
第 4 章:追加器
Appender 接口中的大多数方法都是 setter 和 getter。一个值得注意的例外是 doAppend() 方法将类型 E 的对象实例作为其唯一参数。 E 的实际类型将根据 logback 模块而有所不同。在 logback-classic 模块中,E 的类型为 ILoggingEvent,在 logback-access 模块中,E 的类型为 AccessEvent。 doAppend() 方法可能是 logback 框架中最重要的方法。它负责以合适的格式将日志记录事件输出到合适的输出设备。
Appender 是命名实体。这确保了可以通过名称引用它们,这一质量被证实在配置脚本中很有用。 Appender 接口扩展了 FilterAttachable 接口。由此可见,一个或多个过滤器可以附加到一个追加器实例。过滤器将在后续章节中详细讨论。
Appender 最终负责输出日志事件。但是,他们可以将事件的实际格式委托给 Layout 或 Encoder 对象。每个布局/编码器都与一个且仅有一个附加程序相关联,称为所属附加程序。一些附加程序具有内置或固定的事件格式。因此,它们不需要也没有布局/编码器。例如, SocketAppender 只是在通过线路传输日志记录事件之前将其序列化。
追加器库
ch.qos.logback.core.AppenderBase 类是实现 Appender 接口的抽象类。它提供了所有附加程序共享的基本功能,例如获取或设置其名称、激活状态、布局和过滤器的方法。它是 logback 附带的所有附加程序的超类。尽管 AppenderBase 是一个抽象类,但它实际上实现了 Append 接口中的 doAppend() 方法。也许讨论 AppenderBase 类的最清晰的方法是提供实际源代码的摘录。
public synchronized void doAppend(E eventObject) {
// prevent re-entry.
if (guard) {
return;
}
try {
guard = true;
if (!this.started) {
if (statusRepeatCount++ < ALLOWED_REPEATS) {
addStatus(new WarnStatus(
"Attempted to append to non started appender [" + name + "].",this));
}
return;
}
if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
return;
}
// ok, we now invoke the derived class's implementation of append
this.append(eventObject);
} finally {
guard = false;
}
}
doAppend() 方法的此实现是同步的。由此可见,从不同线程记录到同一个附加程序是安全的。当线程(例如 T)正在执行 doAppend() 方法时,其他线程的后续调用将排队,直到 T 离开 doAppend() 方法,从而确保 T 对附加程序的独占访问。
由于这种同步并不总是合适,logback 附带了 ch.qos.logback.core.UnsynchronizedAppenderBase ,它与 AppenderBase 类非常相似。为了简洁起见,我们将在本文档的其余部分讨论 UnsynchronizedAppenderBase 。
doAppend() 方法所做的第一件事是检查守卫是否设置为 true。如果是,则立即退出。如果未设置保护,则在下一条语句中将其设置为 true。该守卫确保 doAppend() 方法不会递归调用自身。想象一下,在 append() 方法之外的某个地方调用的组件想要记录某些内容。它的调用可能会定向到刚刚调用它的同一个附加程序,从而导致无限循环和堆栈溢出。
在下面的语句中,我们检查 started 字段是否为 true。如果不是, doAppend() 将发送警告消息并返回。换句话说,一旦追加器关闭,就无法对其进行写入。 Appender 对象实现 LifeCycle 接口,这意味着它们实现 start() 、 stop() 和 isStarted() 方法。设置appender的所有属性后,logback的配置框架Joran调用 start() 方法来通知appender激活其属性。根据其类型,如果缺少某些属性或由于各种属性之间的干扰,附加程序可能无法启动。例如,假设文件创建取决于截断模式,则在确定追加选项的值之前, FileAppender 无法对其 File 选项的值进行操作。显式激活步骤确保附加程序在其属性值已知后对其属性进行操作。
如果appender无法启动或已停止,则会通过logback的内部状态管理系统发出警告消息。经过多次尝试后,为了避免内部状态系统被相同警告消息的副本淹没, doAppend() 方法将停止发出这些警告。
下一个 if 语句检查附加过滤器的结果。根据过滤器链产生的决策,事件可以被拒绝或明确接受。在过滤器链没有做出决定的情况下,默认情况下会接受事件。
然后, doAppend() 方法调用派生类的 append() 方法的实现。此方法执行将事件附加到适当设备的实际工作。
最后,释放防护以允许随后调用 doAppend() 方法。
对于本手册的其余部分,我们保留术语“选项”或“属性”来表示使用 JavaBeans 自省通过 setter 和 getter 方法动态推断的任何属性。
Logback核心
Logback-core 为构建其他 logback 模块奠定了基础。一般来说,logback-core 中的组件需要一些(尽管是最小的)定制。然而,在接下来的几节中,我们将描述几个开箱即用的附加程序。
输出流附加器
OutputStreamAppender 将事件附加到 java.io.OutputStream 。此类提供其他附加程序构建的基本服务。用户通常不会直接实例化 OutputStreamAppender 对象,因为通常 java.io.OutputStream 类型无法方便地映射到字符串,因为无法指定目标 OutputStream 配置脚本中的对象。简而言之,您无法从配置文件中配置 OutputStreamAppender 。但是,这并不意味着 OutputStreamAppender 缺少可配置属性。接下来描述这些属性。
物业名称 类型 描述
编码器 Encoder 确定将事件写入底层 OutputStreamAppender 的方式。编码器在专门的章节中进行了描述。
立即刷新 boolean instantFlush 的默认值为“true”。立即刷新输出流可确保日志记录事件立即写出,并且在应用程序在未正确关闭附加程序的情况下退出时不会丢失。
另一方面,将此属性设置为“false”可能会使记录吞吐量增加四倍(您的里程可能会有所不同)。同样,如果立即刷新设置为“假”并且应用程序退出时附加程序未正确关闭,则尚未写入磁盘的日志记录事件可能会丢失。
OutputStreamAppender 是其他三个附加程序的超类,即 ConsoleAppender 、 FileAppender ,而 FileAppender 又是 RollingFileAppender 的超类。下图说明了 OutputStreamAppender 及其子类的类图。
控制台附加程序
正如名称所示, ConsoleAppender 附加在控制台上,或者更准确地说附加在 System.out 或 System.err 上,前者是默认目标。 ConsoleAppender 在用户指定的编码器的帮助下格式化事件。编码器将在后续章节中讨论。 System.out 和 System.err 都是 java.io.PrintStream 类型。因此,它们被包装在缓冲 I/O 操作的 OutputStreamWriter 内。
物业名称 类型 描述
编码器 Encoder 请参阅 OutputStreamAppender 属性。
目标 String 字符串值 System.out 或 System.err 之一。默认目标是System.out。
和詹西 boolean 默认情况下, withJansi 属性设置为 false 。将 withJansi 设置为 true 会激活 Jansi 库,该库在 Windows 计算机上提供对 ANSI 颜色代码的支持。在 Windows 主机上,如果此属性设置为 true,则应将“org.fusesource.jansi:jansi:${jansi.version}”放在类路径上。
请注意,基于 Unix 的操作系统(例如 Linux 和 Mac OS X)默认支持 ANSI 颜色代码。
在 Eclipse IDE 下,您可能想尝试 Eclipse Console 插件中的 ANSI。
以下是使用 ConsoleAppender 的示例配置。
示例:ConsoleAppender 配置
(logback-examples/src/main/resources/chapters/appenders/conf/logback-Console.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
将当前路径设置为 logback-examples 目录并设置类路径后,您可以通过发出以下命令来尝试上述配置文件:
java Chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-Console.xml
文件附加器
FileAppender 是 OutputStreamAppender 的子类,它将日志事件附加到文件中。目标文件由“文件”选项指定。如果文件已存在,则根据附加属性的值将其追加或截断。
物业名称 类型 描述
附加 boolean 如果为 true,则事件将附加到现有文件的末尾。否则,如果追加为 false,则任何现有文件都会被截断。附加选项默认设置为 true。
编码器 Encoder 请参阅 OutputStreamAppender 属性。
文件 String 要写入的文件的名称。如果该文件不存在,则创建该文件。在 MS Windows 平台上,用户经常忘记转义反斜杠。例如,值 c:\temp\test.log 不太可能被正确解释,因为“\t”是解释为单个制表符 (\u0009) 的转义序列。正确的值可以指定为 c:/temp/test.log 或 c:\\temp\\test.log。文件选项没有默认值。
如果文件的父目录不存在, FileAppender 将自动创建它,包括任何必要但不存在的父目录。
缓冲区大小FileSize
bufferSize 选项设置输出缓冲区的大小,以防immediateFlush 选项设置为 false。 bufferSize 的默认值为 8192。即使在负载非常重且持续的情况下,256KKB 值似乎也足够了。
以“FileSize”为单位定义的选项可以通过在数值后分别添加 KB、MB 和 GB 来指定字节、千字节、兆字节或千兆字节。
例如,5000000、5000KB、5MB 和 2GB 都是有效值,前三个是等效的。
谨慎 boolean 在谨慎模式下, FileAppender 将安全地写入指定文件,即使存在在不同 JVM 中运行(可能在不同主机上运行)的其他 FileAppender 实例。谨慎模式的默认值为 false 。
尽管存在一些限制,但谨慎模式可以与 RollingFileAppender 结合使用。
谨慎模式意味着append 属性自动设置为true。
谨慎更依赖独占文件锁。实验表明,文件锁定的成本大约是写入日志事件的成本的三倍 (x3)。在写入本地硬盘上的文件的“普通”PC 上,当谨慎模式关闭时,写入单个日志事件大约需要 10 微秒。当谨慎模式打开时,输出单个日志记录事件大约需要 30 微秒。
这意味着当谨慎模式关闭时每秒记录 100'000 个事件的吞吐量,在谨慎模式下每秒记录大约 33'000 个事件。
谨慎模式有效地序列化写入同一文件的所有 JVM 之间的 I/O 操作。因此,随着竞争访问文件的 JVM 数量增加,每个 I/O 操作产生的延迟也会增加。只要 I/O 操作总数约为每秒 20 个日志请求,对性能的影响就可以忽略不计。每秒生成 100 次或更多 I/O 操作的应用程序可能会对性能产生影响,因此应避免使用谨慎模式。
网络文件锁 当日志文件位于网络文件系统上时,谨慎模式的成本甚至更大。
同样重要的是,网络文件系统上的文件锁有时可能存在强烈偏差,使得当前拥有锁的进程在释放锁后立即重新获得锁。
因此,当一个进程霸占日志文件的锁时,其他进程则因等待该锁而陷入死锁。
谨慎模式的影响高度依赖于网络速度以及操作系统实现细节。我们提供了一个名为 FileLockSimulator 的非常小的应用程序,它可以帮助您在您的环境中模拟谨慎模式的行为。
立即刷新 默认情况下,每个日志事件都会立即刷新到底层输出流。这种默认方法更安全,因为如果您的应用程序在没有正确关闭附加程序的情况下退出,则日志记录事件不会丢失。
但是,为了显着提高日志记录吞吐量,您可能需要将immediateFlush属性设置为 false 。
下面是 FileAppender 配置文件的示例:
示例:FileAppender 配置
(logback-examples/src/main/resources/chapters/appenders/conf/logback-fileAppender.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
将当前目录更改为 logback-examples 后,通过启动以下命令来运行此示例:
java Chapters.appenders.ConfigurationTester
src/main/java/chapters/appenders/conf/logback-fileAppender.xml
唯一命名的文件(按时间戳)
在应用程序开发阶段或短期应用程序的情况下,例如对于批处理应用程序,最好在每次启动新应用程序时创建一个新的日志文件。在 <timestamp> 元素的帮助下这很容易做到。这是一个例子。
示例:唯一命名的 FileAppender
按时间戳配置
(logback-examples/src/main/resources/chapters/appenders/conf/logback-timestamp.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
the key "bySecond" into the logger context. This value will be
available to all subsequent configuration elements. -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- use the previously created timestamp to create a uniquely
named log file -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
timestamp 元素采用两个强制属性 key 和 datePattern 以及一个可选的 timeReference 属性。 key 属性是键的名称,在该键下时间戳将作为变量可供后续配置元素使用。 datePattern 属性表示用于将当前时间(解析配置文件的时间)转换为字符串的日期模式。日期模式应遵循 SimpleDateFormat 中定义的约定。 timeReference 属性表示时间戳的时间参考。默认是配置文件的解释/解析时间,即当前时间。然而,在某些情况下,使用上下文出生时间作为时间参考可能会很有用。
这可以通过将 timeReference 属性设置为 "contextBirth" 来完成。
通过运行以下命令来试验 <timestamp> 元素:
java chapters.appenders.ConfigurationTester src/main/resources/chapters/appenders/conf/logback-timestamp.xml
要使用记录器上下文出生日期作为时间参考,您可以将 timeReference 属性设置为“contextBirth”,如下所示。
示例:使用上下文出生日期作为时间参考的时间戳
(logback-examples/src/main/resources/chapters/appenders/conf/logback-timestamp-contextBirth.xml)
<configuration>
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"
timeReference="contextBirth"/>
...
</configuration>
滚动文件附加器
RollingFileAppender 扩展了 FileAppender 并具有滚动更新日志文件的功能。例如, RollingFileAppender 可以记录到名为 log.txt 的文件,并且一旦满足特定条件,就将其记录目标更改为另一个文件。
有两个重要的子组件与 RollingFileAppender 交互。第一个 RollingFileAppender 子组件,即 RollingPolicy (见下文)负责执行翻转所需的操作。 RollingFileAppender 的第二个子组件,即 TriggeringPolicy (见下文)将确定是否以及何时发生翻转。因此, RollingPolicy 负责什么, TriggeringPolicy 负责何时。
要发挥作用, RollingFileAppender 必须同时设置 RollingPolicy 和 TriggeringPolicy 。但是,如果其 RollingPolicy 也实现了 TriggeringPolicy 接口,则只需显式指定前者即可。
以下是 RollingFileAppender 的可用属性:
物业名称 类型 描述
文件 String 请参阅 FileAppender 属性。请注意,文件可以是 null 在这种情况下,输出仅写入 RollingPolicy 指定的目标。
附加 boolean 请参阅 FileAppender 属性。
编码器 Encoder 请参阅 OutputStreamAppender 属性。
滚动策略 RollingPolicy 该选项将决定发生翻转时 RollingFileAppender 的行为。请参阅下面的更多信息。
触发策略 TriggeringPolicy 该选项是告诉 RollingFileAppender 何时激活翻转过程的组件。请参阅下面的更多信息。
谨慎 boolean 谨慎模式不支持 FixedWindowRollingPolicy 。
RollingFileAppender 支持与 TimeBasedRollingPolicy 结合使用的谨慎模式,尽管有两个限制。
在谨慎模式下,不支持也不允许文件压缩。 (我们不能让一个 JVM 写入文件,而另一个 JVM 正在压缩该文件。)
FileAppender 的文件属性无法设置,必须留空。事实上,大多数操作系统不允许在另一个进程打开文件时重命名该文件。
另请参阅 FileAppender 的属性。
滚动政策概览
RollingPolicy 负责涉及文件移动和重命名的翻转过程。
RollingPolicy 界面如下所示:
package ch.qos.logback.core.rolling;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.spi.LifeCycle;
public interface RollingPolicy extends LifeCycle {
public void rollover() throws RolloverFailure;
public String getActiveFileName();
public CompressionMode getCompressionMode();
public void setParent(FileAppender appender);
}
rollover 方法完成归档当前日志文件所涉及的工作。调用 getActiveFileName() 方法来计算当前日志文件(写入实时日志的位置)的文件名。正如 getCompressionMode 方法所示,RollingPolicy 还负责确定压缩模式。最后,通过 setParent 方法为 RollingPolicy 提供对其父级的引用。
基于时间的滚动策略
TimeBasedRollingPolicy 可能是最流行的滚动策略。它定义了基于时间的展期策略,例如按天或按月。 TimeBasedRollingPolicy 承担展期以及触发所述展期的责任。事实上, TimeBasedTriggeringPolicy 实现了 RollingPolicy 和 TriggeringPolicy 接口。
TimeBasedRollingPolicy 的配置采用一个强制的 fileNamePattern 属性和几个可选属性。
物业名称 类型 描述
文件名模式 String 强制 fileNamePattern 属性定义滚动(归档)日志文件的名称。它的值应包含文件名以及适当放置的 %d 转换说明符。 %d 转换说明符可以包含由 java.text.SimpleDateFormat 类指定的日期和时间模式。如果省略日期和时间模式,则采用默认模式 yyyy-MM-dd。滚动周期是根据 fileNamePattern 的值推断出来的。
请注意, RollingFileAppender ( TimeBasedRollingPolicy 的父级)中的文件属性可以设置为一个值,也可以省略 (=null)。通过设置包含的 FileAppender 的 file 属性,您可以将活动日志文件的位置和归档日志文件的位置解耦。当前日志将始终针对文件属性指定的文件。因此,当前活动日志文件的名称不会随时间而改变。
但是,如果您选择忽略 file 属性,则将根据 fileNamePattern 的值在每个周期重新计算活动文件。在此配置中,除非指定文件压缩,否则不会发生翻转。下面的例子应该可以澄清这一点。
%d{} 的赞誉中发现的日期和时间模式遵循 java.text.SimpleDateFormat 约定。 fileNamePattern 属性内或日期和时间模式内任意位置的正斜杠“/”或反斜杠“\”字符将被解释为目录分隔符。
多个 %d 说明符
可以指定多个 %d 说明符,但只有其中一个可以是主要的,即用于推断滚动周期。所有其他标记必须通过传递“aux”参数标记为辅助标记(请参阅下面的示例)。
多个 %d 说明符允许您以不同于滚动周期的文件夹结构来组织存档文件。
例如,下面显示的文件名模式按年和月组织日志文件夹,但每天午夜滚动更新日志文件。
/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log
时区
在某些情况下,您可能希望根据与主机时区不同的时钟滚动日志文件。可以在 %d 转换说明符中传递遵循日期和时间模式的时区参数。
例如:
aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log
如果指定的时区标识符未知或拼写错误,则假定 GMT 时区由 TimeZone.getTimeZone(String) 方法规范指定。
最大历史记录int
可选的 maxHistory 属性控制要保留的归档文件的最大数量,异步删除旧文件。
例如,如果您指定每月滚动,并将 maxHistory 设置为 6,则将保留 6 个月的存档文件,并删除超过 6 个月的文件。
请注意,当旧的存档日志文件被删除时,为日志文件存档而创建的任何文件夹都将根据需要删除。
将 maxHistory 设置为零会禁用存档删除。默认情况下,maxHistory 设置为零,即默认情况下不删除存档。
总尺寸上限int
可选的totalSizeCap属性控制所有归档文件的总大小。当超过总大小上限时,最旧的存档将被异步删除。 TotalSizeCap 属性还需要设置 maxHistory 属性。此外,始终首先应用“最大历史记录”限制,然后应用“总大小上限”限制。
TotalSizeCap 属性可以通过在数值后分别添加 KB、MB 和 GB 来指定为字节、千字节、兆字节或千兆字节的单位。例如,5000000、5000KB、5MB 和 2GB 都是有效值,前三个是等效的。
没有后缀的数值以字节为单位。
默认情况下,totalSizeCap 设置为零,这意味着没有总大小上限。
开始时清除历史记录 布尔值
如果设置为 true,则存档删除将在附加程序启动时执行。默认情况下,此属性设置为 false。
存档删除通常在翻转期间执行。但是,某些应用程序的生存时间可能不够长,无法触发滚动。因此,对于此类短暂的应用程序,存档删除可能永远没有机会执行。
通过将 cleanHistoryOnStart 设置为 true,存档删除将在附加程序启动时执行。
以下是一些 fileNamePattern 值及其效果的解释。
文件名模式 展期时间表 例子
/wombat/foo.%d每日展期(午夜)。由于省略了 %d 令牌说明符的可选时间和日期模式,因此采用默认模式 yyyy-MM-dd,这对应于每日展期。
文件属性未设置:2006 年 11 月 23 日期间,日志输出将转到文件 /wombat/foo.2006-11-23。在午夜和 24 日剩余时间内,日志输出将定向到 /wombat/foo.2006-11-24。
文件属性设置为 /wombat/foo.txt:2006 年 11 月 23 日期间,日志输出将转到文件 /wombat/foo.txt。午夜时分,foo.txt 将被重命名为 /wombat/foo.2006-11-23。将创建一个新的 /wombat/foo.txt 文件,并且在 11 月 24 日的剩余时间内,日志输出将定向到 foo.txt。
/wombat/%d{yyyy/MM}/foo.txt每个月初进行结转。
文件属性未设置:在 2006 年 10 月期间,日志输出将转到 /wombat/2006/10/foo.txt。 10 月 31 日午夜之后以及 11 月剩余时间,日志输出将定向到 /wombat/2006/11/foo.txt。
文件属性设置为 /wombat/foo.txt:活动日志文件将始终为 /wombat/foo.txt。在 2006 年 10 月期间,日志输出将转到 /wombat/foo.txt。 10 月 31 日午夜,/wombat/foo.txt 将更名为 /wombat/2006/10/foo.txt。将创建一个新的 /wombat/foo.txt 文件,其中记录输出将在 11 月剩余时间进行。 11月30日午夜,/wombat/foo.txt将更名为/wombat/2006/11/foo.txt等。
/wombat/foo.%d{yyyy-ww}.log 每周第一天进行展期。请注意,一周的第一天取决于区域设置。 与之前的情况类似,不同之处在于展期将在每个新的一周开始时发生。
/wombat/foo%d{yyyy-MM-dd_HH}.log 每小时的顶部滚动。 与之前的情况类似,只是翻转会在每小时的顶部发生。
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log 每分钟开始时滚动。 与之前的情况类似,只是翻转会在每分钟开始时发生。
/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log 每分钟开始时滚动。 与之前的情况类似,只是文件名将以 UTC 表示。
/foo/%d{yyyy-MM,aux}/%d.log 每日滚动。档案位于包含年份和月份的文件夹下。 在此示例中,第一个 %d 标记被标记为辅助标记。然后,第二个 %d 标记(省略时间和日期模式)被假定为主要标记。因此,翻转将每天发生(默认为 %d),并且文件夹名称将取决于年份和月份。
例如,在2006年11月期间,存档文件将全部放置在/foo/2006-11/文件夹下,例如/foo/2006-11/2006-11-14.log。
任何正斜杠或反斜杠字符都被解释为文件夹(目录)分隔符。将根据需要创建任何所需的文件夹。因此,您可以轻松地将日志文件放置在单独的文件夹中。
自动文件压缩
TimeBasedRollingPolicy 支持自动文件压缩。如果 fileNamePattern 选项的值以 .gz 或 .zip 结尾,则启用此功能。
文件名模式 展期时间表 例子
/wombat/foo.%d.gz每日滚动(午夜),自动对存档文件进行 GZIP 压缩。
文件属性未设置:2009 年 11 月 23 日期间,日志输出将转到文件 /wombat/foo.2009-11-23。但是,到了午夜,该文件将被压缩为 /wombat/foo.2009-11-23.gz。 11 月 24 日,日志记录输出将定向到 /wombat/folder/foo.2009-11-24,直到第二天开始时滚动。
文件属性设置为 /wombat/foo.txt:2009 年 11 月 23 日期间,日志输出将转到文件 /wombat/foo.txt。午夜该文件将被压缩并重命名为 /wombat/foo.2009-11-23.gz。将创建一个新的 /wombat/foo.txt 文件,其中日志输出将在 11 月 24 日剩余时间内进行。 11月24日午夜,/wombat/foo.txt将被压缩并重命名为/wombat/foo.2009-11-24.gz等。
fileNamePattern 有双重用途。首先,通过研究模式,logback 计算请求的翻转周期。其次,它计算每个存档文件的名称。请注意,两种不同的模式可以指定相同的周期。模式 yyyy-MM 和 yyyy@MM 都指定每月滚动,尽管生成的存档文件将带有不同的名称。
通过设置文件属性,您可以分离活动日志文件的位置和归档日志文件的位置。日志输出将定位到文件属性指定的文件中。由此可见,活动日志文件的名称不会随时间而改变。但是,如果您选择忽略 file 属性,则将根据 fileNamePattern 的值在每个周期重新计算活动文件。通过保留文件选项未设置,您可以避免在翻转期间存在引用日志文件的外部文件句柄时发生的文件重命名错误。
maxHistory 属性控制要保留的归档文件的最大数量,删除较旧的文件。例如,如果您指定每月滚动,并将 maxHistory 设置为 6,则将保留 6 个月的存档文件,并删除超过 6 个月的文件。请注意,当旧的存档日志文件被删除时,为日志文件存档而创建的任何文件夹都将根据需要删除。
由于各种技术原因,翻转不是时钟驱动的,而是取决于日志记录事件的到达。例如,在 2002 年 3 月 8 日,假设 fileNamePattern 设置为 yyyy-MM-dd(每日翻转),则午夜后第一个事件的到达将触发翻转。如果在午夜后 23 分 47 秒期间没有记录事件,则翻转实际上将发生在 3 月 9 日上午 00:23'47,而不是上午 0:00。
因此,根据事件的到达率,可能会以一定的延迟触发翻转。
然而,无论延迟如何,滚动算法都已知是正确的,从某种意义上说,在特定时间段内生成的所有日志记录事件都将输出到界定该时间段的正确文件中。
以下是 RollingFileAppender 与 TimeBasedRollingPolicy 结合使用的示例配置。
示例:示例配置
RollingFileAppender 使用
TimeBasedRollingPolicy
(logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingTimeBased.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
下一个配置示例说明了在谨慎模式下与 TimeBasedRollingPolicy 关联的 RollingFileAppender 的使用。
示例:示例配置
RollingFileAppender 使用
TimeBasedRollingPolicy
(logback-examples/src/main/resources/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- Support multiple-JVM writing to the same log file -->
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
基于大小和时间的滚动策略
有时您可能希望基本上按日期归档文件,但同时限制每个日志文件的大小,特别是如果后处理工具对日志文件施加大小限制。为了满足这个要求,logback 附带了 SizeAndTimeBasedRollingPolicy 。
请注意, TimeBasedRollingPolicy 已经允许限制归档日志文件的组合大小。如果您只想限制日志归档的组合大小,那么上面描述的 TimeBasedRollingPolicy 和设置totalSizeCap 属性应该足够了。此外,鉴于文件重命名是一个相对缓慢的过程并且充满问题,我们不鼓励使用 SizeAndTimeBasedRollingPolicy ,除非您有实际的用例。
除了“%d”之外,基于大小的滚动还依赖于“%i”转换令牌。 %i 和 %d 标记都是必需的。每次当前日志文件在当前时间段结束之前达到 maxFileSize 时,都会以从 0 开始的递增索引进行归档。
下表列出了适用于 SizeAndTimeBasedRollingPolicy 的属性。请注意,这些属性补充了适用于 TimeBasedRollingPolicy 的属性。
物业名称 类型 描述
最大文件大小FileSize
每次当前日志文件在当前时间段结束之前达到 maxFileSize 时,都会以从 0 开始的递增索引进行归档。
以“FileSize”为单位定义的选项可以通过在数值后分别添加 KB、MB 和 GB 来指定字节、千字节、兆字节或千兆字节。
例如,5000000、5000KB、5MB 和 2GB 都是有效值,前三个是等效的。
检查增量Duration
SINCE 1.3.12/1.4.12 鉴于检查文件大小是一项成本相对较高的操作,默认情况下每 60 秒执行一次。但是,您可以设置不同的检查增量作为持续时间。
以下是一个示例配置文件,演示了基于时间和大小的日志文件归档。
示例:示例配置
SizeAndTimeBasedFNATP
(logback-examples/src/main/resources/chapters/appenders/conf/logback-sizeAndTime.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
除了“%d”之外,请注意“%i”转换标记。 %i 和 %d 标记都是必需的。每次当前日志文件在当前时间段结束之前达到 maxFileSize 时,都会以从 0 开始的递增索引进行归档。
基于大小和时间的归档支持删除旧的归档文件。您需要使用 maxHistory 属性指定要保留的周期数。当您的应用程序停止并重新启动时,日志记录将在正确的位置继续,即当前周期的最大索引号。
在 1.1.7 之前的版本中,本文档提到了一个名为 SizeAndTimeBasedFNATP 的组件。但是,鉴于 SizeAndTimeBasedRollingPolicy 提供了更简单的配置结构,我们不再记录 SizeAndTimeBasedFNATP 。尽管如此,使用 SizeAndTimeBasedFNATP 的早期配置文件将继续正常工作。事实上, SizeAndTimeBasedRollingPolicy 是通过 SizeAndTimeBasedFNATP 子组件实现的。
固定窗口滚动策略
鉴于文件重命名是一个相对缓慢的过程并且充满问题,我们将 FixedWindowRollingPolicy 视为已弃用的策略,并且不建议使用它。
翻转时, FixedWindowRollingPolicy 根据固定窗口算法重命名文件,如下所述。
fileNamePattern 选项表示归档(滚动)日志文件的文件名模式。此选项是必需的,并且必须在模式中的某处包含整数标记 %i。
以下是 FixedWindowRollingPolicy 的可用属性
物业名称 类型 描述
最小索引int
该选项表示窗口索引的下限。
最大索引int
该选项表示窗口索引的上限。
文件名模式String
此选项表示重命名日志文件时 FixedWindowRollingPolicy 将遵循的模式。它必须包含字符串 %i,它将指示当前窗口索引值将插入的位置。
例如,使用与最小值和最大值 1 和 3 关联的 MyLogFile%i.log 将生成名为 MyLogFile1.log、MyLogFile2.log 和 MyLogFile3.log 的存档文件。
请注意,文件压缩也是通过此属性指定的。例如,fileNamePattern 设置为 MyLogFile%i.log.zip 表示归档文件必须使用 zip 格式进行压缩;还支持 gz 格式。
鉴于固定窗口滚动策略需要与窗口大小一样多的文件重命名操作,因此强烈建议不要使用大窗口大小。
当用户指定较大的值时,当前实现将自动将窗口大小减小到 20。
让我们看一下固定窗口翻转策略的更具体示例。假设 minIndex 设置为 1,maxIndex 设置为 3,fileNamePattern 属性设置为 foo%i.log,并且该 file 属性设置为 foo.log。
翻滚次数 主动输出目标 归档日志文件 描述
0 foo.log - 尚未发生翻转,logback 记录到初始文件中。
1 foo.log foo1.log 第一次翻车。 foo.log 被重命名为 foo1.log。将创建一个新的 foo.log 文件并成为活动输出目标。
2 foo.log foo1.log,foo2.log 第二次翻车。 foo1.log 被重命名为 foo2.log。 foo.log 被重命名为 foo1.log。将创建一个新的 foo.log 文件并成为活动输出目标。
3 foo.log foo1.log、foo2.log、foo3.log 第三次翻车。 foo2.log 被重命名为 foo3.log。 foo1.log 被重命名为 foo2.log。 foo.log 被重命名为 foo1.log。将创建一个新的 foo.log 文件并成为活动输出目标。
4 foo.log foo1.log、foo2.log、foo3.log 在本轮和后续轮中,滚动从删除 foo3.log 开始。其他文件通过增加其索引来重命名,如前面的步骤所示。在此轮转和后续轮转中,将有 3 个存档日志和 1 个活动日志文件。
下面的配置文件给出了配置 RollingFileAppender 和 FixedWindowRollingPolicy 的示例。请注意,File 选项是强制性的,即使它包含一些与 fileNamePattern 选项传递的信息相同的信息。
示例:使用 RollingFileAppender 的示例配置
FixedWindowRollingPolicy (logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingFixedWindow.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
触发策略概述
TriggeringPolicy 实现负责指示 RollingFileAppender 何时翻转。
TriggeringPolicy 接口仅包含一种方法。
package ch.qos.logback.core.rolling;
import java.io.File;
import ch.qos.logback.core.spi.LifeCycle;
public interface TriggeringPolicy<E> extends LifeCycle {
public boolean isTriggeringEvent(final File activeFile, final <E> event);
}
isTriggeringEvent() 方法将活动文件和当前正在处理的日志事件作为参数。具体实现根据这些参数确定是否发生翻转。
最广泛使用的触发策略,即 TimeBasedRollingPolicy ,它也兼作滚动策略,前面已经与其他滚动策略一起讨论过。
基于大小的触发策略
SizeBasedTriggeringPolicy 查看当前活动文件的大小。如果它变得大于指定的大小,它将向所属的 RollingFileAppender 发出信号,以触发现有活动文件的翻转。
SizeBasedTriggeringPolicy 只接受一个参数,即 maxFileSize,默认值为 10 MB。
maxFileSize 选项可以通过在数值后缀分别为 KB、MB 和 GB 来指定为字节、千字节、兆字节或千兆字节。例如,5000000、5000KB、5MB 和 2GB 都是有效值,前三个是等效的。
以下是一个示例配置,其中 RollingFileAppender 与 SizeBasedTriggeringPolicy 结合使用,当日志文件大小达到 5MB 时触发翻转。
示例:示例配置
RollingFileAppender 使用
SizeBasedTriggeringPolicy
(logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingSizeBased.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>test.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
登录经典
虽然日志记录事件在 logback-core 中是通用的,但在 logback-classic 中它们始终是 ILoggingEvent 的实例。 Logback-classic 无非是处理 ILoggingEvent 实例的专门处理管道。
SocketAppender 和 SSLSocketAppender
到目前为止涵盖的附加程序只能登录到本地资源。相反, SocketAppender 被设计为通过线路传输序列化的 ILoggingEvent 实例来记录到远程实体。使用 SocketAppender 时,线路上的日志记录事件将以明文形式发送。但是,当使用 SSLSocketAppender 时,日志记录事件是通过安全通道传递的。
序列化事件的实际类型是 LoggingEventVO ,它实现了 ILoggingEvent 接口。然而,就日志记录事件而言,远程日志记录是非侵入性的。反序列化后的接收端可以记录该事件,就像它是本地生成的一样。在不同机器上运行的多个 SocketAppender 实例可以将它们的日志输出定向到格式固定的中央日志服务器。 SocketAppender 不采用关联布局,因为它将序列化事件发送到远程服务器。 SocketAppender 在传输控制协议 (TCP) 层之上运行,该层提供可靠、有序、流量控制的端到端八位字节流。因此,如果远程服务器可访问,那么日志事件最终将到达那里。
否则,如果远程服务器关闭或无法访问,则日志记录事件将被删除。如果服务器恢复,事件传输将透明地恢复。
这种透明的重新连接是由定期尝试连接到服务器的连接器线程执行的。
记录事件由本机 TCP 实现自动缓冲。这意味着,如果到服务器的链接较慢,但仍快于客户端生成事件的速率,则客户端将不会受到慢速网络连接的影响。
但是,如果网络连接慢于事件产生的速率,则客户端只能以网络速率进行。特别是,在与服务器的网络链接断开的极端情况下,客户端最终将被阻止。
或者,如果网络链路正常,但服务器已关闭,则客户端不会被阻止,但日志事件将因服务器不可用而丢失。
即使 SocketAppender 不再附加到任何记录器,在存在连接器线程的情况下也不会对其进行垃圾收集。仅当与服务器的连接断开时,连接器线程才存在。为了避免这种垃圾收集问题,您应该显式关闭 SocketAppender 。创建/销毁许多 SocketAppender 实例的长期应用程序应该意识到这个垃圾收集问题。大多数其他应用程序可以安全地忽略它。如果托管 SocketAppender 的 JVM 在 SocketAppender 关闭之前退出(无论是显式关闭还是在垃圾回收之后),则管道中可能存在未传输的数据,这些数据可能会丢失。这是基于 Windows 的系统上的常见问题。为了避免丢失数据,通常在退出之前显式地 close() SocketAppender 或通过调用 LoggerContext 的 stop() 方法就足够了应用程序。
远程服务器由remoteHost 和端口属性标识。下表列出了 SocketAppender 属性。 SSLSocketAppender 支持许多附加配置属性,标题为“使用 SSL”的部分详细介绍了这些属性。
物业名称 类型 描述
包含呼叫者数据boolean
includeCallerData 选项采用布尔值。如果为 true,则调用者数据将可供远程主机使用。默认情况下,不会将呼叫者数据发送到服务器。
港口int
远程服务器的端口号。
重连延迟 Duration reconnectionDelay 选项采用持续时间字符串,例如“10 秒”表示每次尝试连接服务器失败之间等待的时间。该选项的默认值为 30 秒。
将此选项设置为零会关闭重新连接功能。请注意,如果成功连接到服务器,将不会出现连接器线程。
队列大小int
queueSize 属性采用一个整数(大于零),表示要保留以传递到远程接收器的日志记录事件的数量。当队列大小为 1 时,向远程接收器的事件传递是同步的。
当队列大小大于 1 时,假定队列中有可用空间,新事件将入队。使用大于 1 的队列长度可以消除由瞬时网络延迟引起的延迟,从而提高性能。
另请参见 eventDelayLimit 属性。
事件延迟限制 Duration eventDelayLimit 选项采用持续时间字符串,例如“10 秒”。它表示在本地队列已满(即已包含queueSize 事件)的情况下丢弃事件之前等待的时间。如果远程主机接受事件持续缓慢,则可能会发生这种情况。该选项的默认值为 100 毫秒。
远程主机 String 服务器的主机名。
ssl SSLConfiguration 仅支持 SSLSocketAppender ,此属性提供附加程序将使用的 SSL 配置,如使用 SSL 中所述。
日志服务器选项
标准 Logback Classic 发行版包括两个服务器选项,可用于从 SocketAppender 或 SSLSocketAppender 接收日志记录事件。
ServerSocketReceiver 及其启用 SSL 的对应 SSLServerSocketReceiver 是接收器组件,可以在应用程序的 logback.xml 配置文件中配置它们,以便从远程套接字附加程序接收事件。有关配置详细信息和使用示例,请参阅接收器。
SimpleSocketServer 及其支持 SSL 的对应 SimpleSSLSocketServer 都提供了易于使用的独立 Java 应用程序,该应用程序旨在从 shell 的命令行界面进行配置和运行。这些应用程序只是等待来自 SocketAppender 或 SSLSocketAppender 客户端的日志记录事件。根据本地服务器策略记录每个接收到的事件。下面给出了使用示例。
使用 SimpleSocketServer
SimpleSocketServer 应用程序采用两个命令行参数:port 和 configFile;其中 port 是要侦听的端口,configFile 是 XML 格式的配置脚本。
假设您位于 logback-examples/ 目录中,请使用以下命令启动 SimpleSocketServer :
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
src/main/java/chapters/appenders/socket/server1.xml
其中 6000 是要侦听的端口号,server1.xml 是一个配置脚本,它将 ConsoleAppender 和 RollingFileAppender 添加到根记录器。启动 SimpleSocketServer 后,您可以使用 SocketAppender 向其发送来自多个客户端的日志事件。与本手册相关的示例包括两个这样的客户端: chapters.appenders.SocketClient1 和 chapters.appenders.SocketClient2 这两个客户端都等待用户在控制台上键入一行文本。文本被封装在调试级别的日志事件中,然后发送到远程服务器。这两个客户端的不同之处在于 SocketAppender 的配置。 SocketClient1 以编程方式配置附加程序,而 SocketClient2 需要配置文件。
假设 SimpleSocketServer 在本地主机上运行,您可以使用以下命令连接到它:
java Chapters.appenders.socket.SocketClient1 本地主机 6000
您键入的每一行都应显示在上一步中启动的 SimpleSocketServer 的控制台上。如果您停止并重新启动 SimpleSocketServer ,客户端将透明地重新连接到新的服务器实例,尽管断开连接时生成的事件将简单地(且不可撤销地)丢失。
与 SocketClient1 不同,示例应用程序 SocketClient2 本身不配置 logback。它需要 XML 格式的配置文件。下面显示的配置文件 client1.xml 创建一个 SocketAppender 并将其附加到根记录器。
示例:SocketAppender 配置
(logback-examples/src/main/resources/chapters/appenders/socket/client1.xml)
遗产 规范 (1.3)
<configuration>
<appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET" />
</root>
</configuration>
请注意,在上述配置脚本中,remoteHost、port 和 includeCallerData 属性的值不是直接给出的,而是作为替换的变量键给出的。变量的值可以指定为系统属性:
java -Dhost = localhost -Dport = 6000 -DincludeCallerData = false \
Chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
此命令应给出与前面的 SocketClient1 示例类似的结果。
让我们重复强调一下,日志记录事件的序列化不是侵入性的。反序列化事件携带与任何其他日志记录事件相同的信息。
可以像本地生成一样进行操作;除了默认情况下序列化的日志记录事件不包含调用者数据之外。这里有一个例子来说明这一点。首先,使用以下命令启动 SimpleSocketServer :
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
src/main/java/chapters/appenders/socket/server2.xml
配置文件 server2.xml 创建一个 ConsoleAppender ,其布局输出调用者的文件名和行号以及其他信息。如果像以前一样使用配置文件 client1.xml 运行 SocketClient2 ,您会注意到服务器端的输出将在括号之间包含两个问号,而不是文件名和调用者的行号:
2006-11-06 17:37:30,968 调试 [Thread-0] [?:?]chapters.appenders.socket.SocketClient2 - 嗨
通过将 includeCallerData 选项设置为 true 来指示 SocketAppender 包含呼叫者数据,可以轻松更改结果。使用以下命令即可解决问题:
java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
由于反序列化事件可以与本地生成的事件相同的方式进行处理,因此它们甚至可以发送到第二个服务器进行进一步处理。
作为练习,您可能希望设置两个服务器,其中第一个服务器将从客户端接收到的事件通过隧道传输到第二个服务器。
使用 SimpleSSLSocketServer
SimpleSSLSocketServer 需要与 SimpleSocketServer 使用相同的端口和 configFile 命令行参数。此外,您必须使用命令行上指定的系统属性提供日志服务器的 X.509 凭据的位置和密码。
假设您位于 logback-examples/ 目录中,请使用以下命令启动 SimpleSSLSocketServer :
java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \
-Djavax.net.ssl.keyStorePassword=changeit \
ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \
src/main/java/chapters/appenders/socket/ssl/server.xml
此示例使用仅适合测试和实验的 X.509 凭据运行 SimpleSSLSocketServer 。在生产环境中使用 SimpleSSLSocketServer 之前,您应该获取适当的 X.509 凭证来识别您的日志服务器。有关更多详细信息,请参阅使用 SSL。
由于服务器配置在根元素上指定了 debug="true" ,因此您将在服务器的启动日志中看到将使用的 SSL 配置。这对于验证本地安全策略是否得到正确实施非常有用。
当 SimpleSSLSocketServer 运行时,您可以使用 SSLSocketAppender 连接到服务器。以下示例显示了所需的附加程序配置:
示例:SSLSocketAppender 配置
(logback-examples/src/main/resources/chapters/appenders/socket/ssl/client.xml)
遗产 规范 (1.3) 泰勒
<configuration debug="true">
<appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<ssl>
<trustStore>
<location>${truststore}</location>
<password>${password}</password>
</trustStore>
</ssl>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET" />
</root>
</configuration>
请注意,就像前面的示例一样,remoteHost、port 的值是使用替换的变量键指定的。此外,请注意 ssl 属性及其嵌套 trustStore 属性的存在,该属性使用替换变量指定信任存储的位置和密码。此配置是必要的,因为我们的示例服务器使用自签名证书。有关 SSLSocketAppender SSL 配置属性的更多信息,请参阅使用 SSL。
我们可以通过在命令行上将替换变量值指定为系统属性来使用此配置运行客户端应用程序:
java -Dhost=localhost -Dport=6000 \
-Dtruststore=文件:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
-D密码=更改\
Chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml
与前面的示例一样,您可以在客户端应用程序提示时输入消息,该消息将被传送到日志服务器(现在通过安全通道),并在控制台上显示。
请注意,命令行上给出的信任库属性指定了标识信任库位置的文件 URL。您还可以使用类路径 URL,如使用 SSL 中所述。
正如我们之前在服务器启动时看到的,由于客户端配置已在根元素上指定了 debug="true" ,因此客户端的启动日志记录包含 SSL 配置的详细信息,以帮助审核本地策略一致性。
ServerSocketAppender 和 SSLServerSocketAppender
前面讨论的 SocketAppender 组件(及其启用 SSL 的对应组件)旨在允许应用程序通过网络连接到远程日志记录服务器,以便将日志记录事件传送到服务器。
在某些情况下,让应用程序发起与远程日志服务器的连接可能不方便或不可行。对于这些情况,Logback 提供了 ServerSocketAppender 。
ServerSocketAppender 不是启动与远程日志记录服务器的连接,而是被动地侦听 TCP 套接字,等待来自远程客户端的传入连接。传递到附加程序的日志记录事件将分发到每个连接的客户端。没有客户端连接时发生的日志记录事件将被立即丢弃。
除了基本的 ServerSocketAppender 之外,Logback 还提供 SSLServerSocketAppender ,它使用安全、加密的通道将日志记录事件分发到每个连接的客户端。
此外,启用 SSL 的附加程序完全支持基于证书的相互身份验证,这可用于确保只有授权的客户端才能连接到附加程序以接收日志记录事件。
对日志事件进行编码以便在线传输的方法与 SocketAppender 使用的方法相同;每个事件都是 ILoggingEvent 的序列化实例。只是连接发起的方向相反。 SocketAppender 在建立与日志服务器的连接时充当主动对等方,而 ServerSocketAppender 是被动的;它监听来自客户端的传入连接。
ServerSocketAppender 子类型专门用于 Logback 接收器组件。有关此组件类型的更多信息,请参阅接收器。
ServerSocketAppender 支持以下配置属性:
物业名称 类型 描述
地址 String 附加程序将侦听的本地网络接口地址。如果未指定此属性,则附加程序将侦听所有网络接口。
包含呼叫者数据boolean
如果为 true,则调用者数据将可供远程主机使用。默认情况下,不会将呼叫者数据发送到客户端。
港口int
附加程序将侦听的端口号。
ssl SSLConfiguration 仅支持 SSLServerSocketAppender ,此属性提供附加程序将使用的 SSL 配置,如使用 SSL 中所述。
以下示例说明了使用 ServerSocketAppender 的配置:
示例:基本 ServerSocketAppender 配置
(logback-examples/src/main/resources/chapters/appenders/socket/server4.xml)
遗产 规范 (1.3) 泰勒
<configuration debug="true">
<appender name="SERVER"
class="ch.qos.logback.classic.net.server.ServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="debug">
<appender-ref ref="SERVER" />
</root>
</configuration>
请注意,此配置与前面使用 SocketAppender 的示例不同,仅在为附加程序指定的类中,并且在缺少 RemoteHost 属性的情况下 — 此附加程序被动等待来自远程主机的入站连接,而不是打开到远程日志服务器。
以下示例说明了使用 SSLServerSocketAppender 的配置。
示例:基本 SSLServerSocketAppender 配置
(logback-examples/src/main/resources/chapters/appenders/socket/ssl/server3.xml)
遗产 规范 (1.3) 泰勒
<configuration debug="true">
<appender name="SERVER"
class="ch.qos.logback.classic.net.server.SSLServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
<ssl>
<keyStore>
<location>${keystore}</location>
<password>${password}</password>
</keyStore>
</ssl>
</appender>
<root level="debug">
<appender-ref ref="SERVER" />
</root>
</configuration>
此配置与先前配置之间的主要区别在于,appender 的 class 属性标识 SSLServerSocketAppender 类型,以及嵌套 ssl 元素的存在,该元素在本例中指定包含 X 的密钥存储的配置.509 附加者的凭证。有关 SSL 配置属性的信息,请参阅使用 SSL。
因为 ServerSocketAppender 子类型被设计为与接收器组件一起使用,所以我们将推迟在标题为接收器的章节中提供说明性示例。
SMTP附加程序
SMTPAppender 将日志记录事件累积在一个或多个固定大小的缓冲区中,并在用户指定的事件发生后通过电子邮件发送相应缓冲区的内容。 SMTP 电子邮件传输(发送)是异步执行的。
默认情况下,电子邮件传输由错误级别的日志记录事件触发。此外,默认情况下,单个缓冲区用于所有事件。
下表总结了 SMTPAppender 的各种属性。
物业名称 类型 描述
smtp主机 String SMTP 服务器的主机名。该参数为必填项。
smtp端口 int SMTP 服务器正在侦听的端口。默认为 25。
to String 作为模式的收件人的电子邮件地址。使用触发事件作为每封外发电子邮件的输入来重新评估该模式。可以通过用逗号分隔目标地址来指定多个收件人。
或者,也可以使用多个 <to> 元素指定多个收件人。
从 String SMTPAppender 以常用电子邮件地址格式发送的电子邮件的发件人。如果您希望包含发件人的姓名,请使用“Adam Smith ”格式,以便邮件显示为源自“Adam Smith ”。
主题String
电子邮件的主题。它可以是 PatternLayout 接受为有效转换模式的任何值。布局将在下一章中讨论。
传出电子邮件将具有与在触发电子邮件的日志记录事件上应用模式相对应的主题行。
假设主题选项设置为“Log: %logger - %msg”并且触发事件的记录器名为“com.foo.Bar”,并包含消息“Hello world”,则外发电子邮件的主题行将是“日志:com.foo.Bar - 你好世界”。
默认情况下,此选项设置为“%logger{20} - %m”。
鉴别器Discriminator
在鉴别器的帮助下, SMTPAppender 可以根据鉴别器返回的值将传入的事件分散到不同的缓冲区中。默认鉴别器始终返回相同的值,以便对所有事件使用相同的缓冲区。
通过指定默认鉴别器之外的鉴别器,可以接收包含与特定用户、用户会话或客户端 IP 地址有关的事件的电子邮件消息。
评估者IEvaluator
该选项是通过创建新的 <EventEvaluator/> 元素来声明的。用户希望用作 SMTPAppender 的 Evaluator 的类的名称需要通过 class 属性指定。
如果没有此选项, SMTPAppender 会被分配一个 OnErrorEvaluator 实例,当遇到 ERROR 或更高级别的事件时,该实例会触发电子邮件传输。
Logback 附带了其他几个评估器,即 OnMarkerEvaluator (下面讨论)和一个名为 JaninoEventEvaluator 的强大评估器(在另一章中讨论)。
循环缓冲区跟踪器CyclicBufferTracker
顾名思义, CyclicBufferTracker 类的实例跟踪循环缓冲区。它根据鉴别器返回的密钥来执行此操作(见上文)。
如果不指定 cycloBufferTracker,则会自动创建 CyclicBufferTracker 的实例。默认情况下,此实例会将事件保存在大小为 256 的循环缓冲区中。您可以借助 bufferSize 选项更改大小(见下文)。
用户名 String 在普通用户/密码身份验证期间使用的用户名值。默认情况下,该参数为空。
密码 String 用于普通用户/密码身份验证的密码值。默认情况下,该参数为空。
STARTTLS boolean 如果此参数设置为 true,则此附加程序将发出 STARTTLS 命令(如果服务器支持),导致连接切换到 SSL。请注意,连接最初是未加密的。
默认情况下,此参数设置为 false。
SSL boolean 如果此参数设置为 true,则此附加程序将打开到服务器的 SSL 连接。默认情况下,此参数设置为 false。
字符集编码 String 传出的电子邮件消息将以指定的字符集进行编码。默认字符集编码是“UTF-8”,它适用于大多数用途。
本地主机 String 如果 SMTP 客户端的主机名配置不正确,例如如果客户端主机名不完全限定,某些 SMTP 服务器可能会拒绝客户端发送的 HELO/EHLO 命令。要解决此问题,您可以将 localhost 属性的值设置为客户端主机的完全限定名称。另请参阅 com.sun.mail.smtp 包文档中的“mail.smtp.localhost”属性。
异步发送 boolean 该属性决定电子邮件传输是否异步完成。默认情况下,asynchronousSending 属性为“true”。然而,在某些情况下,异步发送可能不合适。例如,如果您的应用程序使用 SMTPAppender 发送警报以响应致命错误,然后退出,则相关线程可能没有时间发送警报电子邮件。在这种情况下,请将 asynchronousSending 属性设置为“false”以进行同步电子邮件传输。
包含呼叫者数据 boolean 默认情况下, includeCallerData 设置为 false 。如果启用了 asynchronousSending 并且您希望在日志中包含呼叫者数据,则应将 includeCallerData 设置为 true 。
会话通过JNDI boolean SMTPAppender 依赖 javax.mail.Session 发送电子邮件。默认情况下,sessionViaJNDI 设置为 false ,因此 javax.mail.Session 实例由 SMTPAppender 本身使用用户指定的属性构建。如果 sessionViaJNDI 属性设置为 true ,则将通过 JNDI 检索 javax.mail.Session 对象。另请参见 jndiLocation 属性。
通过 JNDI 检索 Session 可以减少您需要配置/重新配置相同信息的位置数量,从而使您的应用程序更加干燥。有关在 Tomcat 中配置资源的更多信息,请参阅 JNDI 资源操作方法。注意 正如该文档中所述,从 JNDI 检索 Session 时,请确保从 Web 应用程序 WEB-INF/lib 文件夹中删除 mail.jar 和activation.jar。
英迪位置 String javax.mail.Session 在 JNDI 中放置的位置。默认情况下,jndiLocation 设置为“java:comp/env/mail/Session”。
SMTPAppender 仅在其循环缓冲区中保留最后 256 个日志记录事件,当其缓冲区已满时,将丢弃较旧的事件。因此, SMTPAppender 发送的任何电子邮件中传递的日志事件数量的上限为 256。这使内存需求受到限制,同时仍传递合理数量的应用程序上下文。
SMTPAppender 依赖于 JavaMail API。它已使用 JavaMail API 版本 1.4 进行了测试。 JavaMail API 需要 JavaBeans 激活框架包。您可以从 JavaMail API 和 JavaBeans 激活框架各自的网站下载它们。在尝试以下示例之前,请确保将这两个 jar 文件放入类路径中。
示例应用程序 chapters.appenders.mail.EMail 生成许多日志消息,后跟一条错误消息。它需要两个参数。第一个参数是一个整数,对应于要生成的日志事件的数量。第二个参数是logback配置文件。
电子邮件应用程序生成的最后一个日志记录事件(错误)将触发电子邮件消息的传输。
以下是用于 Email 应用程序的示例配置文件:
示例:示例 SMTPAppender 配置(logback-examples/src/main/resources/chapters/appenders/mail/mail1.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible -->
<from>SENDER-EMAIL</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
在使用上述配置文件尝试 chapters.appenders.mail.Email 应用程序之前,您必须将 smtpHost、to 和 from 属性设置为适合您的环境的值。在配置文件中设置正确的值后,执行以下命令:
java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml
您指定的收件人应收到一封电子邮件,其中包含 100 个日志记录事件,格式为 PatternLayout 下图是 Mozilla Thunderbird 显示的结果电子邮件。
在下一个示例配置文件 mail2.xml 中,smtpHost、to 和 from 属性的值由变量替换确定。这是mail2.xml的相关部分。
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
您可以在命令行中传递所需的参数:
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
chapters.appenders.mail.EMail 10000 src/main/java/chapters/appenders/mail/mail2.xml
请务必替换为适合您的环境的值。
请注意,在这个最新示例中, PatternLayout 已替换为 HTMLLayout ,后者将日志格式设置为 HTML 表。您可以更改列的列表和顺序以及表格的 CSS。请参阅 HTMLLayout 文档以获取更多详细信息。
鉴于循环缓冲区的大小为 256,收件人应该会看到包含 256 个事件的电子邮件,这些事件以 HTML 表的形式方便地格式化。请注意, chapters.appenders.mail.Email 应用程序的这次运行生成了 10'000 个事件,其中仅最后 256 个事件包含在外发电子邮件中。
Mozilla Thunderbird、Eudora 或 MS Outlook 等电子邮件客户端为 HTML 电子邮件提供相当好的 CSS 支持。然而,它们有时会自动将 HTML 降级为纯文本。
例如,要在 Thunderbird 中查看 HTML 电子邮件,必须设置“查看→邮件正文为→原始 HTML”选项。雅虎! Mail对HTML电子邮件的支持,特别是它的CSS支持非常好。
另一方面,Gmail 虽然遵循基本的 HTML 表格结构,但忽略了内部 CSS 格式。 Gmail 支持内联 CSS 格式,但由于内联 CSS 会使结果输出太大,因此 HTMLLayout 不使用内联 CSS。
自定义缓冲区大小
默认情况下,传出消息将包含 SMTPAppender 最近看到的 256 条消息。如果您愿意,您可以设置不同的缓冲区大小,如下一个示例所示。
示例:具有自定义缓冲区大小的 SMTPAppender 配置(logback-examples/src/main/resources/chapters/appenders/mail/customBufferSize.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
<!-- send just one log entry per email -->
<bufferSize>1</bufferSize>
</cyclicBufferTracker>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
触发事件
如果未设置 Evaluator 属性,则 SMTPAppender 默认为 OnErrorEvaluator 实例,该实例在遇到 ERROR 级别的事件时触发电子邮件传输。
虽然触发外发电子邮件以响应错误相对合理,但可以通过提供 EventEvaluator 接口的不同实现来覆盖此默认行为。
SMTPAppender 通过调用 evaluate() 方法将每个传入事件提交给其评估器,以检查该事件是否应触发电子邮件或仅放置在循环缓冲区中。当评估者对其评估给出肯定答复时,就会发送一封电子邮件。 SMTPAppender 包含一个且仅有一个求值器对象。该对象可以管理其自身的内部状态。出于说明目的,接下来列出的 CounterBasedEvaluator 类实现一个事件评估器,其中每第 1024 个事件都会触发一封电子邮件。
示例:A EventEvaluator
每 1024 次计算结果为 true 的实现
事件(logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java)
package chapters.appenders.mail;
import ch.qos.logback.core.boolex.EvaluationException;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.spi.ContextAwareBase;
public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator {
static int LIMIT = 1024;
int counter = 0;
String name;
public boolean evaluate(Object event) throws NullPointerException,
EvaluationException {
counter++;
if (counter == LIMIT) {
counter = 0;
return true;
} else {
return false;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
请注意,此类扩展了 ContextAwareBase 并实现了 EventEvaluator 。这使得用户可以专注于其 EventEvaluator 的核心功能,并让基类提供通用功能。
设置 SMTPAppender 的评估器选项指示它使用自定义评估器。下一个配置文件将 SMTPAppender 附加到根记录器。此附加程序使用 CounterBasedEvaluator 实例作为其事件评估器。
示例:带有自定义的 SMTPAppender
Evaluator 和缓冲区大小(logback-examples/src/main/resources/chapters/appenders/mail/mail3.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="chapters.appenders.mail.CounterBasedEvaluator" />
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
基于标记的触发
尽管合理,但默认的触发策略(即每个错误级别的事件都会触发外发电子邮件)可能会导致电子邮件过多,从而使目标用户的邮箱变得混乱。 Logback 附带了另一个触发策略,称为 OnMarkerEvaluator。它基于标记。本质上,仅当事件标有用户指定的标记时才会触发电子邮件。下一个例子应该会让这一点更清楚。
Marked_EMail 应用程序包含多个日志记录语句,其中一些属于 ERROR 级别。一个值得注意的陈述包含一个标记。这是相关代码。
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
logger.error(notifyAdmin,
"This is a serious error requiring the admin's attention",
new Exception("Just testing"));
下一个配置文件仅在存在带有 NOTIFY_ADMIN 或 TRANSACTION_FAILURE 标记的事件时才会触发外发电子邮件。
示例: SMTPAppender 与
OnMarkerEvaluator (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
<marker>NOTIFY_ADMIN</marker>
<!-- you specify add as many markers as you want -->
<marker>TRANSACTION_FAILURE</marker>
</evaluator>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
<root>
<level value ="debug"/>
<appender-ref ref="EMAIL" />
</root>
</configuration>
使用以下命令尝试一下:
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
chapters.appenders.mail.Marked_EMail src/main/java/chapters/appenders/mail/mailWithMarker.xml
使用 JaninoEventEvaluator 基于标记的触发
请注意,我们可以使用更通用的 JaninoEventEvaluator ,而不是使用以标记为中心的 OnMarkerEvaluator 。例如,以下配置文件使用 JaninoEventEvaluator 而不是 OnMarkerEvaluator ,但在其他方面与前面的配置文件等效。
示例: SMTPAppender 与
JaninoEventEvaluator (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker_Janino.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
<expression>
(markerList.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE"))
</expression>
</evaluator>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
</configuration>
身份验证/STARTTLS/SSL
SMTPAppender 支持通过普通用户密码以及 STARTTLS 和 SSL 协议进行身份验证。
请注意,STARTTLS 与 SSL 的不同之处在于,在 STARTTLS 中,连接最初是非加密的,只有在客户端发出 STARTTLS 命令后(如果服务器支持),连接才会切换到 SSL。
在 SSL 模式下,连接从一开始就被加密。
Gmail (SSL) 的 SMTPAppender 配置
下一个示例向您展示如何使用 SSL 协议为 Gmail 配置 SMTPAppender 。
示例:: SMTPAppender 发送至 Gmail
使用 SSL
(logback-examples/src/main/resources/chapters/appenders/mail/gmailSSL.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>465</smtpPort>
<SSL>true</SSL>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_PASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible -->
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
Gmail 的 SMTPAppender (STARTTLS)
下一个示例向您展示如何为 Gmail 配置 STARTTLS 协议的 SMTPAppender 。
示例:使用 STARTTLS 发送到 GMAIL (logback-examples/src/main/resources/chapters/appenders/mail/gmailSTARTTLS.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>587</smtpPort>
<STARTTLS>true</STARTTLS>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_xPASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible -->
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
带有 MDCDiscriminator 的 SMTPAppender
如前所述,通过指定默认鉴别器之外的鉴别器, SMTPAppender 将生成包含与特定用户、用户会话或客户端 IP 地址相关的事件的电子邮件,具体取决于指定的鉴别器。
下一个示例说明了 MDCBasedDiscriminator 与名为“req.remoteHost”的 MDC 密钥的结合使用,假定包含访问虚构应用程序的远程主机的 IP 地址。在 Web 应用程序中,您可以使用 MDCInsertingServletFilter 来填充 MDC 值。
示例: SMTPAppender 与
基于MDC的鉴别器
(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost>
<to>EMAIL-DESTINATION</to>
<from>SENDER-EMAIL</from>
<discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator">
<key>req.remoteHost</key>
<defaultValue>default</defaultValue>
</discriminator>
<subject>${HOSTNAME} -- %X{req.remoteHost} %msg"</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg</pattern>
</layout>
</appender>
<root>
<level level="DEBUG"/>
<appender-ref ref="EMAIL" />
</root>
</configuration>
这样, SMTPAppender 生成的每封外发电子邮件都将属于一个唯一的远程主机,极大地方便了问题诊断。
非常繁忙的系统中的缓冲区管理
在内部,鉴别器返回的每个不同值将导致创建新的循环缓冲区。但是,最多会保留 maxNumberOfBuffers(默认为 64)。每当缓冲区数量超过 maxNumberOfBuffers 时,最近最少更新的缓冲区就会被自动丢弃。作为第二个安全措施,过去 30 分钟内未更新的任何缓冲区也将被自动丢弃。
在每分钟处理大量事务的系统上,仅允许少量 maxNumberOfBuffers(默认为 64)通常会导致传出电子邮件中的事件数量过少。
事实上,在存在大量事务的情况下,将有多个缓冲区与同一事务相关联,因为对于相同的鉴别器值(或事务),缓冲区将被连续杀死和重生。
请注意,即使在非常繁忙的系统中,循环缓冲区的最大数量也受到 maxNumberOfBuffers 的限制。
为了避免这种溜溜球效应, SMTPAppender 一旦看到标记为“FINALIZE_SESSION”的事件,就会释放与给定鉴别器键关联的缓冲区。这将导致适当的缓冲区在每个事务结束时被丢弃。然后,您可以安全地将 maxNumberOfBuffers 的值增加到更大的值,例如 512 或 1024,而不会面临内存不足的风险。
存在三种不同但互补的机制一起工作来管理循环缓冲区。它们确保在任何给定时刻只有相关缓冲区保持活动状态,即使在非常繁忙的系统中也是如此。
数据库附加程序
DBAppender 以独立于 Java 编程语言的格式将日志记录事件插入到三个数据库表中。
从 logback 版本 1.2.8 开始,DBAppender 不再随 logback-classic 一起提供。但是,logback-classic 的 DBAppender 可在以下 Maven 坐标下使用:
ch.qos.logback.db:logback-classic-db:1.2.11.1
这三个表是logging_event、logging_event_property和logging_event_exception。在使用 DBAppender 之前它们必须存在。 Logback 附带了用于创建表的 SQL 脚本。它们可以在 logback-classic/src/main/java/ch/qos/logback/classic/db/script 文件夹下找到。每个最流行的数据库系统都有一个特定的脚本。如果缺少适用于您的特定类型数据库系统的脚本,则以现有脚本为例,编写一个脚本应该很容易。
如果您将它们发送给我们,我们将很乐意在未来的版本中包含缺失的脚本。
如果您的 JDBC 驱动程序支持 JDBC 3.0 规范中引入的 getGeneratedKeys 方法,假设您已如上所述创建了相应的数据库表,则无需执行任何其他步骤。否则,必须有一个适合您的数据库系统的 SQLDialect 。目前,logback 有 H2、HSQL、MS SQL Server、MySQL、Oracle、PostgreSQL、SQLLite 和 Sybase 的方言。
下表总结了数据库类型及其对 getGeneratedKeys() 方法的支持。
RDBMS 测试版本 测试的 JDBC 驱动程序版本 支持
getGeneratedKeys() 方法 是一种方言
由logback提供
DB2 未经测试的 未经测试的 未知 NO
H2 1.2.132 - 未知 YES
HSQL 1.8.0.7 - NO YES
微软SQL服务器 2005 2.0.1008.2(sqljdbc.jar) YES YES
MySQL 5.0.22 5.0.8(mysql-connector.jar) YES YES
PostgreSQL 8.x 8.4-701.jdbc4 NO YES
甲骨文 10g 10.2.0.1(ojdbc14.jar) YES YES
SQL精简版 3.7.4 - 未知 YES
Sybase SQLAnywhere 10.0.1 - 未知 YES
实验表明,在“标准”PC 上将单个事件写入数据库大约需要 10 毫秒。如果使用池化连接,这个数字会下降到 1 毫秒左右。请注意,大多数 JDBC 驱动程序已附带连接池支持。
配置 logback 以使用 DBAppender 可以通过多种不同的方式完成,具体取决于连接到数据库的工具以及数据库本身。配置 DBAppender 的关键问题是设置其 ConnectionSource 对象,我们很快就会发现。
为数据库配置 DBAppender 后,日志记录事件将发送到指定的数据库。如前所述,logback 使用三个表来存储日志事件数据。
logging_event 表包含以下字段:
场地 类型 描述
时间戳 big int 创建日志记录事件时有效的时间戳。
格式化消息 text 使用 org.slf4j.impl.MessageFormatter 进行格式化后,已添加到日志记录事件的消息,以防对象与消息一起传递。
记录者名称 varchar 用于发出日志记录请求的记录器的名称。
级别字符串 varchar 日志记录事件的级别。
参考标志smallint
logback 使用此字段来标识具有关联的异常或 MDC 属性值的日志记录事件。
它的值由 ch.qos.logback.classic.db.DBHelper 计算。包含 MDC 或 Context 属性的日志记录事件的标志号为 1。包含异常的日志记录事件的标志号为 2。包含这两个元素的日志记录事件的标志号为 1。数量 3.
调用者文件名 varchar 发出日志记录请求的文件的名称。
调用者类 varchar 发出日志记录请求的类。
调用者方法 varchar 发出日志记录请求的方法的名称。
来电者线路 char 发出日志记录请求的行号。
事件ID int 日志记录事件的数据库 ID。
logging_event_property 用于存储 MDC 或 Context 中包含的键和值。它包含以下字段:
场地 类型 描述
事件ID int 日志记录事件的数据库 ID。
映射键 varchar MDC 属性的键
映射值 text MDC 属性的值
logging_event_exception 表包含以下字段:
场地 类型 描述
事件ID int 日志记录事件的数据库 ID。
i smallint 完整堆栈跟踪中该行的索引。
跟踪线 varchar 对应行
为了更直观地展示 DBAppender 所做的工作,下面是 MySQL 数据库的屏幕截图,其中包含 DBAppender 提供的内容。
logging_event 表:
logging_event_exception 表:
logging_event_property 表:
连接源
ConnectionSource 接口提供了一种可插入的方式,可以透明地获取需要使用 java.sql.Connection 的 logback 类的 JDBC 连接。目前 ConnectionSource 共有三种实现,即 DataSourceConnectionSource 、 DriverManagerConnectionSource 和 JNDIConnectionSource 。
我们将回顾的第一个示例是使用 DriverManagerConnectionSource 和 MySQL 数据库的配置。以下配置文件是我们所需要的。
示例: DBAppender 配置(logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-driverManager.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://host_name:3306/database_name</url>
<user>username</user>
<password>password</password>
</connectionSource>
</appender>
<root level="DEBUG" >
<appender-ref ref="DB" />
</root>
</configuration>
必须声明正确的驱动程序。这里使用了 com.mysql.jdbc.Driver 类。 url 必须以 jdbc:mysql:// 开头。
DriverManagerConnectionSource 是 ConnectionSource 的实现,根据连接URL以传统的JDBC方式获取连接。
请注意,此类将为每次调用 getConnection() 建立一个新的 Connection 。建议您使用本机支持连接池的 JDBC 驱动程序,或者创建您自己的 ConnectionSource 实现,该实现利用您已经使用的任何池机制。如果您有权访问支持 javax.sql.DataSource 的 JNDI 实现,例如在 J2EE 应用程序服务器中,请参阅下面的 JNDIConnectionSource 。
使用 DataSource 连接到数据库非常相似。配置现在使用 DataSourceConnectionSource ,它是 ConnectionSource 的实现,它基于 javax.sql.DataSource 以推荐的 JDBC 方式获取 Connection 。
示例: DBAppender 配置(logback-examples/src/main/resources/chapters/appenders/db/append-with-datasource.xml)
遗产 规范 (1.3) 泰勒
<configuration debug="true">
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="${dataSourceClass}">
<!-- Joran cannot substitute variables
that are not attribute values. Therefore, we cannot
declare the next parameter like the others.
-->
<param name="${url-key:-url}" value="${url_value}"/>
<serverName>${serverName}</serverName>
<databaseName>${databaseName}</databaseName>
</dataSource>
<user>${user}</user>
<password>${password}</password>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB" />
</root>
</configuration>
请注意,在此配置示例中,我们大量使用替换变量。当连接详细信息必须集中在单个配置文件中并由 logback 和其他框架共享时,它们有时会很方便。
JNDI连接源
JNDIConnectionSource 是 logback 中另一个 ConnectionSource 实现。正如其名称所示,它从 JNDI 检索 javax.sql.DataSource ,然后利用它来获取 java.sql.Connection 实例。 JNDIConnectionSource 主要设计用于在 J2EE 应用程序服务器内部或由应用程序服务器客户端使用,假设应用程序服务器支持 javax.sql.DataSource 的远程访问。因此,人们可以利用连接池以及应用程序服务器提供的任何其他好处。更重要的是,您的应用程序将更加干燥,因为不再需要在 logback.xml 中定义 DataSource 。
例如,下面是 Tomcat 的配置片段。尽管任何受支持的数据库系统(上面列出)都可以工作,但它假定 PostgreSQL 作为数据库。
<Context docBase="/path/to/app.war" path="/myapp">
...
<Resource name="jdbc/logging"
auth="Container"
type="javax.sql.DataSource"
username="..."
password="..."
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost/..."
maxActive="8"
maxIdle="4"/>
...
</Context>
一旦在 J2EE 服务器中定义了 DataSource ,您的 logback 配置文件就可以轻松引用它,如下一个示例所示。
示例: DBAppender 配置
通过 JNDIConnectionSource
(logback-examples/src/main/resources/chapters/appenders/db/append-via-jndi.xml)
遗产 规范 (1.3) 泰勒
<configuration debug="true">
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource">
<!-- please note the "java:comp/env/" prefix -->
<jndiLocation>java:comp/env/jdbc/logging</jndiLocation>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB" />
</root>
</configuration>
请注意,此类将使用无参数构造函数获取 javax.naming.InitialContext 。当在 J2EE 环境中执行时,这通常会起作用。在 J2EE 环境之外时,请确保提供 JNDI 提供商文档中所述的 jndi.properties 文件。
连接池
可以以相当快的速度创建日志事件。为了跟上必须插入数据库的事件流,建议将连接池与 DBAppender 一起使用。
实验表明,将连接池与 DBAppender 结合使用可以显着提高性能。使用以下配置文件,日志记录事件将发送到 MySQL 数据库,而不进行任何池化。
示例: DBAppender 配置
没有池化
(logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<serverName>${serverName}</serverName>
<port>${port$</port>
<databaseName>${dbName}</databaseName>
<user>${user}</user>
<password>${pass}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB" />
</root>
</configuration>
使用此配置文件,将 500 个日志记录事件发送到 MySQL 数据库需要长达 5 秒的时间,即每个请求 10 毫秒。在处理大型应用程序时,这个数字是不可接受的。
需要专用的外部库才能将连接池与 DBAppender 结合使用。下一个示例使用 c3p0。为了能够使用 c3p0,必须下载它并将 c3p0-VERSION.jar 放入类路径中。
示例: DBAppender 配置
与池化
(logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource
class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl>
<user>${user}</user>
<password>${password}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB" />
</root>
</configuration>
通过这种新配置,向上述 MySQL 数据库发送 500 个日志请求大约需要 0.5 秒,平均每个请求 1 毫秒,性能提高了十倍。
系统日志附加程序
系统日志协议是一个非常简单的协议:系统日志发送方向系统日志接收方发送一条小消息。接收器通常称为系统日志守护程序或系统日志服务器。 Logback 可以将消息发送到远程系统日志守护进程。这是通过使用 SyslogAppender 来实现的。
以下是您可以传递给 SyslogAppender 的属性。
物业名称 类型 描述
系统日志主机 String 系统日志服务器的主机名。
港口 String 要连接到的系统日志服务器上的端口号。通常,人们不想更改默认值 514。
设施String
该设施旨在识别消息的来源。
设施选项必须设置为以下字符串之一:KERN、USER、MAIL、DAEMON、AUTH、SYSLOG、LPR、NEWS、UUCP、CRON、AUTHPRIV、FTP、NTP、AUDIT、ALERT、CLOCK、LOCAL0、LOCAL1、LOCAL2、LOCAL3 、本地4、本地5、本地6、本地7。大小写并不重要。
后缀模式String
suffixPattern 选项指定发送到系统日志服务器的消息的非标准化部分的格式。默认情况下,其值为 [%thread] %logger %msg。 PatternLayout 可以使用的任何值都是正确的 suffixPattern 值。
堆栈跟踪模式String
stackTracePattern 属性允许自定义出现在每个堆栈跟踪行之前的字符串。该属性的默认值为“\t”,即制表符。 PatternLayout 接受的任何值都是 stackTracePattern 的有效值。
throwable排除 boolean 将 throwableExcluded 设置为 true 将导致与 Throwable 关联的堆栈跟踪数据被忽略。默认情况下, throwableExcluded 设置为 false ,以便将堆栈跟踪数据发送到系统日志服务器。
日志记录事件的系统日志严重性是从日志记录事件的级别转换而来的。 DEBUG 级别转换为 7,INFO 转换为 6,WARN 转换为 4,ERROR 转换为 3。
由于 syslog 请求的格式遵循相当严格的规则,因此 SyslogAppender 没有可使用的布局。然而,使用 suffixPattern 选项可以让用户显示她想要的任何信息。
这是使用 SyslogAppender 的示例配置。
示例: SyslogAppender
配置
(logback-examples/src/main/resources/chapters/appenders/conf/logback-syslog.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>remote_home</syslogHost>
<facility>AUTH</facility>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
</appender>
<root level="DEBUG">
<appender-ref ref="SYSLOG" />
</root>
</configuration>
测试此配置时,您应该验证远程系统日志守护程序是否接受来自外部源的请求。经验表明,默认情况下,系统日志守护进程通常会拒绝通过网络连接发出的请求。
筛选Appender
顾名思义, SiftingAppender 可用于根据给定的运行时属性来分离(或筛选)日志记录。例如, SiftingAppender 可以根据用户会话分离日志事件,以便不同用户生成的日志进入不同的日志文件,每个用户一个日志文件。
物业名称 类型 描述
暂停 Duration 超过超时时间仍未被访问的嵌套附加程序被视为过时。过时的附加程序已关闭且未被 SiftingAppender 引用。超时的默认值为 30 分钟。
最大Appender计数 integer 可以创建和跟踪的嵌套附加程序 SiftingAppender 的最大数量。 maxAppenderCount 的默认值为 Integer.MAX_VALUE。
SiftingAppender 通过动态创建嵌套附加程序来实现这一壮举。嵌套附加程序是根据 SiftingAppender 本身配置中指定的模板创建的(包含在 <sift> 元素内,请参阅下面的示例)。 SiftingAppender 负责管理子appender的生命周期。例如, SiftingAppender 将自动关闭并删除任何过时的附加程序。当超出超时参数指定的持续时间没有任何内容访问嵌套附加程序时,该附加程序被认为是过时的。
处理日志记录事件时, SiftingAppender 将选择一个子附加程序进行委托。选择标准由判别器在运行时计算。用户可以借助 Discriminator 指定选择标准。现在让我们研究一个例子。
例子
SiftExample 应用程序记录一条消息,表明应用程序已启动。然后,它将 MDC 密钥“userid”设置为“Alice”并记录一条消息。这是重要的代码:
logger.debug("应用程序已启动");
MDC.put("userid", "Alice");
logger.debug("爱丽丝打招呼");
配置文件的模板说明了 SiftingAppender 的用法。
示例: SiftingAppender
配置
(logback-examples/src/main/resources/chapters/appenders/sift/byUserid.xml)
<configuration>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<!-- in the absence of the class attribute, it is assumed that the
desired discriminator type is
ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
<discriminator>
<key>userid</key>
<defaultValue>unknown</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
<file>${userid}.log</file>
<append>false</append>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d [%thread] %level %mdc %logger{35} -%kvp -%msg%n</pattern>
</layout>
</appender>
</sift>
</appender>
<root level="DEBUG">
<appender-ref ref="SIFT" />
</root>
</configuration>
在没有类属性的情况下,假设鉴别器类型是MDDCBasedDiscriminator。区别值是与键属性给定的键关联的 MDC 值。但是,如果该 MDC 值为 null,则使用 defaultValue 作为区分值。
SiftingAppender 的独特之处在于它能够引用和配置子附加程序。在上面的示例中, SiftingAppender 将创建多个 FileAppender 实例,每个 FileAppender 实例由与“userid”MDC 密钥关联的值标识。每当“userid”MDC 键被分配一个新值时,就会从头开始构建一个新的 FileAppender 实例。 SiftingAppender 跟踪它创建的附加程序。 30 分钟内未使用的 Appender 将被自动关闭并丢弃。
变量导出 拥有不同的appender实例是不够的;每个实例必须输出到不同的目标资源。
为了允许这种区分,在附加程序模板中,传递给鉴别器的密钥(上例中的“userid”)被导出并成为变量。因此,该变量可用于区分给定子附加程序使用的实际资源。
使用上面显示的“byUserid.xml”配置文件运行 SiftExample 应用程序将生成两个不同的日志文件:“unknown.log”和“Alice.log”。
本地范围变量 从版本 1.0.12 开始,配置文件中本地范围中定义的属性将可用于嵌套附加程序。此外,您可以在 <sift> 元素中定义变量或动态计算变量。还支持组合 <sift> 元素外部和内部定义的部分的变量。
正确设置超时时间
对于某些类型的应用程序,可能很难获得正确的超时参数。如果超时太小,则可能会删除嵌套的附加程序,以便在几秒钟后重新创建。这种现象称为垃圾处理。如果超时太长并且连续快速创建附加程序,您可能会耗尽资源。同样,将 maxAppenderCount 设置得太低也可能会导致垃圾。
在许多情况下,在代码中确定不再需要嵌套附加程序的位置可能会更容易。如果存在这样的位置,即使是大约存在,也可以使用 FINALIZE_SESSION 标记从该位置进行记录。每当 SiftingAppender 看到标记为 FINALIZE_SESSION 的日志记录事件时,它就会终止关联的嵌套附加程序的生命周期。在达到其生命周期结束时,嵌套的附加程序将停留几秒钟以处理任何迟到的事件(如果有),然后将被关闭。
import org.slf4j.Logger;
import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;
void job(String jobId) {
MDC.put("jobId", jobId);
logger.info("Starting job.");
... do whatever the job needs to do
// will cause the nested appender reach end-of-life. It will
// linger for a few seconds.
logger.info(FINALIZE_SESSION_MARKER, "About to end the job");
try {
.. perform clean up
} catch(Exception e);
// This log statement will be handled by the lingering appender.
// No new appender will be created.
logger.error("unexpected error while cleaning up", e);
}
}
异步Appender
AsyncAppender 异步记录 ILoggingEvents。它仅充当事件调度程序,因此必须引用另一个附加程序才能执行任何有用的操作。
如果 80% FULL AsyncAppender 在 BlockingQueue 中缓冲事件,则默认为有损。 AsyncAppender 创建的工作线程从队列头部获取事件,并将它们分派到附加到 AsyncAppender 的单个附加程序。请注意,默认情况下,如果 AsyncAppender 队列已满 80%,则 AsyncAppender 将丢弃 TRACE、DEBUG 和 INFO 级别的事件。该策略对性能产生了惊人的有利影响,但代价是事件丢失。
应用程序停止/重新部署应用程序关闭或重新部署时,必须停止 AsyncAppender ,以便停止和回收工作线程并从队列中刷新日志记录事件。这可以通过停止 LoggerContext 来实现,这将关闭所有附加程序,包括任何 AsyncAppender 实例。 AsyncAppender 将等待工作线程刷新到 maxFlushTime 中指定的超时时间。如果您发现排队的事件在 LoggerContext 关闭期间被丢弃,您可能需要增加超时。为 maxFlushTime 指定值 0 将强制 AsyncAppender 在从 stop 方法返回之前等待所有排队事件被刷新。
关闭后清理 根据 JVM 关闭的模式,处理排队事件的工作线程可能会被中断,从而导致事件滞留在队列中。当 LoggerContext 没有完全停止或 JVM 在典型控制流之外终止时,通常会发生这种情况。为了避免在这些情况下中断工作线程,可以将关闭挂钩插入 JVM 运行时,以便在启动 JVM 关闭后正确停止 LoggerContext。当其他关闭挂钩尝试记录事件时,关闭挂钩也可能是彻底关闭 Logback 的首选方法。
以下是 AsyncAppender: 承认的属性列表
物业名称 类型 描述
队列大小 int 阻塞队列的最大容量。默认情况下,queueSize 设置为 256。
丢弃阈值 int 默认情况下,当阻塞队列剩余容量为20%时,将丢弃TRACE、DEBUG和INFO级别的事件,仅保留WARN和ERROR级别的事件。要保留所有事件,请将discardingThreshold 设置为0。
包含呼叫者数据 boolean 提取呼叫者数据可能相当昂贵。为了提高性能,默认情况下,当事件添加到事件队列时,不会提取与该事件关联的调用者数据。默认情况下,仅复制线程名称和 MDC 等“廉价”数据。您可以通过将 includeCallerData 属性设置为 true 来指示此附加程序包含调用者数据。
最大冲洗时间 int 根据队列深度和引用的附加程序的延迟, AsyncAppender 可能需要花费不可接受的时间来完全刷新队列。当 LoggerContext 停止时, AsyncAppender stop 方法将等待工作线程完成此超时。使用 maxFlushTime 指定最大队列刷新超时(以毫秒为单位)。无法在此窗口内处理的事件将被丢弃。该值的语义与 Thread.join(long) 的语义相同。
永不阻塞 boolean 如果 false (默认值),追加器将阻止追加到完整队列而不是丢失消息。设置为 true ,附加程序只会删除消息,而不会阻止您的应用程序。
默认情况下,事件队列配置的最大容量为 256 个事件。如果队列已满,则应用程序线程将被阻止记录新事件,直到工作线程有机会分派一个或多个事件。
当队列不再达到其最大容量时,应用程序线程能够再次开始记录事件。因此,当附加器以等于或接近其事件缓冲区的容量运行时,异步日志记录将变为伪同步。
这不一定是坏事。附加程序旨在允许应用程序继续运行,尽管需要稍多的时间来记录事件,直到附加程序缓冲区的压力减轻。
优化调整追加器事件队列的大小以获得最大应用程序吞吐量取决于几个因素。以下任何或所有因素都可能导致出现伪同步行为:
大量应用程序线程
每个应用程序调用的大量日志记录事件
每个日志记录事件包含大量数据
子追加器的高延迟
为了让事情继续进行,增加队列的大小通常会有所帮助,但会牺牲应用程序可用的堆。
有损行为 根据上面的讨论,为了减少阻塞,默认情况下,当队列容量剩余不足 20% 时, AsyncAppender 将丢弃 TRACE、DEBUG 和 INFO 级别的事件,仅保留以下级别的事件级别警告和错误。
当队列容量低于 20% 时,此策略可确保日志记录事件的非阻塞处理(因此具有出色的性能),但代价是丢失 TRACE、DEBUG 和 INFO 级别的事件。通过将discardingThreshold 属性设置为0(零)可以防止事件丢失。
示例: AsyncAppender
配置
(logback-examples/src/main/resources/chapters/appenders/conc/logback-async.xml)
遗产 规范 (1.3) 泰勒
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myapp.log</file>
<encoder>
<pattern>%logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<root level="DEBUG">
<appender-ref ref="ASYNC" />
</root>
</configuration>
编写自己的 Appender
您可以通过子类化 AppenderBase 轻松编写附加程序。它处理对过滤器、状态消息和大多数附加程序共享的其他功能的支持。派生类只需要实现一个方法,即 append(Object eventObject) 。
我们接下来列出的 CountingConsoleAppender 在控制台上附加有限数量的传入事件。达到限制后就会关闭。它使用 PatternLayoutEncoder 来格式化事件并接受名为 limit 的参数。因此,除了 append(Object eventObject) 之外还需要一些方法。如下所示,这些参数由 logback 的各种配置机制自动处理。
示例 4: CountingConsoleAppender (logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java)
package chapters.appenders;
import java.io.IOException;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
public class CountingConsoleAppender extends AppenderBase<ILoggingEvent> {
static int DEFAULT_LIMIT = 10;
int counter = 0;
int limit = DEFAULT_LIMIT;
PatternLayoutEncoder encoder;
public void setLimit(int limit) {
this.limit = limit;
}
public int getLimit() {
return limit;
}
@Override
public void start() {
if (this.encoder == null) {
addError("No encoder set for the appender named ["+ name +"].");
return;
}
try {
encoder.init(System.out);
} catch (IOException e) {
}
super.start();
}
public void append(ILoggingEvent event) {
if (counter >= limit) {
return;
}
// output the events as formatted by our layout
try {
this.encoder.doEncode(event);
} catch (IOException e) {
}
// prepare for next event
counter++;
}
public PatternLayoutEncoder getEncoder() {
return encoder;
}
public void setEncoder(PatternLayoutEncoder encoder) {
this.encoder = encoder;
}
}
start() 方法检查是否存在 PatternLayoutEncoder 。如果未设置编码器,附加程序将无法启动并发出错误消息。
这个自定义的appender说明了两点:
遵循 setter/getter JavaBeans 约定的所有属性都由 logback 配置器透明地处理。 start() 方法在 logback 配置期间自动调用,负责验证 Appender 的各种属性是否已设置且一致。
AppenderBase.doAppend() 方法调用其派生类的append() 方法。实际的输出操作发生在 append ()方法中。特别是,在这个方法中,appender 通过调用事件的布局来格式化事件。
CountingConsoleAppender 可以像任何其他附加程序一样进行配置。有关示例,请参阅示例配置文件 logback-examples/src/main/resources/chapters/appenders/countingConsole.xml。
登录访问
logback-classic 中的大多数附加程序在 logback-access 中都有对应的附加程序。它们的工作方式本质上与它们的 logback-classic 对应部分相同。在下一节中,我们将介绍它们的使用。
SocketAppender 和 SSLSocketAppender
SocketAppender 旨在通过线路传输序列化的 AccessEvent 对象来记录到远程实体。就访问事件而言,远程日志记录是非侵入性的。反序列化后的接收端可以记录该事件,就像它是本地生成的一样。
SSLSocketAppender 扩展了基本的 SocketAppender ,允许通过安全套接字层 (SSL) 登录到远程实体。
access 的 SocketAppender 属性与 classic 的 SocketAppender 的属性相同。
ServerSocketAppender 和 SSLServerSocketAppender
与 SocketAppender 一样, ServerSocketAppender 旨在通过线路传输序列化的 AccessEvent 对象来记录到远程实体。但是,当使用 ServerSocketAppender 时,附加程序充当服务器,被动侦听 TCP 套接字,等待来自感兴趣的客户端的入站连接。传递到附加程序的日志记录事件将分发到所有连接的客户端。
SSLSocketAppender 扩展了基本的 ServerSocketAppender ,允许通过安全套接字层 (SSL) 登录到远程实体。
access 的 ServerSocketAppender 属性与 classic 的 ServerSocketAppender 的属性相同。
SMTP附加程序
Access 的 SMTPAppender 的工作方式与其 Classic 版本相同。然而,评估者的选择却截然不同。默认情况下, URLEvaluator 对象由 SMTPAppender 使用。该评估器包含根据当前请求的 URL 检查的 URL 列表。当请求提供给 URLEvaluator 的页面之一时, SMTPAppender 会发送一封电子邮件。
以下是访问环境中 SMTPAppender 的示例配置。
示例: SMTPAppender
配置
(logback-examples/src/main/resources/chapters/appenders/conf/access/logback-smtp.xml)
<appender name="SMTP"
class="ch.qos.logback.access.net.SMTPAppender">
<layout class="ch.qos.logback.access.html.HTMLLayout">
<pattern>%h%l%u%t%r%s%b</pattern>
</layout>
<Evaluator class="ch.qos.logback.access.net.URLEvaluator">
<URL>url1.jsp</URL>
<URL>directory/url2.html</URL>
</Evaluator>
<from>sender_email@host.com</from>
<smtpHost>mail.domain.com</smtpHost>
<to>recipient_email@host.com</to>
</appender>
例如,这种触发电子邮件的方式可以让用户选择特定流程中重要步骤的页面。
当访问此类页面时,系统会发送电子邮件,其中包含之前访问过的页面以及用户希望包含在电子邮件中的任何信息。
数据库附加程序
DBAppender 用于将访问事件插入数据库。
从 logback 版本 1.2.8 开始 DBAppender 不再附带 logback-access。但是,用于 logback-access 的 DBAppender 可在以下 Maven 坐标下使用:
ch.qos.logback.db:logback-access-db:1.2.11.1
DBAppender 使用两个表:access_event 和 access_event_header。在使用 DBAppender 之前,它们都必须存在。 Logback 附带了用于创建表的 SQL 脚本。它们可以在 logback-access/src/main/java/ch/qos/logback/access/db/script 目录中找到。每个最流行的数据库系统都有一个特定的脚本。如果缺少针对您的特定类型数据库系统的脚本,则以现有脚本之一为例,编写一个脚本应该很容易。
我们鼓励您将此类缺失的脚本贡献回项目中。
access_event表的字段描述如下:
场地 类型 描述
时间戳 big int 创建访问事件时有效的时间戳。
请求URI varchar 请求的 URI。
请求网址 varchar 所请求的 URL。这是一个由请求方法、请求 URI 和请求协议组成的字符串。
远程主机 varchar 远程主机的名称。
远程用户 varchar 远程用户的名称。
远程地址 varchar 远程 IP 地址。
协议 varchar 请求协议,例如 HTTP 或 HTTPS。
方法 varchar 请求方法,通常是GET或POST。
服务器名称 varchar 发出请求的服务器的名称。
事件ID int 访问事件的数据库 ID。
access_event_header 表包含每个请求的标头。信息组织如下图所示:
场地 类型 描述
事件ID int 对应访问事件的数据库id。
标头键 varchar 标头名称,例如 User-Agent。
标头值 varchar 标头值,例如 Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0
classic 的 DBAppender 的所有属性都可以在 access 的 DBAppender 中使用。后者提供了另一种选择,如下所述。
物业名称 类型 描述
插入标题 boolean 告诉 DBAppender 使用所有传入请求的标头信息填充数据库。
以下是使用 DBAppender 的示例配置。
示例:DBAppender 配置(logback-examples/src/main/resources/chapters/appenders/conf/access/logback-DB.xml)
<configuration>
<appender name="DB" class="ch.qos.logback.access.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://localhost:3306/logbackdb</url>
<user>logback</user>
<password>logback</password>
</connectionSource>
<insertHeaders>true</insertHeaders>
</appender>
<appender-ref ref="DB" />
</configuration>
筛选Appender
logback-access 中的 SiftingAppender 与 logback-classic 中的 SiftingAppender 非常相似。主要区别在于,在 logback-access 中,默认鉴别器(即 AccessEventDiscriminator)不是基于 MDC 的。顾名思义,AccessEventDiscriminator 使用 AccessEvent 中的指定字段作为选择嵌套附加程序的基础。如果指定字段的值为 null,则使用 defaultValue 属性中指定的值。
指定的AccessEvent字段可以是COOKIE、REQUEST_ATTRIBUTE、SESSION_ATTRIBUTE、REMOTE_ADDRESS、LOCAL_PORT、REQUEST_URI之一。请注意,前三个字段还要求指定AdditionalKey 属性。
下面是一个配置文件示例。
示例:SiftingAppender 配置(logback-examples/src/main/resources/chapters/appenders/conf/sift/access-siftingFile.xml)
<configuration>
<appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender">
<Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator">
<Key>id</Key>
<FieldName>SESSION_ATTRIBUTE</FieldName>
<AdditionalKey>username</AdditionalKey>
<defaultValue>NA</defaultValue>
</Discriminator>
<sift>
<appender name="ch.qos.logback:logback-site:jar:1.5.6" class="ch.qos.logback.core.FileAppender">
<file>byUser/ch.qos.logback:logback-site:jar:1.5.6.log</file>
<layout class="ch.qos.logback.access.PatternLayout">
<pattern>%h %l %u %t \"%r\" %s %b</pattern>
</layout>
</appender>
</sift>
</appender>
<appender-ref ref="SIFTING" />
</configuration>
在上面的配置文件中, SiftingAppender 嵌套了 FileAppender 实例。键“id”被指定为可供嵌套 FileAppender 实例使用的变量。默认鉴别器,即 AccessEventDiscriminator ,将在每个 AccessEvent 中搜索“用户名”会话属性。如果没有此类属性可用,则将使用默认值“NA”。因此,假设名为“username”的会话属性包含每个登录用户的用户名,则在(当前文件夹的)byUser/ 文件夹下将有一个以每个用户命名的日志文件,其中包含该用户的访问日志。
什么是编码器
编码器负责将事件转换为字节数组以及将该字节数组写入 OutputStream 。编码器是在 logback 版本 0.9.19 中引入的。在以前的版本中,大多数附加程序依赖布局将事件转换为字符串并使用 java.io.Writer 将其写出。在以前版本的 logback 中,用户将 PatternLayout 嵌套在 FileAppender 中。从 logback 0.9.19 开始, FileAppender 和子类需要编码器并且不再采用布局。
为什么要进行重大改变?
正如下一章详细讨论的,布局只能将事件转换为字符串,这将其范围限制为非二进制输出。
Mapped Diagnostic Contexts (MDC)第 8 章:映射的诊断上下文
logback 的设计目标之一是审计和调试复杂的分布式应用程序。大多数现实世界的分布式系统需要同时处理多个客户端。
在此类系统的典型多线程实现中,不同的线程将处理不同的客户端。
一种可能但不太鼓励的方法是将一个客户端的日志输出与另一个客户端的日志输出区分开来,包括为每个客户端实例化一个新的单独的记录器。
这种技术促进了记录器的激增,并可能增加其管理开销。
一种较轻的技术包括为给定客户端提供服务的每个日志请求进行唯一标记。 Neil Harrison 在 R. Martin、D. Riehle 和 F. Buschmann 编辑的《Patterns for Logging Diagnostic Messages in Pattern Languages of Program Design 3》一书中描述了这种方法(Addison-Wesley,1997 年)。 Logback 利用 SLF4J API 中包含的该技术的变体:映射诊断上下文 (MDC)。