阅读Logback文档笔记--Logback的Configuration配置

要在项目中引入日志,需要一定的代价,据统计日志代码量将会占整个代码量的4%左右。因此我们需要一个工具管理这些日志声明。
对于Log4j.properties可以通过以下网址转化成logback.xml

Logback如何查找配置文件:
  1. 在classpath中查找logback.groovy
  2. 如果不存在,则在classpath中查找logback-test.xml
  3. 如果不存在,继续查找logback.xml
  4. 如果不存在,则由jdk1.6中提出的service-provider loading facility 通过查找META-INF\services\ch.qos.logback.classic.spi.Configurator这个文件中定义的继承 com.qos.logback.classic.spi.Configurator接口的配置实现类的全限定名
  5. 如果还不存在,logback则会使用基本的配置,将日志输出定向到控制台。

如果你使用maven构建项目,可以将logback-test.xml放在src/test/resources路径下,如此,该配置就只在test阶段会启用。将logback.xml放在src/main/resources中,则会随项目一起打包到正式环境使用。

快速启动(不明觉厉):将配置文件转化需要100毫秒的时间,可以通过使用以上第四步的方法来缩减这个时间。

我们来看看上面第5步的BasicConfigurator的源码

public class BasicConfigurator extends ContextAwareBase implements Configurator {
31 
32      public BasicConfigurator() {
33      }
34 
35      public void configure(LoggerContext lc) {
36          addInfo("Setting up default configuration.");
37         
38          ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender<ILoggingEvent>();
39          ca.setContext(lc);
40          ca.setName("console");
41          LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<ILoggingEvent>();
42          encoder.setContext(lc);
43         
44 
45          // same as
46          // PatternLayout layout = new PatternLayout();
47          // layout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
48          TTLLLayout layout = new TTLLLayout();
49 
50          layout.setContext(lc);
51          layout.start();
52          encoder.setLayout(layout);
53         
54          ca.setEncoder(encoder);
55          ca.start();
56         
57          Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
58          rootLogger.addAppender(ca);
59      }
60  }

这个最小的配置为root logger绑定一个ConsoleAppender,输出则使用PatternLayoutEncoder将日志模板设置成

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n.
另外,root logger的默认级别是debug。
输出格式如下:
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.


下面我们来看个小例子
package manual.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.");
  }
}
例子中我们使用slf4j的接口类,并没有使用直接导入logback的实现。事实上,我们也不需要让logback客户端声明式地依赖logback,因为slf4j会在抽象层采用可以使用的日志框架。这就为我们可以随时方便地切换日志框架提供了可能。


打印logback status message(logback上下文状态)的方法
方式一:
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);
  ...
}

方式二:cofiguration 的debug设置为true,但如果该配置文件有错将无法自动输出状态信息。
<configuration debug="true">
这种方式也相当于,注册了一个statusListener

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

</configuration>


我们也可以通过jvm参数指定默认配置文件,注意:文件名必须以.xml或者.groovy结尾

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


自动重载配置:当配置文件更新时,自动重新加载
扫描时间间隔单位可以是:milliseconds, seconds, minutes or hours.,如不设置默认为1分钟检测一次。实际上扫描间隔,不单单取决于间隔时间,还取决于logger request的次数
<configuration scan="true" scanPeriod="30 seconds" >
  ...
</configuration>


允许记录产生错误时的堆栈状态,默认为false。虽然有用,但在错误频繁的系统中,性能消耗比较大
<configuration packagingData="true">
  ...
</configuration>
也可以通过代码实现

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


如果你想改写logback默认的配置加载机制,可以调用 JoranConfigurator.doConfigure(String arg0)重新配置
但是先要调用loggerContext.context的reset()方法清除之前的配置
  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);
}


通过网页查看LoggerContext上下文信息:Logback将它的内部状态数据手机在StatusManager中,可以通过context获取。
可以在web.xml中引入以下servlet,然后就可以通过对应的url访问如下界面:
  <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>
技术分享


设置日志状态监听器:注册一个状态监听器,可以方便管理logback的内部状态,在没有人工参与的情况下。
代码实现:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();

  StatusManager statusManager = lc.getStatusManager();
  OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
  statusManager.add(onConsoleListener);
配置实现(推荐):状态监听器的声明应该在一开始就定义,否则无法收集到声明之前的状态操作
<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

  ... the rest of the configuration file
</configuration>
jvm参数实现:
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...
logback提供了一个状态监听器的实现
OnConsoleStatusListener:输出到控制台
OnErrorConsoleStatusListener:输出到错误控制台
NopStatusListener:丢弃状态信息


在某些情况下,为了释放logback带来的资源消耗,可以暂停logback context
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
在web应用中,以上功能页可以通过调用servletContextListener的contextDestroyed方法实现,原话如下:
In web-applications the above code could be invoked from within the contextDestroyed method of ServletContextListener in order to stop logback-classic and release resources.


下面我们来说说Configuration文件的编写格式
这是一个基本的结构图
技术分享
首先看看他的一些基本规范
  1. 对大小写敏感(建议采用全小写):虽然大小写不明感,但是开/闭标签需要大小写一致,第一个字母除外。
  2. logger通过<logger>标签定义,属性如下:
    1. name:必要,唯一标识一个Logger
    2. level:可选,不设置默认从父logger中继承,logger日志级别,可选值:TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF
    3. additivity:可选,默认为true,表示是否叠加祖宗logger的appender, appender代表一个日志输出地
    4. <appender-ref>:内部元素,至少包含一个,属性ref=“appender”的name。
  3. <root>:只提供一个level属性,因为name=ROOT,additity=false已经规定。

以下是一个例子:
<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" level="INFO" />
  <logger name="chapters.configuration.Foo" level="DEBUG" />

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

</configuration>


下面我们再来看看怎么定义<appender>

技术分享

需要注意是:logger默认的additity为true,代表同时向祖宗logger的appender输出,所以错误设置,可能导致重复的日志记录
如果有需要,这时候就需要声明additity=false
<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>


设置context的name:目的是区别当有多个应用程序logging到同一个目的,如何区分来自哪个application
<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>


定义属性:可以通过定义属性,同一管理一些重复设置的值,获取方式类似于EL表达式,表达式之间可以嵌套
<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>
当然也可以通过jvm参数设置
java -DUSER_HOME="/home/sebastien" MyApp2
如果属性较多,也可以引入文件系统中单独的属性文件,或classpath路径下的文件
<configuration>

     <!--文件系统-->
  <property file="src/main/java/chapters/configuration/variables1.properties" />
 
     <!--类路径--> 
  <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>


定义的属性的范围:
  1. LOCAL 
  2. CONTEXT
  3. SYSTEM

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


获取属性值的时候,也可以默认值的方式,当属性未定义,则采用默认值
${aName:-golden}
${id:-${userid}}

如果属性值使用过计算获得,也可以动态定义,实现PropertyDefiner接口的getPropertyValue()
<configuration>

  <define name="rootLevel" class="a.class.implementing.PropertyDefiner">
    <shape>round</shape>
    <color>brown</color>
    <size>24</size>
  </define>


  <root level="${rootLevel}"/>
</configuration>
logback也提供了两个简单的动态定义属性类
Implementation name Description
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.


当然,有时候我们也需要根据情况适当调整配置:需要注意的是,p("k")相当于property("k"),且当属性未定义,或null时,都同一返回空字符串。这样就可以减少对属性的判断操作
<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>

我们也可以从JNDI中获取属性值,as 代表属性引入后的名字
<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>


我们也可以将配置文件分片,然后引入,optional属性代表非必须
<configuration>
  <include file="src/main/java/chapters/configuration/includedConfig.xml" optional=false/>

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

</configuration>

文件名: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>
引入路径也分为三种
  1. file system: <include file=
  2. classpath:<include resource=
  3. url:<include url=
文章来自:http://blog.csdn.net/doraemon_wu/article/details/51897061
© 2021 jiaocheng.bubufx.com  联系我们
ICP备案:鲁ICP备09046678号-3