zoukankan      html  css  js  c++  java
  • Logback中文文档(三):配置

    在第一部分,我们将介绍配置 logback 的各种方法,给出了很多配置脚本例子。在第二部分,我们将介绍 Joran,它是一个通用配置框架,你可以在自己的项目里使用 Joran。


    Logback里的配置

    把记录请求插入程序代码需要相当多的计划和努力。有观察显示大约 4%的代码是记录。
    所以即使是一个中等规模的应用程序也会包含数以千计的记录语句。考虑到数量庞大,我们需要使用工具来管理记录语句。
    Logback 可以通过编程式配置,或用 XML 格式的配置文件进行配置。
    Logback 采取下面的步骤进行自我配置:

    1. 尝试在 classpath 下查找文件 logback-test.xml;
    2. 如果文件不存在,则查找文件 logback.xml;
    3. 如果两个文件都不存在,logback 用 BasicConfigurator 自动对自己进行配置,这会导致记录输出到控制台。
      第三步也是最后一步是为了在缺少配置文件时提供默认(但基本的)记录功能。

    自动配置

    最简单的配置方法就是使用默认配置。
    BasicConfigurator 用法的简单例子:
    ((logback-examples/src/main/java/chapters/configuration/MyApp1.java))

    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.");
        }
    }
    

    该 类 定义 了一 个静 态变 量 logger ,然 后实 例 化一 个 Foo 对 象 。Foo 类 如 下
    ((logback-examples/src/main/java/chapters/configuration/Foo.java)):

    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!");
        }
    }
    

    假设配置文件 logback-test.xml 和 logback.xml 都不存在,那么 logback 默认地会调用BasicConfigurator , 创 建一 个 最小 化配 置 。最 小化 配置 由 一个 关联 到 根 logger 的ConsoleAppender 组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 的 PatternLayoutEncoder 进行格式化。还有,根 logger 默认级别是 DEBUG。
    因此,chapters.configuration.MyApp1 运行后的输出应当类似于:

    16:06:09.031 [main] INFO chapters.configuration.MyApp1 - Entering application.
    16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!
    16:06:09.046 [main] INFO chapters.configuration.MyApp1 - Exiting application.
    

    MyApp1 程序通过调用 org.slf4j.LoggerFactory 类和 org.slf4j.Logger 类连接到 logback,取得想 要的 logger, 然后 继续 。注 意 Foo 类 对 logback 唯 一的 依赖 是通 过引入org.slf4j.LoggerFactory 和 org.slf4j.Logger。

    用logback-test.xml 或 logback.xml自动配置

    前面提到过,如果 classpath 里有 logback-test.xml 或 logback.xml,logback 会试图用它进行自我配置。下面的配置文件与刚才的 BasicConfigurator 等效。
    示例:基本配置文件(logback-examples/src/main/java/chapters/configuration/sample0.xml)

    <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}
                    - %msg%n
                </pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    

    把 sample0.xml 重命名为 logback.xml 或 logback-test.xml,放到 classpath 里,运行后会和上例的输出几乎一样。

    自动打印警告和错误消息

      当解析配置文件有警告或出错时,logback 会在控制台上自动打印状态数据。如果没有警告或错误,你还是想检查 logback 的内部状态的话,可以调用 StatusPrinter 的 print()方法。
      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);
            logger.info("Entering application.");
            Foo foo = new Foo();
            foo.doIt();
            logger.info("Exiting application.");
        }
    

      如果一切顺利,控制台上会输出如下:

    17:44:58,578 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
    17:44:58,671 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
    17:44:58,671 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type  [ch.qos.logback.core.ConsoleAppender]
    17:44:58,687 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
    17:44:58,812  |-INFO  in ch.qos.logback.core.joran.action.AppenderAction  -  Popping appender named [STDOUT] from the object stack
    17:44:58,812 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG
    17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[root]
    17:44:58.828 [main] INFO chapters.configuration.MyApp2 - Entering application.
    17:44:58.828 [main] DEBUG chapters.configuration.Foo - Did it again!
    17:44:58.828 [main] INFO chapters.configuration.MyApp2 - Exiting application.
    

        在输出的最后面,你可以看到上例输出的内容。你也应当注意到 logback 的内部消息,也就是 Status 对象,它可以方便地访问 logback 的内部状态。
        可以不用从代码里调用 StatusPrinter,而是在配置文件里进行相关配置,即使没有出现错误。方法是,设置 configuration 元素的 debug 属性为 true。请注意 debug 属性只与状态数据有关,它不影响 logback 的配置,更不会影响记录级别。
        示例:debug 模式的基本配置(logback-examples/src/main/java/chapters/configuration/sample1.xml)

    <configuration debug="true">
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <!--
            encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder
            -->
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}
                    - %msg%n
                </pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>
    

        把 configuration 元素的 debug 属性设为 true 后,会输出状态信息,但是前提是:

    1. 找到了配置文件;
    2. 配置文件是格式化良好的 XML。
      如果其中任一条件未满足,Joran 就会因为配置文件不可读而无法读取 debug 属性。如果找到了配置文件,但却不是格式化良好的,那么 logback 会检测出错误并把内部状态打印到控制台。然而,如果找不到配置文件,由于这不是个严重的错误,logback 不会自动打印状态数据。使用编程式的主动调用 StatusPrinter.print()可以确保始终打印状态信息,如MyApp2。

    把默认配置文件的位置作为系统属性进行指定

    设置名为 logback.configurationFile 的系统属性,把默认配置文件的位置作为属性值,这种方法也可以。属性值即配置文件位置可以是个 URL、classpath 里的一个资源,或者是程序外部的文件路径。

    java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
    

    配置文件修改后自动重新加载

    如果设置成自动重新加载,logback-classic 会扫描配置文件里的变化,并且当发生变化后进行重新配置。设置访方法是设 configuration 元素的 scan 属性为 true。
    示例:扫描配置文件的变化并自动重新配置(logback-examples/src/main/java/chapters/configuration/scan1.xml)

    <configuration scan="true">
    ...
    </configuration>
    

    默认情况下,每隔一分钟扫描一次。configuration 元素的 scanPeriod 属性控制扫描周期,其值可以带时间单位,包括:milliseconds、seconds、minutes 和 hours。
    示例:指定不同的扫描周期(logback-examples/src/main/java/chapters/configuration/scan2.xml)

    <configuration scan="true" scanPeriod="30 seconds">
    ...
    </configuration>
    

    如果没写明时间单位,则默认为毫秒。

    内部实现是这样的,当设置扫描属性为 true 时,会安装一个叫 ReconfigureOnChangeFilter的 TurboFilter。每次调用 logger 的打印方法时,都会进行扫描。比如,当名为 myLogger 的logger执行“myLogger.debug("hello");”时,如果scan属性为true,则ReconfigureOnChangeFilter会被调用。而且,即使 myLogger 的 debug 级别被禁用了,仍然会调用上述过滤器。
    考虑到在任何 logger 在每次被调用时都要调用 ReconfigureOnChangeFilter,这个过滤器的性能就变得十分关键了。为提高性能,不会在每个 logger 被调用时去检查是否需要扫描,而是每隔 16 次记录操作进行一次检查。简言之,当配置文件改变后,它会被延时重新加载,延时时间由扫描间隔时间和一些 logger 调用所决定。

    直接调用 JoranConfigurator

    Logback 依赖 Joran,Joran 是 logback-core 的一部分,是个配置类库。Logback 的默认配置机制是调用JoranConfigurator对classpath上的默认配置文件进行处理。不管出于什么理由,如果你想重新实现 logback 的默认配置机制的话,你可以直接调用 JoranConfigurator。下面没的程序 MyApp3 就调用了 JoranConfigurator 对作为参数传入的配置文件进行处理。
    示例:直接调用 JoranConfigurator(logback-examples/src/main/java/chapters/configuration/MyApp3.java)

    package chapters.configuration;
    /**
     * Demonstrates programmatic invocation of Joran.
     */
    
    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;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    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 lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            try {
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(lc);
    // the context was probably already configured by default
    // configuration rules
                lc.reset();
                configurator.doConfigure(args[0]);
            } catch (JoranException je) {
    // StatusPrinter will handle this
            }
            StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
            logger.info("Entering application.");
            Foo foo = new Foo();
            foo.doIt();
            logger.info("Exiting application.");
        }
    }
    

    本程序直接取得 LoggerContext,创建新 JoranConfigurator 并设置它要操作的上下文,重置 logger 上下文,最后要求配置器用参数中的配置文件对上下文进行配置。同时打印了内部状态数据。

    查看状态消息

    Logback 把内部数据放在一个 StatusManager 对象里,并通过 LoggerContext 访问。
    StatusManager 通过 logback 上下文来访问所有数据对象。为把内存占用保持在合理的范围内,默认的 StatusManager 实现将状态消息按头和尾两部分存储。头部存储开始的 H 条状态消息,尾部存储后面的 T 条消息。现在的 H=T=150,将来或许会改变。
    Logback-classic 带了一个叫 ViewStatusMessagesServlet 的 Servlet,它以 HTML 表格的格式打印与当前 LoggerContext 关联的 StatusManager 的内容。示例如下。

    要加到自己的 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>
    

    访问地址是 http://host/yourWebapp/lbClassicStatus。

    监听状态消息

    你也可以为 StatusManager 附加一个 StatusListener,这样就能立即对状态消息作出响应,尤其对那些 logback 配置完成之后的消息。注册一个状态监听器可以方便地实现对 logback
    内部状态的无人监管。
    Logback 带了一个叫 OnConsoleStatusListener 的 StatusListener 实现,可以把状态消息打印到控制台。
    下例演示了如何为 StautsManager 注册一个 OnConsoleStatusListener 实例。

    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    StatusManager statusManager = lc.getStatusManager();
    OnConsoleStatusListener onConsoleListener = new
    OnConsoleStatusListener();
    statusManager.add(onConsoleListener);
    

    注意注册了的状态监听器只会接收被注册之后的状态消息,不会注册之前的消息。
    也可以在配置文件里注册一个或多个状态监听器。如下面的例子。
    示例:注册状态监听器(logback-examples/src/main/java/chapters/configuration/onConsoleStatusListener.xml)

    <configuration>
    <statusListener
    class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    ... the rest of the configuration file
    </configuration>
    

    还可以通过设置 Java 系统属性“logback.statusListenerClass”注册状态监听器,例如,

    java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
    

    配置文件语法

    到目前为止,正如你已经在这份手册里看到的不少例子,logback 允许你重新定义记录行为而不必重新编译你的代码。实际上,你可以轻易地配置 logback,比如禁用程序里某些地方的记录功能,或者直接输出到一个 UNIX 系统守护进程、数据库、日志查看器,或把记录事件发送到远程 logback 服务器,远程 logback 服务器按照其本地策略进行记录,比如把记录时间发送到第二个 logback 服务器。
    本节剩余部分介绍配置文件的语法。
    Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以开头,后面有零个或多个元素,有零个或多个元素,有最多一个元素。如下图所示:

    标记名大小写敏感性

    从 logback 0.9.17 版起,标记名不区分大小些。比如,<logger><Logger><LOGGER>都是合法元素且表示同一个意思。按照隐式规则,标记名除了首字母外要区分大小写。因此,<xyz><Xyz>等价,但不等价于<xYz>。隐式规则一般遵循 Java 世界里常用的驼峰命名规则。因为很难确定一个标记什么时候与显式动作相关,什么时候又与隐式动作相关,所以很难说 XML 标记是否是大小写敏感。如果你不确定标记名的大小写,就用驼峰命名法,基本不会错。

    配置logger或<logger>元素

    Logger 是用<logger>元素配置的。<logger>元素有且仅有一个 name 属性、一个可选的level 属性和一个可选的 additivity 属性。
    Level 属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、WARN、ERROR、ALL 和 OFF。还可以是一个特殊的字符串“INHERITED”或其同义词“NULL”,表示强制继承上级的级别。
    <logger>元素可以包含零个或多个<appender-ref>元素,表示这个 appender 会被添加到该 logger。强调一下,每个用元素声明的 logger,首先会移除所有 appender,然后才添加引用了的 appender,所以如果 logger 没有引用任何 appender,就会失去所有 appender。

    配置根logger,或<root>元素

    <root>元素配置根 logger。该元素有一个 level 属性。没有 name 属性,因为已经被命名
    为“ROOT”。
    Level 属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、WARN、ERROR、ALL 和 OFF。注意不能设置为“INHERITED” 或“NULL”。
    <logger>元素可以包含零个或多个<appender-ref>元素。与<logger>元素类似,声明<root>元素后,会先关闭然后移除全部当前 appender,只引用声明了的 appender。如果 root 元素没有引用任何 appender,就会失去所有 appender。

    示例

    假设我们不想看到“chapters.configuration”包里的任何组件的任何 DEBUG 信息,可以设置如下:
    示例:设置 logger 级别(logback-examples/src/main/java/chapters/configuration/sample2.xml)

    <configuration>
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <!--
            encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder
            -->
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %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”的 logger 生成的 DEBUG 级别的信息都被屏蔽了。
    你 可 以为 任意 数 量的 logger 设 置级 别。 下面 的 配置 文件 里, 我 们为 logger“chapters.configuration”设置级别为 INFO,同时设置 logger“chapters.configuration.Foo”级别为 DEBUG。
    示例:设置多个 logger 的级别

    <configuration>
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <!-- encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %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 进行配置后,各 logger 和各自级别如下表所示:

    Logger 名 分配级别 有效级别
    root DEBUG DEBUG
    chapters.configuration INFO INFO
    chapters.configuration.MyApp3 null INFO
    chapters.configuration.Foo DEBUG DEBUG

    MyApp3类的INFO级别的记录语句和Foo.doIt()的DEBUG消息都被启用。注意根 logger总是设为非 null 值,默认为 DEBUG。
    注意,基本选择规则依赖于被调用的logger的有效级别,而不是 appender所关联的 logger的级别。Logback 首先判断记录语句是否被启用,如果启用,则调用logger 等级里的 appender,无视 logger 的级别。配置文件 sample4.xml 演示了这一点。
    示例:logger 级别(logback-examples/src/main/java/chapters/configuration/sample4.xml)

    <configuration>
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <!--
            encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder
            -->
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                </pattern>
            </encoder>
        </appender>
        <logger name="chapters.configuration" level="INFO"/>
        <root level="OFF">
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    

    用 sample4.xml 对 logback 进行配置后,各 logger 和各自级别如下表所示:

    Logger 名 分配级别 有效级别
    root OFF OFF
    chapters.configuration INFO INFO
    chapters.configuration.MyApp3 null INFO
    chapters.configuration.Foo null INFO

    配置里唯一的 appender “STDOUT”,被关联到级别为 OFF 的根 logger,但是运行 MyApp3后会输出:

    17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
    17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.
    

    根 logger 的级别不起任何作用,是因为 chapters.configuration.MyApp3 里的 logger 和chapters.configuration.Foo 类的 INFO 级别都是启用的。附注:logger“chapters.configuration”即使没有任何 Java 代码直接引用它也会存在,全因为在配置文件里声明了它。

    配置 Appender

    Appender 用<appender>元素配置,该元素必要属性 name 和 class。
    name 属性指定 appender 的名称,class 属性指定 appender 类的全限定名。
    <appender>元素可以包含零个或多个<layout>元素、零个或多个<encoder>元素和零个或多个<filter>元素。除了这三个常用元素之外,还可以包含 appender 类的任意数量的 javabean属性。下图演示了常用结构,注意对 javabean 属性的支持在图中不可见。

    <layout>元素的 class 属性是必要的,表示将被实例化的 layout 类的全限定名。和<appender>元素一样, <layout>元素可以包含 layout 的javabean 属性。因为太常用了,所以当layout 是 PatternLayout 时,可以省略 class 属性。
    <encoder>元素 class 属性是必要的,表示将被实例化的 encoder 类的全限定名。因为太常用了,所以当当 encoder 是 PatternLayoutEncoder 时,可以省略 class 属性。
    记录输出到多个 appender 很简单,先定义各种 appender,然后在 logger 里进行引用,就行了。如下面的配置文件所示:
    示例:多个 logger (logback-examples/src/main/java/chapters/configuration/multiple.xml)

    <configuration>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>myApp.log</file>
            <!--
            encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder
            -->
            <encoder>
                <pattern>%date %level [%thread] %logger{10}
                    [%file:%line] %msg%n
                </pattern>
            </encoder>
        </appender>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%msg%n</pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="FILE" />
            <appender-ref ref="STDOUT" />
        </root>
    </configuration>
    

    该配置文件定义了两个 appender,分别是“FILE”和“STDOUT”。
    “ FILE” 这 个 appender 把 记 录 输 出 到 文 件 “ myapp.log ”, 它 的 encoder 是PatternLayoutEncoder,输出了日期、级别、线程名、logger 名、文件名及记录请求的行号、消息和行分隔符。
    “STDOUT”这个 appender 把记录输出到控制台,它的 encoder 只是输出消息和行分隔符。
    注意每个 appender 都有自己的 encoder。Encoder 通常不能被多个 appender 共享,layout也是。所以,logback 的配置文件里没有共享 encoder 或 layout 的语法。

    Appender累积

    默认情况下,appender 是可累积的:logger 会把记录输出到它自身的 appender 和它所有祖先的 appender。因此,把同一 appender 关联到多个 logger 会导致重复输出。
    示例:重复的 appender
    (logback-examples/src/main/java/chapters/configuration/duplicate.xml)

    <configuration>
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %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 - Entering application.
    14:25:36.343 [main] INFO chapters.configuration.MyApp3 - Entering application.
    14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application.
    14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application.
    

    看到重复输出了吧?名为 STDOUT 的 appender 被关联给两个 logger,分别是根和chapters.configuration。由于根 logger 是所有 logger 的祖先,hapters.configuration 是chapters.configuration.MyApp3 和 chapters.configuration.Foo 之父,所以这两个 logger 的记录请求会被输出两次。一次是因为 STDOUT 被关联到 chapters.configuration,一次是因为被关联到根 logger。
    Appender 的叠加性对新手来说并不是陷阱,反而是非常方便的。举例来说,你可以让某些系统里所有 logger 的记录信息出现在控制台,却让某些特定 logger 的记录信息发到一个特定的 appender。
    示例:多个 appender(logback-examples/src/main/java/chapters/configuration/restricted.xml)

    <configuration>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>myApp.log</file>
            <!--
            encoders are assigned by default the type
            ch.qos.logback.classic.encoder.PatternLayoutEncoder
            -->
            <encoder>
                <pattern>%date %level [%thread] %logger{10} [%file:%line] %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">
            <appender-ref ref="FILE"/>
        </logger>
        <root level="debug">
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    

    本例中,控制台 appender 会输出所有消息(出自系统里的所有 logger),但是只有来自chapters.configuration 这个 logger 的消息会输出到文件 myApp.log。

    覆盖默认的累积行为

    如果你觉得默认的累积行为不合适,可以设置叠加性标识为 false 以关闭它。这样的话,logger 树里的某个分支可以输出到与其他 logger 不同的 appender。
    示例:叠加性标识(logback-examples/src/main/java/chapters/configuration/additivityFlag.xml)

    <configuration>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>foo.log</file>
            <encoder>
                <Pattern>
                    %date %level [%thread] %logger{10} [%file : %line] %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>
    

    本例中,logger“chapters.configuration.Foo”关联 appender“FILE”,它的叠加性标记为false,这样它的记录输出仅会被发送到 appender“FILE”,不会被发送到更高 logger 等级关联的 appender。其他 logger 不受此影响。
    用 additivityFlag.xml 配 置 MyApp3 , 运 行 后 , 控 制 台 上 由 输 出 由“chapters.configuration.MyApp3”产生的记录。而 logger“ chapters.configuration.Foo”将且仅仅将输出到文件 foo.log。

    设置上下文名称

    每个 logger 都关联到 logger 上下文。默认情况下,logger 上下文名为“default”。但是你可以借助配置指令设置成其他名字。注意一旦设置 logger 上下文名称后,
    不能再改。设置上下文名称后,可以方便地区分来自不同应用程序的记录。
    示例:设置上下文名称并显示它(logback-examples/src/main/java/chapters/configuration/contextName.xml)

    <configuration>
        <contextName>myAppName</contextName>
        <appender name="STDOUT"
                  class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <Pattern>%d %contextName [%t] %level %logger{36} - %msg%n</Pattern>
            </encoder>
    
        </appender>
        <root level="debug">
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    

    本例演示了 logger 上下文的命名方法:在 layout 模式里添加“%contextName”就会输
    出上下文的名称。

    变量替换

    原则上,指定变量的地方就能够发生变量替换。变量替换的语法与 Unix shell 中的变量替换相似。位于“({”与“}”之间的字符串是键(key),取代键的值可以在同一配置文件里指定,也可以在外部文件或通过系统属性进行指定。例如,如果设系统属性“java.home.dir”为“/home/xyz”,那么每次当){java.home.dir}出现时都会被解释为“/home/xyz”。Logback自动定义了一个常用变量“${HOSTNAME}”。

    属性被插入 logger 上下文

    注意通过<property>元素定义的值实际上会被插入 logger 上下文。换句话说,这些值变成了 logger 上下文的属性。所以,它们对所有记录事件都可用,包括通过序列化方式被发送到远程主机的记录事件。
    下面的例子在配置文件的开头声明了一个变量又名替换属性,它代表输出文件的位置,然后在后面的配置文件里使用它。
    示例:简单变量替换(logback-examples/src/main/java/chapters/configuration/variableSubstitution1.xml)

    <configuration>
        <property name="USER_HOME" value="/home/sebastien"/>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>${USER_HOME}/myApp.log</file>
            <encoder>
                <pattern>%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/java/chapters/configuration/variableSubstitution2.xml)

    <configuration>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>${USER_HOME}/myApp.log</file>
            <encoder>
                <pattern>%msg%n</pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="FILE" />
        </root>
    </configuration>
    

    当需要很多变量时,更方便的做法是在一个单独的文件里声明所有变量,如下例所示。
    示例:文件变量替换(logback-examples/src/main/java/chapters/configuration/variableSubstitution3.xml)

    <configuration>
        <property
                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>%msg%n</pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="FILE"/>
        </root>
    </configuration>
    

    这个配置文件包含对文件“variables1.properties”的引用,该文件里的变量会被读入
    logback 配置文件的上下文里。
    文件“variables1.properties”内容类似于:
    示例:变量文件
    (logback-examples/src/main/java/chapters/configuration/variables1.properties)

    USER_HOME=/home/sebastien
    

    还可以不引用文件,而是引用 class path 上的资源。

    <configuration>
        <property resource="resource1.properties"/>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>${USER_HOME}/myApp.log</file>
            <encoder>
                <pattern>%msg%n</pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="FILE"/>
        </root>
    </configuration>
    

    嵌套变量替换

    Logback 支持嵌套变量替换。这里的嵌套是指变量的值里包含对其他变量的引用。假设你希望用变量指定目的地目录和文件名,然后用一个变量“destination”组合这两个变量,如下面所示。
    示例:嵌套变量替换(logback-examples/src/main/java/chapters/configuration/variables2.properties)

    USER_HOME=/home/sebastien
    fileName=myApp.log
    destination=${USER_HOME}/${fileName}
    

    注意在上面的属性文件里, “destination”由另外两个变量“USER_HOME”和“fileName”组合而成。
    示例:文件变量替换(logback-examples/src/main/java/chapters/configuration/variableSubstitution4.xml)

    <configuration>
        <property
                file="src/main/java/chapters/configuration/variables2.properties"/>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
            <file>${destination}</file>
            <encoder>
                <pattern>%msg%n</pattern>
            </encoder>
        </appender>
        <root level="debug">
            <appender-ref ref="FILE"/>
        </root>
    </configuration>
    

    变量的默认替换值

    在某些特定情况下,最好给变量一个默认值,以免变量未被声明或值为 null。Bash shell用“:-”指定默认值。例如,假设“aKey”未被声明,那么“${aKey:-golden}”将被解释为“golden”。

    HOSTNAME 属性

    HOSTNNAME 属性因为很常用,所以在配置过程中被自动定义。

    设置时间戳

    元素 timestamp 可以定义表示一个当前日期和时间的属性。

    在运行中定义属性

    可以配置文件里用 <define>元素定义属性。<define>元素有两个必要属性:name 和 class。
    name 属性代表属性的名称,class 属性代表 PropertyDefiner 接口的任意实现。PropertyDefiner接口的 getPropertyValue()方法返回的值就是属性值。
    如下例。

    <configuration>
        <define name="rootLevel"
                class="a.class.implementing.PropertyDefiner">
            <aProperty>of a.class.implementing.PropertyDefiner</aProperty>
        </define>
        <root level="${rootLevel}"/>
    </configuration>
    

    Logback 目前还没有提供 PropertyDefiner 接口的具体实现,只是为动态定义属性提供了一种方法。

    配置文件里的条件 化处理

    开发者经常需要针对不同的环境在不同的配置文件里换来换去,比如开发、测试和生产环境。这些配置文件大同小异。为避免重复劳动,logback 支持在配置文件里进行条件化处理,用这些元素可以让一个配置文件适用于多个环境。
    条件语句一般格式如下。

    <configuration>
        <!-- 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>
    </configuration>
    

    其中“condition”是 java 表达式,只允许访问上下文属性和系统属性。对于作为参数传入的键,property()方法或其等价的 p()方法将返回属性的字符串值。例如,想访问属性键为“k”的值,你可以用 property("k")或等价的 p("k")。如果键为“k”的属性未被定义,property方法将返回空字符串而不是 null,这样避免了检查 null 值。
    下一个例子里,ConsoleAppender 被关联到根 logger,但是前提条件是 HOSTNAME 属性的值是“torino”。注意名为“FILE”的 FileAppender 在任何情况下都被关联到根 logger。

    <configuration>
        <if condition='property("HOSTNAME").contains("torino")'>
            <then>
                <appender name="CON"
                          class="ch.qos.logback.core.ConsoleAppender">
                    <encoder>
                        <pattern>%d %-5level %logger{35} - %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} - %msg %n</pattern>
            </encoder>
        </appender>
        <root level="ERROR">
            <appender-ref ref="FILE"/>
        </root>
    </configuration>
    

    <configuration>元素之内的任何地方都支持条件化处理。也支持嵌套的 if-then-else 语句。
    然而,加入过多的条件语句会导致 XML 文件非常难读。

    从JNDI获取变量

    在某些特定情况下,你也许利用 JNDI 里存储的 env 项,指令会从JNDI里取得 env 项,然后用 as 属性把它们作为变量。
    示例:通过 JNDI 取得 env 项并作为属性插入(logback-examples/src/main/java/chapters/configuration/insertFromJNDI.xml)

    <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 %contextName %level %msg %logger{50}%n</pattern>
            </encoder>
        </appender>
        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
        </root>
    </configuration>
    

    本例中,env 项“java:comp/env/appName”被作为“appName”属性插入到配置文件。
    注意先是 <insertFromJNDI>指令插入的“appName”属性,然后<contextName>指令把“appName”的值设成“contextName”。

    文件包含

    Joran 支持在配置文件里包含其他文件。方法是声明元素,如下所示:
    示例:文件包含
    (logback-examples/src/main/java/chapters/configuration/containingConfig.xml)

    <configuration>
        <include
                file="src/main/java/chapters/configuration/includedConfig.xml"/>
        <root level="DEBUG">
            <appender-ref ref="includedConsole"/>
        </root>
    </configuration>
    

    被包含的文件必须把它的元素嵌套在元素里。例如,可以这样声明ConsoleAppender:
    示例:文件包含(logback-examples/src/main/java/chapters/configuration/includedConfig.xml)

    <included>
        <appender name="includedConsole"
                  class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>"%d - %m%n"</pattern>
            </encoder>
        </appender>
    </included>
    

    请再次注意元素是必需的。
    被包含的内容可以是文件、资源或 URL。

    • 作为文件
      用“file”属性包含一个文件。可以用相对路径,但是需要注意,当前目录是由应用程序决定的,与配置文件的路径必要的联系。
    • 作为资源
      用“resource”属性包含一个资源,也就是在 class path 上的文件。
      <include resource="includedConfig.xml" />
    • 作为 URL
      用“url”属性包括一个 URL。
      <include url="http://some.host.com/includedConfig.xml" />
  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/yw0219/p/9320846.html
Copyright © 2011-2022 走看看