2

前言

对于配置文件中的具体模块——如logback.xml中的<appender></appender>这个具体的关于配置Appender的元素标签——本篇并不记录,而会记录到它们所涉及的组件的笔记中去。

本盘主要关注以配置文件整个整体为考察对象的内容,以及logback.xml的根元素标签configuration这种级别的内容的配置。

正文

logback定位和读取配置文件的流程

这小节对应的官方文档已经正式讲到了配置文件,所以从这小节往后的笔记以及对应的官方文档内容,都要有一个意识:现在讲的东西,多是配置在配置文件里的,并且一般是用在用在logback.xml这个配置文件里的。

chapter s3

Let us begin by discussing the initialization steps that logback follows to try to configure itself:

  1. Logback tries to find a file called logback-test.xml in the classpath.
  2. If no such file is found, logback tries to find a file called logback.groovy in the classpath.
  3. If no such file is found, it checks for the file logback.xml in the classpath..
  4. If no such file is found,service-provider loading facility (introduced in JDK 1.6) is used to resolve the implementation of com.qos.logback.classic.spi.Configurator interface by looking up the file META-INF\services\ch.qos.logback.classic.spi.Configurator in the class path. Its contents should specify the fully qualified class name of the desired Configurator implementation.
  5. If none of the above succeeds, logback configures itself automatically using the BasicConfigurator which will cause logging output to be directed to the console.

The last step is meant as last-ditch effort to provide a default (but very basic) logging functionality in the absence of a configuration file.

这一段主要讲了logback定位它的配置文件流程和在有多个可用的配置文件下不同配置文件的优先级。

第四步听着很唬人,但实践中一般用不到。要么啥配置都没有,就干巴巴的用第五步的相当简单的默认配置。要么就自己写好了 logback.xml 等着被读取并配置。

If you are using Maven and if you place the logback-test.xml under the src/test/resources folder, Maven will ensure that it won't be included in the artifact produced. Thus, you can use a different configuration file, namely logback-test.xml during testing, and another file, namely, logback.xml , in production.

这一段说明在使用maven的情况下,由于maven会确保测试环境和运行环境的分离,可以与如何为测试环境和运行环境使用两份logback配置。虽然后者(运行环境)没仔细说,但其实就是读取配置的顺序时的第二步或第三步(因为测试和运行的分离,text-logback.xml即便存在也不会干扰运行环境时进行到第二步或第三步)。

FAST START-UP t takes about 100 miliseconds for Joran to parse a given logback configuration file. To shave off those miliseconds at aplication start up, you can use the service-provider loading facility (item 4 above) to load your own custom Configurator class with BasicConfigrator serving as a good starting point.

最后讲了如何加快带有logback的软件的启动速度。但涉及的知识点在于service-provider loading facility,此处不多做讨论。

默认配置

chapter 3

Assuming the configuration files logback-test.xml or logback.xml are not present, logback will default to invoking BasicConfigurator which will set up a minimal configuration. This minimal configuration consists of a ConsoleAppender attached to the root logger. The output is formatted using a PatternLayoutEncoder set to the pattern %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n. Moreover, by default the root logger is assigned the DEBUG level.

默认配置的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>

自定义配置文件的路径

chapter 3

对应标题为"Specifying the location of the default configuration file as a system property"的整个小节。

官方的两个例子

例子1

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

例子2

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

通过设置logback.configurationFile的系统变量,可以修改logback定位(在此属性被赋值的情况下,其是最高优先级)的配置文件的路径。
但其必需在logback的任何Logger被加载出来前修改。

logback自动重载配置功能的一些说明

chapter 3

对应标题为“Automatically reloading configuration file upon modification”的整个小节。

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

By default, the configuration file will be scanned for changes once every minute. You can specify a different scanning period by setting the scanPeriod attribute of the <configuration> element. Values can be specified in units of milliseconds, seconds, minutes or hours. Here is an example:

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

If no unit of time is specified, then the unit of time is assumed to be milliseconds, which is usually inappropriate. If you change the default scanning period, do not forget to specify a time unit.

As it is easy to make errors while editing a configuration file, in case the latest version of the configuration file has XML syntax errors, it will fall back to a previous configuration file free of XML syntax errors.

我之前一直认为是一旦修改配置文件则重载配置,这导致我几次调整配置文件而不重启应用,试图看看自己设置后的配置文件带给logback什么变化。
但细读这段话,其实logback不能立刻知道配置文件是否被修改了(也是,它归根结底一个jar包,凭什么能知道),它只是每隔一段时间去扫描比对,比对出有没有被修改。

最后它说开启扫描后自动,如果发现修改后的配置文件有问题,则会回退到最近的无问题的配置文件。

让logback记录异常时,额外记录异常栈中每一行所在的jar包信息

chapter 3

对应于标题为“Enabling packaging data in stack traces”的整个小节。

通过

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

或者

  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
  lc.setPackagingDataEnabled(true);

开启输出packaging data数据。

何为packaging data?

Packaging data consists of the name and version of the jar file whence the class of the stack trace line originated.
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]

这是官方文档中给出的示例,该示例中,可以看到第一行是一个异常,紧接其后的是对这个异常的追踪。每一行(形如at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]这样的)都是一个 stack trace line ,而其中类似于 servlet-api-2.5-6.1.12.jar:6.1.12的就是一个 packaging data

有了这东西可以快速定位消息被记录时的位置,但很耗费资源。

配置文件(xml)的基本结构

chapter 3

As will be demonstrated over and over, the syntax of logback configuration files is extremely flexible. As such, it is not possible to specify the allowed syntax with a DTD file or an XML schema. Nevertheless, the very basic structure of the configuration file can be described as, <configuration> element, containing zero or more <appender> elements, followed by zero or more <logger> elements, followed by at most one <root> element. The following diagram illustrates this basic structure.

基本结构为 配置文件基本结构

在官方文档本章的读取配置文件顺序/优先级时,谈及默认配置,并给了一个默认配置等效的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>
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

从这里我们可以看到,这个xml元素以<configuration></configuration>为总览,其内部直接子元素有<appender></appender><root></root>,没有<logger></logger>。这与appenderlogger可有0到无穷多个的下标相符合,也与root没有任何下标表示有且必须只有一个相符。

配置文件(xml)的注意事项:大小写

chapter 3

Since logback version 0.9.17, tag names pertaining to explicit rules are case insensitive. For example, <logger>, <logger> and <logger> are valid configuration elements and will be interpreted in the same way. Note that XML well-formedness rules still apply, if you open a tag as <xyz> you must close it as </xyz>, will not work. As for implicit rules, tag names are case sensitive except for the first letter. Thus, <xyz> and <xyz> are equivalent but not <xyz>. Implicit rules usually follow the camelCase convention, common in the Java world. Since it is not easy to tell when a tag is associated with an explicit action and when it is associated with an implicit action, it is not trivial to say whether an XML tag is case-sensitive or insensitive with respect to the first letter. If you are unsure which case to use for a given tag name, just follow the camelCase convention which is almost always the correct convention.

简单来说,就是像logger,appender,layout,encoder,filter这样的明确的被logback内置和实现的组件,它们的标签名不区分大小写。对于搞不清楚什么成分的,保险起见,使用驼峰命名法准没错。

定义属性(变量):一次定义,之后复用(引用)。

chapter 3

Earlier versions of this document used the term "property substitution" instead of the term "variable". Please consider both terms interchangeable although the latter term conveys a clearer meaning.

虽然文档说 variable 是现在推荐的用法,但我个人还是认为用 属性 或者 属性键值对 更直白对应一些。

The string between an opening ${* and closing *}* is interpreted as a reference to the *value* of the property. For property*aName*, the string "${aName}" will be replaced with the value held by the aName* property.

As they often come in handy, the HOSTNAME and CONTEXT_NAME variables are automatically defined and have context scope. Given that in some environments it may take some time to compute the hostname, its value is computed lazily (only when needed). Moreover, HOSTNAME can be set from within the configuration directly.

在配置文件中定义

<configuration>

  <property name="USER_HOME" value="/home/sebastien" />
    <!-- 这一行定义了一个variable -->

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

在java指令中作为参数传入

java -DUSER_HOME="/home/sebastien" MyApp2
<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>

集成在一个文件中的多个属性键值对(不过文档给的例子只有一个)

<configuration>

  <property file="src/main/java/chapters/configuration/variables1.properties" />
    <!-- 不过一般应该放在src/main/resources/文件夹下,其中src/main/resources/可以通过resource属性省略 --> 
    <!-- <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>

对应src/main/java/chapters/configuration/variables1.properties的内容:

USER_HOME=/home/sebastien

除了上述使用property元素定义属性的方法外,还可以使用define元素定义动态属性,使用timestamp元素使用时间戳属性,使用insertFromJNDI元素获取JNDI中的属性。

属性的作用域

chapter 3

三种作用域

LOCAL SCOPE A property with local scope exists from the point of its definition in a configuration file until the end of interpretation/execution of said configuration file. As a corollary, each time a configuration file is parsed and executed, variables in local scope are defined anew.

CONTEXT SCOPE A property with context scope is inserted into the context and lasts as long as the context or until it is cleared. Once defined, a property in context scope is part of the context. As such, it is available in all logging events, including those sent to remote hosts via serialization.

SYSTEM SCOPE A property with system scope is inserted into the JVM's system properties and lasts as long as the JVM or until it is cleared.

查找一个变量时先本地作用域,后上下文作用域,后系统作用域,最后系统环境。

During substitution, properties are looked up in the local scope first, in the context scope second, in the system properties scope third, and in the OS environment fourth and last.

指定一个属性的作用域(默认为本地作用域)

The scope attribute of the element, element or the `` element can be used to set the scope of a property. The scope attribute admits "local", "context" and "system" strings as possible values. If not specified, the scope is always assumed to be "local".

<configuration>

  <property scope="context" name="nodeId" value="firstNode" />

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/opt/${nodeId}/myApp.log</file>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="FILE" />
  </root>
</configuration>

属性的高级用法

chapter 3

默认值

default values can be specified using the ":-" operator. For example, assuming the variable named aName is not defined, "${aName**:-golden**}" will be interpreted as "golden".

属性嵌套:属性的值中引用其它属性

The value definition of a variable can contain references to other variables. Suppose you wish to use variables to specify not only the destination directory but also the file name, and combine those two variables in a third variable called "destination". The properties file shown below gives an example.

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

in the properties file above, "destination" is composed from two other variables, namely "USER_HOME" and "fileName".

<configuration>

  <property file="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>

属性名嵌套:属性名中引用其它属性

When referencing a variable, the variable name may contain a reference to another variable. For example, if the variable named "userid" is assigned the value "alice", then "${${userid}.password}" references the variable with the name "alice.password".

例子中.password不是成员运算操作,最后的结果上来看就是单纯的字符串拼接。

属性默认值嵌套

The default value of a variable can reference a another variable. For example, assuming the variable 'id' is unassigned and the variable 'userid' is assigned the value "alice", then the expression "${id**:-**${userid}}" will return "alice".

HOSTNAME 属性和CONTEXT_NAME属性

As it often comes in handy, the HOSTNAME property is defined automatically during configuration with context scope.

在我自己的主机上使用了以下HOSTNAME的结果(第一个)

DESKTOP-L79Q7C3 15:01:12.299 [main] INFO xyz.s613.main.Hello - runtimeexception

windows 10的此电脑上右键,属性,计算机名。就那个东西。

As its name indicates, the CONTEXT_NAME property corresponds to the name of the current logging context.

时间戳属性

The timestamp element can define a property according to current date and time. The timestamp element is explained in a subsequent chapter.

根据超链接跳转,简单复制下对面举的例子

chapter 4

<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} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

以及说明

The timestamp element takes two mandatory attributes key and datePattern and an optional timeReferenceattribute. The key attribute is the name of the key under which the timestamp will be available to subsequent configuration elements as a variable. The datePattern attribute denotes the date pattern used to convert the current time (at which the configuration file is parsed) into a string. The date pattern should follow the conventions defined in SimpleDateFormat. The timeReference attribute denotes the time reference for the time stamp. The default is the interpretation/parsing time of the configuration file, i.e. the current time. However, under certain circumstances it might be useful to use the context birth time as time reference. This can be accomplished by setting the timeReference attribute to "contextBirth".

<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" timeReference="contextBirth"/>

动态属性:Defining properties on the fly

chatper 3

You may define properties dynamically using the `<define> element. The define element takes two mandatory attributes: name and class. The name attribute designates the name of the property to set whereas the class attribute designates any class implementing the PropertyDefiner interface. The value returned by the getPropertyValue() method of the PropertyDefiner instance will be the value of the named property. You may also specify a scope for the named property by specifying a scope attribute.

Here is an example.

<configuration>

  <define name="rootLevel" class="a.class.implementing.PropertyDefiner">
    <shape>round</shape>
    <color>brown</color>
    <size>24</size>
  </define>
 
  <root level="${rootLevel}"/>
</configuration>

At the present time, logback does ships with two fairly simple implementations of PropertyDefiner.

Implementation name Description
CanonicalHostNamePropertyDefiner Set the named variable to the canonical host name of the local host. Note that obtaining the canonical host name may take several seconds.
FileExistsPropertyDefiner Set the named variable to "true" if the file specified by path property exists, to "false" otherwise.
ResourceExistsPropertyDefiner Set the named variable to "true" if the resource specified by the user is available on the class path, to "false" otherwise.

我实际使用了一下

    <define name="ResourceExists" class="ch.qos.logback.core.property.ResourceExistsPropertyDefiner">
        <resource>logback-test.xml</resource>
    </define>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern> ${ResourceExists} %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg %n</pattern>
        </encoder>
    </appender>

我在src/main/resources下有logback.xml但没有logback-test.xml,故<resource>logback-test.xml</resource>时输出结果为false 15:44:59.767 [main] INFO xyz.s613.main.Hello - runtimeexception ,注意第一个false。而<resource>logback.xml</resource>时输出结果为true 15:46:52.925 [main] INFO xyz.s613.main.Hello - runtimeexception

从JNDI中获取属性

The `` configuration directive extracts an env-entry stored in JNDI and inserts the property in local scope with key specified by the asattribute. As all properties, it is possible to insert the new property into a different scope with the help of the scopeattribute.

<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 %msg %logger{50}%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>

条件配置

chapter 3

Developers often need to juggle between several logback configuration files targeting different environments such as development, testing and production. These configuration files have substantial parts in common differing only in a few places. To avoid duplication, logback supports conditional processing of configuration files with the help of , and `` elements so that a single configuration file can adequately target several environments. Note that conditional processing requires the Janino library.

开发者通常会有多种开发环境,比如开发环境,测试环境和运行环境。这些环境要求的配置文件大部分是相同的,但有小部分不一样。为了避免重复,logback 提供了对条件配置功能。>

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

The condition is a Java expression in which only context properties or system properties are accessible. For a key passed as argument, the property() or its shorter equivalent p() methods return the String value of the property. For example, to access the value of a property with key "k", you would write property("k") or equivalently p("k"). If the property with key "k" is undefined, the property method will return the empty string and not null. This avoids the need to check for null values.

The isDefined() method can be used to check whether a property is defined. For example, to check whether the property "k" is defined you would write isDefined("k") Similarly, if you need to check whether a property is null, the isNull() method is provided. Example: isNull("k").

举个例子

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

Conditional processing is supported anywhere within the `` element. Nested if-then-else statements are also supported.

引入其它文件中的配置

<configuration>
  <include file="src/main/java/chapters/configuration/includedConfig.xml"/>

  <root level="DEBUG">
    <appender-ref ref="includedConsole" />
  </root>

</configuration>

The target file MUST have its elements nested inside an <include> element.

Example: File include (logback-examples/src/main/resources/chapters/configuration/includedConfig.xml)

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

As a resource:
To include a resource, i.e a file found on the class path, use the resource attribute.

<include resource="includedConfig.xml"/>

As a URL:
To include the contents of a URL use the url attribute.

<include url="http://some.host.com/includedConfig.xml"/>

引入的文件默认不可不存在,即不存在会报错。可以使用optional属性告知,如果不存在,那就不用了,不必报错。

If it cannot find the file to be included, logback will complain by printing a status message. In case the included file is optional, you can suppress the warning message by setting optional attribute to true in the `` element.

<include optional="true" ..../>

阳光号
129 声望5 粉丝

引用和评论

0 条评论