以下内容翻译整理自logback官方手册,地址:logback官方手册
logback 配置
将日志请求插入应用程序代码需要相当多的计划和工作。观察表明,大约有4%的代码用于日志记录。因此,即使是一个中等大小的应用程序,其代码中也会包含数千条日志语句。考虑到它们的数量,我们需要工具来管理这些日志语句。
可以通过编程方式配置logback,也可以使用XML或Groovy格式表示的配置脚本配置logback。顺便说一下,通过使用我们的 PropertiesTranslator web应用程序,现有的log4j用户可以将他们的log4j.properties文件转换为logback.xml。
logback的初始化配置步骤:
Logback尝试在类路径中找到一个名为logback -test.xml的文件。
如果没有找到这样的文件,logback将尝试在类路径中找到一个名为logback.groovy的文件。
如果没有找到这样的文件,它将检查类路径中的logback.xml文件。
如果没有找到这样的文件, service-provider loading facility
(在 JDK 1.6 引入)通过在类路径中查找META-INF\services\ch. qs .logback.classic.spi.Configurator文件,用于解析com. qs .logback.classic.spi.Configurator接口的实现。它的内容应该指定所需配置程序的完全限定类名。
如果以上方法都不成功,logback将使用BasicConfigurator自动配置自己,这将导致日志输出定向到控制台。
最后一步是在没有配置文件的情况下提供默认(但非常基本)日志功能。
如果您正在使用Maven,并且将logback-test.xml放在src/test/resources文件夹下,Maven将确保它不会包含在生成的包中。因此,您可以使用不同的配置文件,在测试期间使用logback-test. xml,在生产中使用另一个文件logback.xml。
自动配置logback
配置logback的最简单方法是让logback使用其默认配置。
使用 BasicConfigurator 的简单示例
package com.wangbo.cto.logback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:54
* @auther wangbo
*/
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类如下所示:
package com.wangbo.cto.logback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:53
* @auther wangbo
*/
public class Foo {
static final Logger logger = LoggerFactory.getLogger(Foo.class);
public void doIt(){
logger.debug("Did it again!");
}
}
假设不存在 logback-test.xml或logback.xml配置文件,logback将默认调用BasicConfigurator设置一个最小配置。这个最小配置由一个附加到根logger的ConsoleAppender组成,使用PatternLayoutEncoder格式化输出。
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
此外,在默认情况下,根logger被指定为DEBUG级别。
因此,上面程序运行结果显示如下:
13:56:07.357 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
13:56:07.359 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
13:56:07.359 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
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所建立的配置文件等效。
基本配置文件:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
将上面的配置文件命名为logback.xml(或logback-test.xml)后,将其放入可以从类路径访问的目录中。运行 MyApp1 应用程序应该会得到与其前一次运行相同的结果。
如果发生警告或错误自动打印状态信息
如果在解析配置文件期间出现警告或错误,logback将自动在控制台上打印其内部状态消息。注意,为了避免重复,如果用户显式注册了状态侦听器(定义如下),则禁用自动状态打印。在没有警告或错误的情况下,如果仍然希望检查logback的内部状态,则可以通过调用StatusPrinter类的print()方法命令logback打印状态数据。只需要添加两行代码即可:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
下面我们可以新建一个类 MyApp2,运行观察日志信息。
package com.wangbo.cto.logback;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:54
* @auther wangbo
*/
public class MyApp2 {
final static Logger logger = LoggerFactory.getLogger(MyApp2.class);
public static void main(String[] args) {
//打印内部状态
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
执行结果:
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Programmer/projects/mytest/target/classes/logback.xml]
14:18:25,918 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
14:18:25,919 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
14:18:25,923 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
14:18:25,929 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
14:18:25,965 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
14:18:25,966 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@4f933fd1 - Registering current configuration as safe fallback point
14:18:25.968 [main] INFO com.wangbo.cto.logback.MyApp2 - Entering application.
14:18:25.969 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
14:18:25.969 [main] INFO com.wangbo.cto.logback.MyApp2 - Exiting application.
状态数据
您可以指示配置文件转储状态数据,而不是从代码中以编程方式调用StatusPrinter,即使在没有错误的情况下也是如此。要实现这一点,您需要设置configuration元素的debug属性,即配置文件中最上面的元素,如下所示。请注意,这个debug属性只与状态数据相关。它不会影响logback关于日志程序级别的配置。
使用调试模式的基本配置文件:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
在元素中设置debug="true"将输出状态信息。我们假定:
找到配置文件。
配置文件是格式良好的 XML。
如果这两个条件都没有满足,Joran 无法解释debug属性,因为配置文件无法读取。如果找到配置文件但格式不正确,然后logback将检测错误条件并在控制台上自动打印其内部状态。但是,如果找不到配置文件,logback不会自动打印其状态数据,因为这并不一定是一个错误条件。这种情况下以编程方式调用StatusPrinter.print()可以确保在每种情况下都打印状态信息。
强制状态输出
在没有状态消息的情况下,跟踪一个异常的logback.xml配置文件可能很困难,特别是在生产环境中,应用程序源代码不容易修改。为了帮助识别恶意配置文件的位置,你可以通过logback.statusListenerClass系统属性设置一个StatusListener来强制输出状态信息。logback.statusListenerClass系统属性还可用于在出现错误时自动生成的输出静默。
顺便说一下,设置debug="true"严格地等同于安装OnConsoleStatusListener。下面将进一步讨论状态侦听器。接下来展示OnConsoleStatusListener的安装。
注册状态侦听器:
... 配置文件的其余部分
通过设置debug属性或安装OnConsoleStatusListener,将在很大程度上帮助您诊断logback问题。因此,强烈建议启用logback状态数据,并应将其视为首选资源。
将默认配置文件的位置指定为系统属性
您可以使用名为“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 {
// 必须在第一次调用 LoggerFactory.getLogger(); 之前设置
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
...
}
}
public static final String CONFIG_FILE_PROPERTY = "logback.configurationFile";
修改后自动重新加载配置文件
如果指示这样做,logback-classic将扫描配置文件中的更改,并在配置文件更改时自动重新配置自己。为了指示logback-classic扫描配置文件中的更改并自动重新配置自己,将元素的scan属性设置为true,如下所示。
扫描配置文件中的更改和自动重新配置:
...
默认情况下,配置文件将每分钟扫描一次更改。您可以通过设置元素的scanPeriod属性来指定不同的扫描周期。
指定不同的扫描周期:
...
注意:如果没有指定时间单位,则假定时间单位为毫秒,这通常是不合适的。如果更改了默认扫描周期,请不要忘记指定时间单位。
在幕后,当您将scan属性设置为true时,将安装一个ReconfigureOnChangeTask,此任务在单独的线程中运行,并将检查配置文件是否已更改。ReconfigureOnChangeTask将自动监视文件的任何改动。
由于在编辑配置文件时很容易出错,如果配置文件的最新版本有XML语法错误,它将返回到没有XML语法错误的前一个配置文件。
启用堆栈跟踪中的打包数据
注意:从1.1.4版本开始,默认情况下将禁用打包数据。
如果指示这样做,logback可以包含它输出的堆栈跟踪行的每一行的打包数据。打包数据由jar文件的名称和版本组成,该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]
打包数据在默认情况下是禁用的,但可以通过配置启用:
...
或者,可以通过调用LoggerContext中的setPackagingDataEnabled(boolean)方法以编程方式启用/禁用打包数据,如下所示:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);
直接调用 JoranConfigurator
logback依赖于一个名为Joran的配置库,logback-core的一部分。logback的默认配置机制在类路径上找到的默认配置文件上调用JoranConfigurator。无论出于什么原因,如果您希望覆盖logback的默认配置机制,您可以通过直接调用JoranConfigurator来实现这一点。
直接调用JoranConfigurator:
package com.wangbo.cto.logback;
import ch.qos.logback.access.joran.JoranConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 22:01
* @auther wangbo
*/
public class MyApp3 {
final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
public static void main(String[] args) {
//假设SLF4J绑定到当前环境中的logback
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
//调用context.reset()清除任何以前的配置,比如默认配置。
//对于多步骤配置,省略调用context.reset()。
context.reset();
configurator.doConfigure(args[0]);
} catch (JoranException e) {
//StatusPrinter将处理这个问题
}
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
这个应用程序获取当前有效的LoggerContext,创建一个新的JoranConfigurator,设置将在其上操作的上下文,重置记录器上下文,然后最后要求配置器使用作为参数传递给应用程序的配置文件来配置上下文。如果出现警告或错误,将打印内部状态数据。注意,对于多步骤配置,应该省略context.reset()调用。
查看状态信息
Logback将其内部状态数据收集到StatusManager对象中,通过LoggerContext进行访问。
给定StatusManager,您可以访问与logback上下文关联的所有状态数据。为了使内存使用保持在合理的水平,默认的StatusManager实现将状态消息存储在两个单独的部分:头部和尾部。头部分存储第一个H状态消息,而尾部分存储最后一个T消息。目前H=T=150,尽管这些值在将来的版本中可能会改变。
Logback-classic附带一个名为ViewStatusMessagesServlet的 servlet。这个 servlet 将StatusManager中与当前LoggerContext关联的内容打印为HTML表。这是样本输出。
lbClassicStatus.jpg
要将此 servlet 添加到 web 应用程序中,请将以下行添加到其 WEB-INF/web.xml 文件中。
ViewStatusMessages
ch.qos.logback.classic.ViewStatusMessagesServlet
ViewStatusMessages
/lbClassicStatus
ViewStatusMessages servlet 可以通过 URL http://host/yourWebapp/lbClassicStatus访问。
监听状态消息
您还可以将StatusListener附加到StatusManager,以便您可以立即响应状态消息,特别是在回退配置之后发生的消息。注册状态侦听器是一种方便的方法,可以在不需要人工干预的情况下监视登录的内部状态。
Logback附带一个名为OnConsoleStatusListener的StatusListener实现,顾名思义,它将在控制台打印所有新的传入状态消息。
下面是向StatusManager注册OnConsoleStatusListener实例的示例代码。
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusManager statusManager = lc.getStatusManager();
OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
statusManager.add(onConsoleListener);
注意,已注册的状态侦听器只会在注册之后接收状态事件。它将不会接收先前的消息。因此,通常最好将状态侦听器注册指令放在配置文件的顶部,然后才是其他指令。
还可以在配置文件中注册一个或多个状态侦听器。这里有一个例子。
注册状态侦听器:
... 配置文件的其余部分
logback.statusListenerClass 系统特性
还可以通过设置“logback”来注册状态侦听器。将Java系统属性设置为要注册的侦听器类的名称。例如:
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
Logback附带几个状态侦听器实现。OnConsoleStatusListener在控制台打印输入状态消息,即在System.out上打印。OnErrorConsoleStatusListener在System.err上打印传入的状态消息。NopStatusListener删除传入的状态消息。
注意,如果在配置期间注册了任何状态侦听器,特别是如果用户通过logback.statusListenerClass指定了一个状态侦听器,则禁用自动状态打印(以防出现错误)。因此,通过将NopStatusListener设置为状态侦听器,可以完全禁用内部状态打印。
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...
停止 logback-classic
为了释放logback-classic使用的资源,停止logback上下文总是一个好主意。停止上下文将关闭附加到上下文定义的日志记录器的所有附加程序,并以有序的方式停止任何活动线程。请阅读下面关于“关机挂钩”的部分。
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
// 假设SLF4J在当前环境中绑定到logback-classic
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
在web应用程序中,可以从ServletContextListener的contextDestroyed方法中调用上面的代码,以停止logback-classic并释放资源。从1.1.10版本开始,将自动为您安装适当的ServletContextListener(见下面)。
通过关闭挂钩停止 logback-classic
安装JVM关机挂钩是关闭登录和释放相关资源的一种方便方法。
....
请注意,您可以通过将 class 属性设置为与关机钩子的类名相对应来安装自己制作的关机钩子。
默认的关闭钩子,即 DefaultShutdownHook,将在指定的延迟(默认为0)之后停止logback上下文。停止上下文将允许在后台运行的任何日志文件压缩任务在30秒内完成。在独立的 Java 应用程序中,向配置文件中添加指令是确保允许在 JVM 退出之前完成任何正在进行的压缩任务的简单方法。在Web服务器中的应用程序中,webShutdownHook将自动安装,使指令相当冗余和不必要。
web应用程序中的WebShutdownHook或stop logback-classic
自1.1.10以后, Logback-classic将自动要求web服务器安装一个LogbackServletContainerInitializer来实现ServletContainerInitializer接口(可在servlet-api 3.x和更高的版本中获得)。
通过在 web 应用程序的web.xml文件中设置一个名为logbackDisableServletContainerInitializer的。下面是相关的代码片段。
logbackDisableServletContainerInitializer
true
....
注意,logbackDisableServletContainerInitializer变量也可以设置为 OS 环境变量的 Java 系统属性。最本地的设置具有优先级,即 web-app 第一,系统属性第二,操作系统环境最后。
配置文件的语法
正如您在手册中所看到的,还有很多例子需要跟随,logback允许您重新定义日志记录行为,而不需要重新编译代码。事实上,您可以很容易地配置logback以禁用应用程序的某些部分日志,或直接输出到UNIX 系统日志守护进程,到一个数据库,到一个日志可视化工具,或将日志事件转发给远程logback服务器,根据本地服务器策略进行日志记录,例如,通过将日志事件转发到第二个logback服务器。
本节的其余部分介绍配置文件的语法。
我们将一遍又一遍地演示,logback配置文件的语法非常灵活。因此,不可能使用DTD文件或XML模式指定允许的语法。然而,配置文件的基本结构可以描述为,元素,包含零个或多个元素,然后是零个或多个元素,随后是元素,下图说明了这个基本结构。
标签名称的大小写敏感性
自从logback 0.9.17版本之后,显式规则下标签名称不区分大小写。例如,、和都是有效的配置元素,将以相同的方式解释。注意,XML格式规则仍然适用,如果你打开一个标签为你必须关闭它为,而不能使用作为关闭标签。对于隐式规则,除了第一个字母外,标记名是区分大小写的。因此,和是等价的,但不等价。隐式规则通常遵循驼峰命名规则,这在 Java 世界中很常见。由于很难区分标记何时与显式操作关联,何时与隐式操作关联,XML 标记对于第一个字母是区分大小写的还是不区分大小写的,这一点很重要。如果您不确定对给定的标记名使用哪种情况,请遵循驼峰命名规则,它几乎总是正确的约定。
配置日志记录器,或元素
此时,您至少应该对级别继承和基本选择规则有一些了解。日志程序是使用元素配置的。一个元素只接受一个强制的name属性、一个可选的level属性和一个可选的additivity属性。level属性的值允许不区分大小写的字符串值:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。不区分大小写的特殊值 INHERITED,或其同义词NULL,将强制从层次结构的较高层次继承日志程序的级别。如果您设置了日志程序的级别,然后决定它应该继承其级别,那么这将非常方便。
元素可以包含零个或多个元素;这样引用的每个追加器都被添加到指定的日志程序中。注意,与log4j不同,logback-classic在配置给定的日志程序时不会关闭或删除任何以前引用的附加程序。
配置根记录器,或元素
元素配置根记录器。它支持单个属性,即level属性。它不允许任何其他属性,因为additivity标志不适用于根记录器。此外,由于根日志记录器已经被命名为“ROOT”,所以它也不允许使用name属性。level属性的值可以是不区分大小写的字符串之一:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。注意,根日志程序的级别不能设置为INHERITED或NULL。
与元素类似,元素可以包含零个或多个元素;这样引用的每个追加器都被添加到根日志记录器中。注意,与log4j不同,logback-classic在配置根日志程序时不会关闭或删除任何以前引用的附加程序。
示例
设置日志程序或根日志程序的级别与声明和设置其级别一样简单,正如下一个例子所示。假设我们不再有兴趣去看属于com.wangbo.cto.logback包的任何组件的任何调试信息。下面的配置文件展示了如何实现这一点。
设置日志程序的级别:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
当将上述配置文件作为 MyApp1 应用程序的参数时,它将产生以下输出:
23:44:53.876 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
23:44:53.877 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
注意,只有info级别信息执行了。“Foo”类中的debug级别日志未打印。
您可以根据需要配置任意多的日志记录器的级别。在下一个配置文件中,我们设置了包的日志级别info,也设置了Foo类的日志级别debug,
设置多个日志记录器的级别:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
执行 MyApp1,打印结果:
23:51:49.116 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
23:51:49.118 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
23:51:49.118 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
下表列出了JoranConfigurator使用sample3.xml配置文件配置logback之后的日志记录器及其级别。
Logger name
指定级别
有效级别
root
DEBUG
DEBUG
com.wangbo.cto.logback
INFO
INFO
com.wangbo.cto.logback.MyApp1
null
INFO
com.wangbo.cto.logback.Foo
DEBUG
DEBUG
因此,MyApp1类中的两个INFO日志语句以及Foo.doIt()中的DEBUG消息都是启用的。注意,根日志程序的级别总是设置为非空值,默认情况下为DEBUG。
让我们注意,基本选择规则取决于被调用的日志程序的有效级别,不是附加程序所在的记录器级别。Logback将首先确定是否启用了日志语句,如果启用,它将调用logger层次结构中找到的appenders,而不管它们的级别如何。下面的配置文件就是一个例子:
class="ch.qos.logback.core.ConsoleAppender">
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
sui
下表列出了应用 sample4.xml配置文件后的日志记录器及其级别。
Logger name
指定级别
有效级别
root
OFF
OFF
com.wangbo.cto.logback
INFO
INFO
com.wangbo.cto.logback.MyApp1
null
INFO
com.wangbo.cto.logback.Foo
null
INFO
名为STDOUT的ConsoleAppender,是配置文件中唯一配置的追加器,附加到根记录器,其级别设置为OFF,但是,运行 MyApp1 将产生以下结果:
00:01:55.395 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
00:01:55.397 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
因此,根日志记录器的级别没有明显的影响,因为在com.wangbo.cto.logback中有日志记录器。MyApp3 和 Foo 类的日志级别都是INFO。
配置 Appenders
追加器使用元素配置,它接受两个强制属性name和class。name属性指定追加器的名称,class属性指定要实例化的appender类的完全限定名。元素可以包含零个或一个元素,零个或多个元素,零个或多个元素。除了这三个通用的元素,元素可以包含与appender类的JavaBean属性相对应的任意数量的元素。无缝地支持给定logback组件的任何属性是Joran的主要优势之一,这将在后面的章节中讨论。下图说明了常见的结构。注意,对属性的支持是不可见的。
元素接受一个强制性的class属性,该属性指定要实例化的布局类的完全限定名。与元素一样,可能包含与layout实例属性相对应的其他元素。因为这是很常见的情况,如果布局类是PatternLayout,可以省略class属性。按照默认的类映射规则指定。
登录到多个appenders就像定义不同的appenders并在日志程序中引用它们一样简单,如下面的配置文件所示:
多个日志实例:
myApp.log
%date %level [%thread] %logger{10} [%file:%line] %msg%n
%msg%n
上面的配置文件定义了两个appenders,分别称为 FILE和STDOUT。FILE 追加日志到一个myApp.log的文件。此附加程序的编码器是PatternLayoutEncoder,它输出日期、级别、线程名称、日志程序名称、文件名称和日志请求所在的行号、消息和行分隔符。第二个追加器称为STDOUT输出到控制台。此附加程序的编码器仅输出消息字符串和行分隔符。
通过在appender-ref元素中按名称引用附加程序,附加程序被附加到根日志程序。注意,每个附加器都有自己的编码器。编码器通常不设计为多个附加程序共享,布局也是如此,因此,logback配置文件不提供共享编码器或布局的任何语法方法。
Appenders 累积
默认情况下,附加程序是累积的:日志记录器将记录到附加到其自身(如果有的话)的附加程序,以及附加到其祖先的所有附加程序。因此,将相同的追加器附加到多个日志记录器将导致日志重复输出。
重复的appender:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
修改配置文件为上面的配置,运行 MyApp1 程序,结果如下:
10:35:46.197 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.197 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
10:35:46.198 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
注意重复的输出。名为STDOUT的附加程序被附加到两个日志记录器,root和com.wangbo.cto.logback。因为根记录器是所有日志记录器的祖先,com.wangbo.cto.logback记录器又是com.wangbo.cto.logback.MyApp1和com.wangbo.cto.logback.Foo的父级日志记录器,使用这两个日志记录器发出的每个日志请求都将输出两次,一次是com.wangbo.cto.logback输出的,另一次是root输出的。
附加程序的可累加性并不是为新用户设计的陷阱。这是一个非常方便的logback特性。例如,您可以配置日志记录,使日志消息出现在控制台上(对于系统中的所有日志记录程序),而只有来自特定日志记录程序集的消息流进特定的附加程序。
多个appender:
myApp.log
%date %level [%thread] %logger{10} [%file:%line] %msg%n
%msg%n
在本例中,console appender将记录所有消息(对于系统中的所有日志记录器),而file appender只记录来自com.wangbo.cto.logback请求进入myApp.log文件。
覆盖默认的累积行为
如果默认的累积行为不适合您的需要,您可以通过将累加标志设置为false来覆盖它。因此,日志记录器树中的分支可以将输出直接输出到一组与树的其他部分不同的追加器。
累加性标志:
foo.log
%date %level [%thread] %logger{10} [%file : %line] %msg%n
%msg%n
在本例中,名为FILE的附加器被附加到com.wangbo.cto.logback.Foo记录器。此外,com.wangbo.cto.logback.Foo记录器的可累加标记设置为false,这样它的日志输出将只会被发送到foo.log文件中,而不会发送到层次结构中更高的附录中(对于本例也就是说不会被打印在控制台上)。
运行 MyApp1,控制台打印结果如下:
Entering application.
Exiting application.
foo.log文件记录如下:
2019-09-15 10:54:13,667 DEBUG [main] c.w.c.l.Foo [Foo.java : 14] Did it again!
设置上下文名称
如前一章所述,每个日志程序都附加到一个日志程序上下文。默认情况下,日志记录器上下文称为“default”。但是,您可以使用配置指令设置一个不同的名称。注意,一旦设置好,日志程序上下文名称将无法更改。设置上下文名称是一种简单而直接的方法,用于区分多个应用程序对同一目标的日志记录。
设置上下文名称并显示它:
myAppName
%d %contextName [%t] %level %logger{36} - %msg%n
最后一个示例演示了日志记录器上下文的命名。在layout的模式中添加contextName转换词将输出所述名称。
运行 MyApp1,结果如下:
2019-09-15 11:05:09,344 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
2019-09-15 11:05:09,345 myAppName [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
2019-09-15 11:05:09,345 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
可以看出,上下文名称myAppName被输出了。
变量替换
该文档的早期版本使用了术语“属性替换”而不是术语“变量”。请考虑这两个术语是可以互换的,尽管后者表达了更清晰的含义。
与许多脚本语言一样,logback配置文件支持定义和替换变量。变量有一个作用域(见下面)。此外,变量可以在配置文件本身、外部文件、外部资源中定义,甚至可以动态计算和定义。
变量替换可以发生在配置文件中指定值的任何位置。变量替换的语法类似于 Unix shell 的语法。开始${和结束}之间的字符串被解释为对属性值的引用。对于属性aName,字符串“${aName}”将替换为aName属性所持有的值。
由于它们经常会派上用场,主机名HOSTNAME和CONTEXT_NAME变量是自动定义的,并且具有上下文作用域。考虑到在某些环境中计算主机名可能需要一些时间,它的值是延迟计算的(仅在需要时)。此外,也可以直接在配置中设置主机名。
定义变量
可以在配置文件本身中一次定义一个变量,也可以从外部属性文件或外部资源批量加载变量。由于历史原因,用于定义变量的XML元素是,尽管在logback 1.0.7和以后的版本中,元素可以互换使用。
下一个示例显示配置文件开头声明的变量。然后在文件的后面使用它来指定输出文件的位置。
简单的变量替换:
${USER_HOME}/myApp.log
%msg%n
下一个示例展示了如何使用系统属性来实现相同的结果。该属性没有在配置文件中声明,因此logback将在系统属性中查找它。Java系统属性可以在命令行上设置,如下所示:
java -DUSER_HOME="/home/sebastien" MyApp2
系统变量替换:
${USER_HOME}/myApp.log
%msg%n
当需要多个变量时,创建一个包含所有变量的单独文件可能更方便。下面是如何进行这样的设置。
使用单独的文件替换变量:
${USER_HOME}/myApp.log
%msg%n
这个配置文件包含对一个名为variables1.properties文件的引用。该文件中包含的变量将被读取,然后在本地范围内定义。这是变量。属性文件可能看起来像下面这样。
USER_HOME=/home/sebastien
您还可以在类路径上引用资源,而不是引用文件。
${USER_HOME}/myApp.log
%msg%n
作用域
属性可以定义为插入到本地范围、上下文范围或系统范围中。本地范围是缺省值。虽然可以从 OS 环境中读取变量,但不可能写入 OS 环境。
局部作用域:具有局部作用域的属性从其定义开始存在于配置文件中,直到所述配置文件的解释/执行结束。因此,每次解析和执行配置文件时,局部范围内的变量都会重新定义。
上下文作用域:具有上下文作用域的属性被插入到上下文中,并持续到上下文被清除为止。一旦定义了上下文范围中的属性,它就是上下文的一部分。因此,它在所有日志事件中都可用,包括那些通过序列化发送到远程主机的日志事件。
系统作用域:具有系统范围的属性被插入到JVM的系统属性中,并在JVM运行期间或清除之前一直有效。
在替换期间,首先在本地范围内查找属性,然后在上下文范围内查找属性,最后在系统属性范围内查找属性,最后在 OS 环境中查找属性。
可以使用元素的scope属性,元素或元素的scope属性设置属性的范围。scope属性允许“local”、“context” 和 “system”字符串作为可能的值。如果没有指定,则默认为“local”。
在“上下文”范围中定义的变量:
/opt/${nodeId}/myApp.log
%msg%n
在上面的例子中,假定nodeId属性是在上下文范围中定义的,它将在每个日志事件中可用,即使是那些通过序列化发送到远程主机的日志事件。
变量的默认值
在某些情况下,如果变量没有声明或其值为null,则可能希望它具有默认值。与在Bash shell中一样,可以使用“:-”操作符指定默认值。例如,假设没有定义名为aName的变量,“${aName:-golden}”将被解释为“golden”。
嵌套的变量
完全支持变量嵌套。变量的名称、默认值和值定义都可以引用其他变量。
值嵌套
变量的值定义可以包含对其他变量的引用。假设您希望使用变量不仅指定目标目录,还指定文件名,并将这两个变量组合到名为“destination”的第三个变量中。下面显示的属性文件给出了一个示例。
嵌套变量引用:
USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}
注意,在上面的属性文件中,“destination”由另外两个变量组成,即“USER_HOME”和“fileName”。
配置文件如下:
${destination}
%msg%n
名字嵌套
在引用变量时,变量名可能包含对另一个变量的引用。例如,如果名为“userid”的变量被赋值为“alice”,那么"${${userid}.password}"就是引用名为“alice.password”的变量。
默认值嵌套
变量的默认值可以引用另一个变量。例如,假设变量'id'未赋值,变量'userid'被赋值为"alice",那么表达式"${id:-${userid}}"将返回"alice"。
HOSTNAME 属性
由于HOSTNAME属性通常很方便,所以在配置过程中会使用上下文范围自动定义它。
CONTEXT_NAME 属性
正如其名称所示,CONTEXT_NAME属性对应于当前日志上下文的名称。
设置一个时间戳
timestamp元素可以根据当前日期和时间定义属性。时间戳元素将在下一章中解释。
动态定义属性
可以使用元素动态定义属性。define元素接受两个强制属性:name和class。name属性指定要设置的属性的名称,而class属性指定实现PropertyDefiner接口的任何类。PropertyDefiner实例的getPropertyValue()方法返回的值将是命名属性的值。您还可以通过指定scope属性为指定的属性设定范围。
round
brown
24
在上面的例子中,形状、颜色和大小是“a.class.implementing.PropertyDefiner”的属性。只要在PropertyDefiner实例的实现中有一个给定属性的setter方法,logback会将配置文件中指定的属性值注入到实例的属性中。
目前,logback附带了PropertyDefiner的两个相当简单的实现。
实现类
描述
CanonicalHostNamePropertyDefiner
将命名变量设置为本地主机的规范主机名。注意,获得规范主机名可能需要几秒钟。
FileExistsPropertyDefiner
如果path属性指定的文件存在,则将命名变量设置为“true”,否则设置为“false”。
ResourceExistsPropertyDefiner
如果用户指定的资源在类路径上可用,则将命名变量设置为“true”,否则设置为“false”。
配置文件的条件处理
开发人员经常需要在几个针对不同环境(如开发、测试和生产)的logback配置文件之间来回切换。这些配置文件有很多共同之处,只是在一些地方有所不同。为了避免重复,logback在、和元素的帮助下支持配置文件的条件处理,这样一个配置文件就可以用于对多个环境。注意,条件处理需要Janino库。
条件语句的一般格式如下所示。
...
...
...
该条件是一个 Java 表达式,其中只能访问上下文属性或系统属性。对于作为参数传递的键,property()或其简写p()方法返回该属性的字符串值。例如,要访问键为“k”的属性的值,您可以编写propert(“k”)或等价的p(“k”)。如果名称为"k"的属性未定义,属性方法将返回空字符串,而不是null。这避免了检查空值的需要。
isDefined()方法可用于检查是否定义了属性。例如,要检查属性“k”是否已定义,可以使用isDefined(“k”),如果需要检查属性是否为null,提供了isNull()方法。例如:isNull (“k”)。
%d %-5level %logger{35} - %msg %n
${randomOutputDir}/conditional.log
%d %-5level %logger{35} - %msg %n
元素中的任何位置都支持条件处理。还支持嵌套if-then-else语句。然而,XML语法非常麻烦,不适合作为通用编程语言的基础。因此,太多的条件将很快使后续读者(包括您自己)无法理解您的配置文件。
从JNDI获取变量
在某些情况下,您可能希望使用 JNDI 中存储的env-entries。配置指令提取 JNDI 中存储的一个env-entry,并用as属性指定的键插入本地范围中的属性。所有属性,在scope属性的帮助下,可以将新属性插入不同的范围。
作为通过JNDI获得的属性env-entry插入:
${appName}
%d ${CONTEXT_NAME} %level %msg %logger{50}%n
在最后一个示例中,“java:comp/env/appName”作为appName属性插入。注意,指令根据前面的指令插入的appName属性的值设置上下文名称。
文件包含
Joran 支持从另一个文件中包含配置文件的部分。这是通过声明一个元素来实现的,如下所示:
目标文件必须将其元素嵌套在元素中。例如,一个ConsoleAppender可以声明为:
"%d - %m%n"
再次强调,必须使用元素。
要包含的内容可以作为文件、资源或URL引用。
作为一个文件:
要包含文件,请使用file属性。您可以使用相对路径,但是请注意,当前目录是由应用程序定义的,并不一定与配置文件的路径相关。
作为一个资源:
为了包含资源,比如在类路径上找到一个文件,使用resource属性。
作为一个 URL:
要包含 URL 的内容,请使用 URL 属性。
如果找不到要包含的文件,logback将通过打印状态消息来提示。如果包含的文件是可选的,您可以通过将元素中的可选属性设置为true来抑制警告消息。
添加上下文侦听器
LoggerContextListener接口的实例侦听与日志记录器上下文的生命周期相关的事件。
JMXConfigurator是LoggerContextListener接口的一个实现。它将在下一章进行描述。
#######LevelChangePropagator
从0.9.25版开始,logback-classic附带LevelChangePropagator,这是LoggerContextListener的实现,它将任何logback-classic日志程序级别的更改传播到java.util.logging框架。这种传播消除了禁用日志语句的性能影响。LogRecord实例将仅通过启用的日志语句发送到logback(通过SLF4J)。这使得实际应用程序可以合理地使用jul-to-slf4j桥接。
contextListener元素可用于安装LevelChangePropagator,如下所示。
....
设置LevelChangePropagator的resetJUL属性将重置所有j.u.l.日志记录器的所有以前的级别配置。但是,以前安装的处理程序将保持不变。
true
....