星游第二领地

logback_config_demo

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014

我自己的logback一般先用这个总结好的架子,需要的再往上添(搞毛线?wordpress.com官方说用“[ code ]”来标记,编辑器里又自动用<code>标记,但即使指定lang=”xml”,也没法正确的现实xml内容啊):

 

<?xml version=”1.0″ encoding=”UTF-8″?>
<!– <configuration scan=”true” scanPeriod=”60 seconds”> –>
<configuration>
<!– print configuration status on console –>
<statusListener class=”ch.qos.logback.core.status.OnConsoleStatusListener”/>

<!– Beagle: eclipse plugin –>
<!– <consolePlugin /> –>

<!– log path –>
<property name=”LOG_PATH” value=”d:/log” />
<!– <property resource=”resource1.properties” /> –>

<!– ================================= appenders =================================== –>

<!– console appender (must be omitted in product environment) –>
<appender name=”STDOUT” class=”ch.qos.logback.core.ConsoleAppender”>
<encoder charset=”UTF-8″>
<!– [%p] works fine with “grep console” –>
<!– \(%F:%L\) for caller reference –>
<pattern>%d{HH:mm:ss.SSS} [%t] [%p] %c{1} – %m \(%F:%L\) %n</pattern>
</encoder>
</appender>

<!– default appender –>
<appender name=”COMMON-DEFAULT-APPENDER” class=”ch.qos.logback.core.rolling.RollingFileAppender”>
<File>${LOG_PATH}/common-default.log</File>
<rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
<FileNamePattern>${LOG_PATH}/common-default.%d.log.gz</FileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder charset=”UTF-8″>
<Pattern>%d [%t] %-5p %c{1} – %m%n</Pattern>
<!– since default appender deal with massive messages, so somewhat losses can be tolerated –>
<immediateFlush>false</immediateFlush>
</encoder>
</appender>

<!– error appender –>
<appender name=”COMMON-ERROR-APPENDER” class=”ch.qos.logback.core.rolling.RollingFileAppender”>
<filter class=”ch.qos.logback.classic.filter.ThresholdFilter”>
<level>ERROR</level>
</filter>
<File>${LOG_PATH}/common-error.log</File>
<rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
<FileNamePattern>${LOG_PATH}/common-error.%d.log.gz</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder charset=”UTF-8″>
<Pattern>%d [%t] %-5p %c{1} – %m%n</Pattern>
</encoder>
</appender>

<!– business appender –>
<appender name=”BUSINESS-APPENDER” class=”ch.qos.logback.core.rolling.RollingFileAppender”>
<File>${LOG_PATH}/business.log</File>
<rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
<!– folders archive for month, files archive for day –>
<FileNamePattern>${LOG_PATH}/%d{yyyy-MM,aux}/business.%d.log.gz</FileNamePattern>
</rollingPolicy>
<encoder charset=”UTF-8″>
<Pattern>%d [%t] %-5p %c{1} – %m%n</Pattern>
</encoder>
</appender>

<!– alarm email appender –>
<appender name=”ALARM-APPENDER” class=”ch.qos.logback.classic.net.SMTPAppender”>
<smtpHost>localhost</smtpHost>
<smtpPort>587</smtpPort>
<SSL>true</SSL>
<username>master1@watchzerg.me</username>
<password>test_pass</password>
<to>master2@watchzerg.me</to>
<from>master3@watchzerg.me</from>
<subject>[ALARM] Fail to change price for goods %X{change_price_id}! </subject>
<layout class=”ch.qos.logback.classic.html.HTMLLayout”>
<pattern>%d%t%p%c%m</pattern>
</layout>
<charsetEncoding>UTF-8</charsetEncoding>
<asynchronousSending>false</asynchronousSending>
<!–
<cyclicBufferTracker class=”ch.qos.logback.core.spi.CyclicBufferTracker”>
<maxNumberOfBuffers>256</maxNumberOfBuffers>
</cyclicBufferTracker>
–>
<evaluator class=”ch.qos.logback.classic.boolex.OnMarkerEvaluator”>
<marker>ALARM_EMAIL</marker>
</evaluator>
<discriminator class=”ch.qos.logback.classic.sift.MDCBasedDiscriminator”>
<key>change_price_id</key>
<defaultValue>default</defaultValue>
</discriminator>
</appender>

<!– ================================= loggers =================================== –>

<!– business logger –>
<logger name=”BUSINESS” level=”INFO” additivity=”false”>
<appender-ref ref=”STDOUT”/>
<appender-ref ref=”BUSINESS-APPENDER” />
<appender-ref ref=”ALARM-APPENDER”/>
</logger>

<!– root logger –>
<root level=”DEBUG”>
<appender-ref ref=”STDOUT”/>
<appender-ref ref=”COMMON-DEFAULT-APPENDER”/>
<appender-ref ref=”COMMON-ERROR-APPENDER”/>
<appender-ref ref=”ALARM-APPENDER”/>
</root>

</configuration>

logback_eclipse_plugin_beagle

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
 
先安装依赖插件:
http://download.eclipse.org/technology/nebula/snapshot    (注意这个是稳定的release版的地址,只需要安装“Nubula Release Individual Widgets”中的“Nebula Grid Feature”即可)
再安装logback-beagle插件:
http://logback.qos.ch/p2/ ( “Logback” “Logback Beagle” “SLF4J”这三个都得选)
完成后按“alt-shift-Q,Q”弹出view选择框,找到beagle打开即可(之后需要在logback-test.xml里配置<consolePlugin />,参见logback文档)。
 
在logback-test.xml里加上配置<consolePlugin />即可
     <configuration debug=”true”>
       <!– sends logs to logback-beagle –>
       <consolePlugin />
     </configuration>
     ——实际上这个配置会自动创建一个SocketAppender,并把日志发到本机的4321端口。
 
单击停止滚动,双击开始滚动
右键菜单可以跳转到调用行,也可以查看最多8行调用栈
 
配置:
eclipse的preference里:
     Run/Debug – Beagle
          在这里可以自定义控制台输出的pattern
     General – Appearance – Colors and Fonts
          可以单独对Beagle设置字体
 
 
 

logback_doc_manual_09_logging_separation

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
 
主要处理多个应用程序运行在同一个web容器,并且每个应用使用各自独立的日志环境。
——想想一个tomcat运行了多个项目——也就是“一个JVM,多个ClassLoader,每个ClassLoader拥有自己独立的Logger Context”的问题。
 
Context Selectors
     调用LoggerFactory.getLogger(“foo”)的时候,会要求SLF4J绑定一个ILoggerFactory,如果SLF4J使用的是logback,那么“绑定ILoggerFactory”的方法,会代理给一个ContextSelector实例,该实例会根据情况返回最合适的LoggerContext对象——这个对象实现了ILoggerFactory接口。
     默认总是返回DefaultContextSelector,可以在系统属性里通过logback.ContextSelector属性指定自己的类。
 
ContextJNDISelector
     根据JNDI查找Selector——这样可以在各个项目的web.xml里配置自己的JNDI环境变量。
     为了在单个web项目重启和关闭的时候,对应的logger Context可以被回收,最好配置ContextDetachingSCL监听器
     不过每次获取logger都查找JNDI太慢,可以配置LoggerContextFilter过滤器来避免——会在每个http请求到来的时候把logger context放在线程内部,从而允许之后跳过JNDI搜索。
     此时最好把logger全声明成static的,减少logger的获取总次数。
 
Taming static references in shared libraries
     如果有一个类属于共享包,被两个web项目同时引用
          1,当这个类使用非静态logger时,一切良好,来自两个web项目的调用,会返回各自的logger context。
          2,当这个类使用静态logger时,会错误的返回首次调用它的web项目的logger context——对这个问题ContextJNDISelector无能为力(eluded a solution for eons… 哈哈哈哈)
 
     对于上述的“来自共类的静态logger,获取不同的logger context”的问题,有几个的办法:
          1,共享类改成非静态logger——一般不现实,因为共享类的源码往往不受项目控制。
          2,把共享类挪到web项目里——也够呛,一个两个类还好,那么多第三方依赖咋弄?
          3,所以还有一种取巧的办法:用SiftingAppender,根据JNDI信息把不同的日志分别输出到不同的文件里去(logback提供了JNDIBasedContextDiscriminator帮助做这事儿)——也就是说,两个项目分享同一个logger,等到其关联的appender需要把event输出到文件时,再临时决定输出到哪个文件。
     上述第3种方法有一个问题:
               假如项目A和项目B都使用共享包S。
                    项目A启动并调用S的时候,就已经把S的logger context设置为A了,日志会写入A.log中。
                    B启动并调用S的时候logger contex依然是A(不过因为配置了SiftingAppender的存在,B的日志依然可以写到正确的B.log中)
               ——也就是说,共享logger的问题并没有实质解决,这个logger还是只能属于A或者B其中之一,只不过logger在写日志前根据MDC里的JNDI可以将日志写到正确的文件中去。
               而初始化了两个不同的logger context都指向B.log,存在多个线程写同一个文件的不安全问题——需要对appender配置prudent参数来解决(会影响性能)。
 
     (正常使用都是一个web container部署一个web项目,很少遇到上述情况。这部分文档看的很蛋疼……)
 
 

logback_doc_manual_08_mapped_diagnostic_contexts

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
http://logback.qos.ch/manual/mdc.html

 
MDC(注意这个类在org.slf4j包里)
     基于“多个线程同步处理多个请求”的假设来设计的,上下文信息记录。
          ——子线程会自动拷贝双亲线程的这类信息。
          ——如果没有附加处理的话,放入线程池处理的任务会丢失MDC上下文。
          该设计假定向MDC放数据的速度不会太快。
 
最常用的web场景,是集成在一个servlet的Filter中,在请求时载入MDC信息,doFilter处理完成后卸载MDC信息。
——最好在“验证用户”这个Filter之后(或者之中)进行,这样可以把用户验证信息(包括但不限于用户名)写入MDC。
 
交给线程池处理(submit)之前:MDC.getCopyOfContextMap(),把返回的map当作参数传给任务线程
线程池处理代码的第一行:MDC.setContextMapValues(),把接到的map参数设置到本线程的MDC中(别忘了最后清除掉)
 
MDCInsertingServletFilter
     将web请求常用信息设置到MDC中:
          req.remoteHost as returned by the getRemoteHost() method
          req.xForwardedFor value of the “X-Forwarded-For” header
          req.requestURI       as returned by getRequestURI() method
          req.requestURL as returned by getRequestURL() method
          req.queryString as returned by getQueryString() method
          req.userAgent      value of the “User-Agent” header
 
     web.xml中的配置
<filter>
  <filter-name>MDCInsertingServletFilter</filter-name>
  <filter-class>
    ch.qos.logback.classic.helpers.MDCInsertingServletFilter
  </filter-class>
</filter>
<filter-mapping>
  <filter-name>MDCInsertingServletFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping> 
     注意filter顺序,经过该filter过滤之后,其它filter才能打印出MDC信息(特别是struts之类依赖filter处理主逻辑的)
 
     使用例子:
     %X{req.remoteHost} %X{req.requestURI}%n%d – %m%n
 
     ——其实没多大意义,还是自己写这个filter,挑选自己的有效信息比较好。
 
 
 
 

logback_doc_manual_07_filters

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
http://logback.qos.ch/manual/filters.html

 
logback-classic有两种filter:Regular filters和turbo filters,挂在appender上
 
Regular filters
     只有一个decide()方法,返回DENY, NEUTRAL or ACCEPT
 
LevelFilter
     <filter class=”ch.qos.logback.classic.filter.LevelFilter”>
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
     </filter>
 
ThresholdFilter
     高于或等于指定日志级别的记录,会返回NEUTRAL
     <filter class=”ch.qos.logback.classic.filter.ThresholdFilter”>
      <level>INFO</level>
     </filter>     
 
EvaluatorFilter
     EventEvaluator的实现判断某个条件是否满足。
     
     GEventEvaluator
          接受groovy语法的布尔表达式作为判断条件,需要依赖Groovy运行时,表达式语句在配置的时候完成编译。
          logback会自动将目标事件作为一个变量传进来,可以用“event”或者“e”来引用。
          TRACE, DEBUG, INFO, WARN and ERROR这些级别也引入了,所以可以这样判断相等: “event.level == DEBUG” 。
          其他比较符,大于小于,需要转换成int再比较。
    <filter class=”ch.qos.logback.core.filter.EvaluatorFilter”>      
      <evaluator class=”ch.qos.logback.classic.boolex.GEventEvaluator”> 
        <expression>
           e.level.toInt() >= WARN.toInt() &amp;&amp;  <!– Stands for && in XML –>
           !(e.mdc?.get(“req.userAgent”) =~ /Googlebot|msnbot|Yahoo/ )
        </expression>
      </evaluator>
      <OnMismatch>DENY</OnMismatch>
      <OnMatch>NEUTRAL</OnMatch>
    </filter>
 
     JaninoEventEvaluator
          使用java表达式,使用上比基于groovy的GEventEvaluator繁琐,但执行速度更快。
          详情略。
          
Matchers
     执行上面的过滤器时,可以调用String.matches()方法,但代价是每次需要重编译一个正则Pattern对象。
     所以可以预先定义和编译一个,以便复用。
     详情略。
 
TurboFilters
     跟普通过滤器功能一样,但是:
          1,TurboFilter是跟logging context关联的,而不是跟appender关联。作用域更大。不仅在指定appender使用时,而且在每次logging请求时都会被调用。
          2,他们是在LoggingEvent对象创建之前被调用,过滤时不需要event实例做参数,所以性能更高(因为在event创建之前就已经执行过滤了)。
     内置了一些TurboFilter:
 
          MDCFilter     测试指定的值是否存在于MDC中
          DynamicThresholdFilter     基于MDC的key和level来限流
          MarkerFilter     测试指定的marker是否出现在请求中
          DuplicateMessageFilter
               自动过滤相同的消息。
                    使用简单的字符串比对——即使两个字符串基本相同,相差一两个字母:也会被认为不同。
                    仅比较raw字符串,用{}转义过的字符串不去比较。
                    可以通过AllowedRepetitions设置允许的重复上限,超过上限的会被抛弃。默认大小为5
                    需要通过一个内部cache来保存老的消息以便判断,可以通过CacheSize设置缓存大小,默认100.
          
在evaluator的expression里,将logging时间与项目启动时间对比,可以控制仅输出“项目启动后20秒内的某类型日志”——这个对于“确认某个定时任务在启动时是运行状态”很有用,例如:
<filter class=”ch.qos.logback.core.filter.EvaluatorFilter”>
  <evaluator name=”loggingTaskEval”>
    <expression>
      logger.getName().contains(“LoggingTask”) &amp;&amp;
      message.contains(“Howdydy-diddly-ho”) &amp;&amp;
      (timeStamp – event.getStartTime()) >= 20000
    </expression>
  </evaluator>
  <OnMatch>DENY</OnMatch>
</filter>
 

logback_doc_manual_06_layouts

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
http://logback.qos.ch/manual/layouts.html

 
PatternLayout
     转换模式类似于C语言里的printf()。
 
转移字符:
     c{length}
     lo{length}
     logger{length}
          其中的length代表输出的logger长度。
          设为0的话例外,仅输出最右边的logger名。
          其余情况会自动计算,尽量使得输出的总字符串长度小于指定长度——但是,最右边的logger名称无论如何会完整保留,同时前面的每一级logger,最少会被精简到1个字符。
          ——%c{1}
     C{length}
     class{length}
          打印调用者的类名称,设置方法与上面一样。
          性能不高。
     contextName
     cn
          打印logger在event最初绑定的logger context的名称。
     d{pattern}
     date{pattern}
     d{pattern, timezone}
     date{pattern, timezone}
          输入日志时间,使用java.text.SimpleDateFormat的日期格式化方法。
          如果不指定日期格式,默认使用ISO8601,也就是2006-10-20 14:06:49,812这种形式。
          ——%d
     F / file
          输出java源文件的名称。
          性能不高。
     caller{depth}
     caller{depth, evaluator-1, … evaluator-n}
          打印日志事件的调用堆栈。
          使用评估器evaluator决定是否打印。
     L
     line
          输出日志记录请求发起的行数。
          性能不高。
     m
     msg
     message
          日志正文
          ——%m
     M
     method
          日志调用方法名。
          性能不高
     n
          操作系统对应的换行符
          ——%n
     p
     le
     level
          日志等级
     r
     relative
          应用程序启动到日志创建的相对时间
     t
     thread
          线程名
          ——%t
     X{key:-defaultVal}
     mdc{key:-defaultVal}
          MDC信息
     ex{depth} 
     exception{depth} 
     throwable{depth} 
     ex{depth, evaluator-1, …, evaluator-n} 
     exception{depth, evaluator-1, …, evaluator-n} 
     throwable{depth, evaluator-1, …, evaluator-n}
          输出异常堆栈深度(如果有的话),默认full全部输出。
          可以指定的参数值:
               short:打印堆栈的第一行
               full:打印所有行
               任何数字:指定行数
          使用评估器evaluator决定是否打印。
     xEx{depth} 
     xException{depth} 
     xThrowable{depth} 
     xEx{depth, evaluator-1, …, evaluator-n} 
     xException{depth, evaluator-1, …, evaluator-n} 
     xThrowable{depth, evaluator-1, …, evaluator-n}
          跟上面的类似,但是附加了包信息。
          如果包信息不准确(是猜测的),那么会自动在包信息前面附加一个“~”字符。
          如果在日志信息模式里,未指定任何异常格式,那么系统会自动在末尾加上一个%xEx。
          如果不想打印包信息(例如netbean里会出问题),那么在日志模式的末尾明确指定%ex即可,就会输出不包含包信息的堆栈。
          如果想不打印任何异常堆栈信息,可以使用%nopex。
          ——%xEx
     nopex 
     nopexception          
          加上%nopex可以阻止系统自动在日志模式末尾添加%xEx——也就是完全禁止异常堆栈打印。
     marker
          输出关联的marker信息,如果marker多级关联,会都打印出来。
     property{key}
          输出key关联的属性——定义在logger context或者system properties里面。
     replace(p){r, t}
          将p中的所有符合r正则的字符串,都替换成t。
          例如%replace(%logger%msg){‘\.’, ‘/’},会将输出的logger和msg信息中的点号都替换成斜杠。
     rEx{depth} 
     rootException{depth} 
     rEx{depth, evaluator-1, …, evaluator-n} 
     rootException{depth, evaluator-1, …, evaluator-n}
          类似于xEx,也会打印异常的包信息,但是会将root exception打印到前面,跟普通的异常打印顺序是反着的。
 
     转义百分号: \%
     正常情况下转义字符会被正确分割,但有些时候例外,例如%date%nHello,系统会解析%nHello失败。如果真的需要在%n后紧跟一个Hello,可以这样:%date%n{}Hello
 
     我一般用这个:
     %d [%t] %-5p  %c{1} – %m%n
——————————————————————
Format modifiers
     控制数据段的补齐。
     例如 %20.30logger 
          如果logger长度小于20,则从左边用空格补齐;如果logger长度大于30,则从开头(左边)切去多余字符。
          如果20或者30前面有负号,则左右颠倒。
     假如想给日志级别只输出1个字符(T,D,W,I,E),不需要自己写一个Converter,而只需要配置一下即可:%.-1level
 
转义选项:
     如果转义选项里面包含特殊字符(特别是在使用正则表达式的时候),例如大小括号、逗号、空格,那么可以用单引号或双引号括起来,例如:
          <pattern>%-5level – %replace(%msg){‘\d{14,16}’, ‘XXXX’}%n</pattern>
     ——这个可以把14到16位的数字(信用卡号)转换为XXXX。
——————————————————————
括号的特殊作用:
     %-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} – %msg%n
     可以让括号里的两个表达式联合起来按照30个字符补齐。
     括号可以用反斜杠转义:\(%d{HH:mm:ss.SSS} [%thread]\)
——————————————————————
Coloring
     着色,window需要引其它包,linux和mac os本身支持着色。
     样例:<pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) – %msg %n</pattern>
          %highlight会将ERROR设为红色加粗,WARN设为红色,INFO设为蓝色,其余默认。
          %cyan会将logger名称设为蓝绿色。
——————————————————————
Evaluators
     EventEvaluator类的实现,用来评估一个事件是否符合打印条件。
  <evaluator name=”DISP_CALLER_EVAL”>
    <expression>logger.contains(“chapters.layouts”) &amp;&amp; \
      message.contains(“who calls thee”)</expression>
  </evaluator>
 
  <appender name=”STDOUT” class=”ch.qos.logback.core.ConsoleAppender”> 
    <encoder>
      <pattern>
        %-4relative [%thread] %-5level – %msg%n%caller{2, DISP_CALLER_EVAL}
      </pattern>
    </encoder>
  </appender>
     注意因为xml的关系,要用&amp;转义&
     应用场景:
          例如,如果日志级别是WARN以上,并且logger是来自一个财务模块——那么就打印caller信息。
 
     注意:
          在%caller转义模式中,当Evaluators返回true的时候才输出。
          在%ex转义模式中,当Evaluatorstrue的时候不输出。
 
     例如下面的配置,当异常对象为TestException时,不输出。
  <evaluator name=”DISPLAY_EX_EVAL”>
    <expression>throwable != null &amp;&amp; throwable instanceof  \
      chapters.layouts.TestException</expression>
  </evaluator>
        
  <appender name=”STDOUT” class=”ch.qos.logback.core.ConsoleAppender”>
    <encoder>
      <pattern>%msg%n%ex{full, DISPLAY_EX_EVAL}</pattern>
    </encoder>
  </appender>
——————————————————————
Creating a custom conversion specifier
     略
——————————————————————
HTMLLayout
     使用html表格来布局日志信息。
     可以使用普通的pattern转义符,但转义字符之前,不许用包括空格在内的任何字符分隔。
  <appender name=”FILE” class=”ch.qos.logback.core.FileAppender”>
    <encoder class=”ch.qos.logback.core.encoder.LayoutWrappingEncoder”>
      <layout class=”ch.qos.logback.classic.html.HTMLLayout”>
        <pattern>%relative%thread%mdc%level%logger%msg</pattern>
      </layout>
    </encoder>
    <file>test.html</file>
  </appender>
 
     HTMLLayout会自动创建一个DefaultThrowableRenderer,将异常信息打印到完整的一行里。如果不想这样,可以指定一个NOPThrowableRenderer。
 
用CSS指定表格的样式:略
 
该Layout最常见的用法是配合SMTPAppender,发送html格式的日志邮件。
——————————————————————
Logback access
     略
 
 
 

logback_doc_manual_05_encoders

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
http://logback.qos.ch/manual/encoders.html

 
Encoder
     负责将事件对象转换为字节数组.
     目前只有PatternLayoutEncoder是唯一有用的Encoder。
 
LayoutWrappingEncoder
     兼容的包装器(因为老版本的logback是跳过Encoder直接依赖Layout的)
 
PatternLayoutEncoder
     针对PatternLayout(最常用的layout)定制的兼容包装器。
 
     immediateFlush属性:是否将日志立刻写入磁盘,默认为true。将这个选项设为false可以达高4到5倍的吞吐量。
<appender name=”FILE” class=”ch.qos.logback.core.FileAppender”> 
  <file>foo.log</file>
  <encoder>
    <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
    <!– this quadruples logging throughput –>
    <immediateFlush>false</immediateFlush>
  </encoder> 
</appender>
 
     outputPatternAsHeader属性:在日志文件的顶部输出一行字符串样式,默认flase。
<appender name=”FILE” class=”ch.qos.logback.core.FileAppender”> 
  <file>foo.log</file>
  <encoder>
    <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
    <outputPatternAsHeader>true</outputPatternAsHeader>
  </encoder> 
</appender>
     输出结果:
#logback.classic pattern: %d [%thread] %-5level %logger{36} – %msg%n
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App – Hello world
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App – Hi again
 
 
 

logback_doc_manual_04_appenders

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
Appender都集成ch.qos.logback.core.Appender接口。
每个Appender可以绑定若干个Filter。
每个Appender可以将任务代理给Layout或者Encoder。
每个Layout或者Encoder只能属于1个Appender。
Appener也可以不包含Layout或者encoder,例如SocketAppender,直接把Event序列化传输。
ch.qos.logback.core.AppenderBase这是一个抽象类,对Appender接口提供了骨架实现,线程安全。
ch.qos.logback.core.UnsynchronizedAppenderBase是对应的线程不安全的类,但是可以将线程安全性委托给下一层的类(例如OutputStream)
OutputStreamAppender是文件输出和控制台输出Appender的父类,类层次结构如下:
logback-4-1
ConsoleAppender
     输出到System.out or System.err
可配置属性:
     encoder
     target:     默认是System.out
     withJansi:     默认false,用来在windows中支持控制台色彩的
样例:
  <appender name=”STDOUT” class=”ch.qos.logback.core.ConsoleAppender”>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} – %msg %n</pattern>
    </encoder>
  </appender>
———————————————————-
FileAppender
     写入到文件(新消息到来,写入前,才会判断是否该分割文件等)
可配置属性:
     append:     文件追加,默认为true
     encoder:
     file:     文件路径和名词(如果windows下,记得分隔符转义)
     prudent:     谨慎模式,不同JVM(甚至存在于不同主机上)安全的写入同一个文件。默认关闭。
默认每行都flush到磁盘,可以修改Encoder的immediateFlush属性来改变这一行为。
样例:
  <appender name=”FILE” class=”ch.qos.logback.core.FileAppender”>
    <file>testFile.log</file>
    <append>true</append>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} – %msg%n</pattern>
    </encoder>
  </appender>
唯一名词的记录文件(通过启动时间戳):
  <timestamp key=”bySecond” datePattern=”yyyyMMdd’T’HHmmss”/>
  <appender name=”FILE” class=”ch.qos.logback.core.FileAppender”>
    <file>log-${bySecond}.txt</file>
    <encoder>
      <pattern>%logger{35} – %msg%n</pattern>
    </encoder>
  </appender>
——这样就可以在每次项目启动时生成一个新的文件。
默认是使用xml解析的时间,也可以指定使用log context的创建时间:
     <timestamp key=”bySecond” datePattern=”yyyyMMdd’T’HHmmss”
             timeReference=”contextBirth”/>
———————————————————-
RollingFileAppender
     滚动记录日志文件
     有两个子组件:
          RollingPolicy ——接管“滚动”操作,控制该操作如何发生
          TriggeringPolicy ——决定滚动操作是否发生,何时发生
     默认情况下,必须同时含有上述两个组件。不过有的RollingPolicy也实现了后者的接口,那么可以身兼2职。
可配置属性:
     file:
     append:
     encoder:
     rollingPolicy:
     triggeringPolicy:
     prudent:
RollingPolicy
     包含的操作:将当前的日志文件归档(并重命名),压缩(如果需要)
TimeBasedRollingPolicy
     最常用的,基于时间滚动,同时实现了RollingPolicy和TriggeringPolicy
     可配置属性:
          fileNamePattern:     文件名匹配格式
               文件名+日期定义器“%d”
               日期定义器的格式在SimpleDateFormat中定义
               滚动周期是通过日期定义器推断出来的
               其父元素RollingFileAppender中file属性可以省略(因为可以用fileNamePattern猜测出当前时间的日志名)
                    也可以不省略,这样可以为“当前日志文件”和“归档日志文件”分别制定不同的路径
               日志定义器中的正反斜杠都会被认为是目录分隔符
               可以指定多个日期定义器,但只能有1个是主要的,其余的必须标记为aux(辅助的)
          maxHistory:     最大文件数
          cleanHistoryOnStart:     启动时清除历史归档日志
     对fileNamePattern的更详细的解释和示例:
          /wombat/foo.%d
               按天分隔日志
               未指定格式,所以默认为yyyy-MM-dd(按天滚动)
               如果在RollingFileAppender中指定了file(默认日志文件名):
                    今天日志为 /wombat/foo.2006-11-24
                    昨天日志为 /wombat/foo.2006-11-23
               如果没有在RollingFileAppender中指定file为“/wombat/foo.txt”,
                    今天日志为 /wombat/foo.txt
                    昨天日志为 /wombat/foo.2006-11-23
               ——其实不指定默认日志文件名的方式更好,因为避免了滚动时重命名操作,也就避免了潜在异常。
          /wombat/%d{yyyy/MM}/foo.txt
               按“年/月”分两级文件夹,文件名固定为“foo.txt”
               如果在RollingFileAppender中指定了file(默认日志文件名):
                    2006年10月日志为 /wombat/2006/10/foo.txt
                    2006年11月日志为 /wombat/2006/11/foo.txt
               如果没有在RollingFileAppender中指定file为“/wombat/foo.txt”,
                    当前正在写的日志永远为 /wombat/foo.txt
                    归档的日志格式为 /wombat/2006/10/foo.txt
          /wombat/foo.%d{yyyy-ww}.log
               每个星期归档一个新文件(注意具体“哪天算是一个星期的第一天”取决于系统locale属性)
          /wombat/foo%d{yyyy-MM-dd_HH}.log
               每小时一次归档
          /wombat/foo%d{yyyy-MM-dd_HH-mm}.log
               每分钟一次归档
          /foo/%d{yyyy-MM,aux}/%d.log
               每天一次归档,但每个月一个新文件夹
     如果fileNamePattern文件名是以.gz或者.zip结尾的,那么TimeBasedRollingPolicy会自动压缩:
          /wombat/foo.%d.gz
     日志归档是“记录日志的事件”触发的,所以有一定延迟。例如第二天的第一条日志是临晨01:00才过来,那么这个时候才会归档前一天的日志。
配置样例:
  <appender name=”FILE” class=”ch.qos.logback.core.rolling.RollingFileAppender”>
    <file>logFile.log</file>
    <rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
      <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} – %msg%n</pattern>
    </encoder>
  </appender>
———————————————————-
FixedWindowRollingPolicy
     可配置属性:
          minIndex:
          maxIndex:
          fileNamePattern:
               必须包含“%i”占位符,用来表示将当前索引值插入什么位置。
               例如“MyLogFile%i.log”配合“最小1,最大3”,表示文件名为:MyLogFile1.log, MyLogFile2.log, MyLogFile3.log
               同样加上zip或gz后缀可以启用压缩
     因为每次需要很多重命名操作(重命名次数等于window size),所以如果设置size超过20,会被强制指定为20.
     配置样例:
  <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} – %msg%n</pattern>
    </encoder>
  </appender>
———————————————————-
SizeAndTimeBasedFNATP(File Naming And Triggering Policy)——是TimeBasedRollingPolicy的一个子组件
     配置样例:
  <appender name=”ROLLING” class=”ch.qos.logback.core.rolling.RollingFileAppender”>
    <file>mylog.txt</file>
    <rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
      <!– rollover daily –>
      <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy
            class=”ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP”>
        <!– or whenever the file size reaches 100MB –>
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
     ——每天滚动一个文件,如果文件体积达到100M,则也拆分。
     ——也支持自动删除老文件,可以通过maxHistory指定保存的最多文件数(每天最多这么多,还是一共最多这么多?)
———————————————————-
SizeBasedTriggeringPolicy——一般配合FixedWindowRollingPolicy使用
     指定文件超过“maxFileSize”指定大小时,上层RollingFileAppender触发滚动操作。
     默认“10MB”,可以指定各种后缀:KB,MB,GB
———————————————————-
———————————————————-
SocketAppender and SSLSocketAppender
以及后面关于网络,数据库,远程日志服务器,JNDI等……省略……
———————————————————-
SiftingAppender
     可以根据指定的变量分割文件。例如根据用户ID,则每个用户一个日志文件。
     可配置参数:
          timeout:如果一个关联的appender如果超过默认30分钟没有被访问,则被SiftingAppender卸载掉。
          maxAppenderCount:可以最大追踪的关联appender数量,默认int最大值。
     这个Appener会把日志记录动作代理给关联的appender。
     选择条件由Discriminator指定,默认为MDCBasedDiscriminator。
     示例,假设应用程序这样设置了MDC信息:
logger.debug(“Application started”);
MDC.put(“userid”, “Alice”);
logger.debug(“Alice says hello”);
     然后这样配置:
  <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} – %msg%n</pattern>
        </layout>
      </appender>
    </sift>
  </appender>
     因为确定timeout和maxAppenderCount比较困难,所以如果程序可以知道“执行到某个地方很可能应该关闭对应的appender”,那么可以明确指定一个FINALIZE_SESSION的marker。这样siftingAppender关联的对应appender,会在接到这个marker的几秒钟后关闭。例如:
import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;
  void job(String jobId) {
    MDC.put(“jobId”, jobId);
    logger.info(“Starting job.”);
    // 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);
    }
  }
———————————————————-
SMTPAppender
     将日志事件缓存指定的数量,被特定事件触发后,异步发送邮件。
          smtpHost
          smtpPort     默认25
          to          发送目标,多个联系人可以用逗号隔开,也可以用多个<to>元素
          from      发送者邮箱,如果想包含名字,可以用特定格式“Adam Smith &lt;smith@moral.org&gt;”
          subject     邮件标题,可以使用PatternLayout的转义字符,会用“触发该邮件的日志事件”的信息替换转义字符
          discriminator     默认只有一个缓存。通过指定该属性,可以有多个缓存,这样可以根据事件信息发给不同的人或者ip
          evaluator     声明一个<EventEvaluator/>元素,通过class属性指定类型。默认为OnErrorEvaluator,也可以自己指定OnMarkerEvaluator,JaninoEventEvaluator,GEventEvaluator
          cyclicBufferTracker     环形缓存跟踪器,基于discriminator的返回值工作。默认保存缓存大小为256
          username
          password
          STARTTLS     如果开启,会发起STARTTLS命令,导致连接转换为SSL。连接默认是不使用加密的。默认为false
          SSL          如果开启,那么使用SSL连接。默认false。
          charsetEncoding     默认UTF-8
          localhost     如果SMTP发送方的hostname没配置好,邮件服务器可能拒绝请求,这个时候可以设置这个值为客户端全名。
          asynchronousSending     异步发送,默认true。特定情况下需要设置为false,例如应用程序发送完邮件就会立即关闭。
          includeCallerData     包含调用者信息,默认为false。
          sessionViaJNDI     logback依赖javax.mail.Session来发送邮件。该属性默认为false,SMTPAppender会根据配置构建新的Session。如果设为true,会去web容器寻找Session对象,此时应用程序不应该再依赖mail.jar等
          jndiLocation     查找JNDI的路径,例如”java:comp/env/mail/Session”
     最多保存256条日志事件,否则内存消耗太大(不建议自己指定其它值)。
     发邮件依赖JavaMail API (mail.jar)和JavaBeans Activation Framework (activation.jar) ——maven里引入前者会自动依赖后者。
     发送者和接收者都可以是动态属性:
<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>
     注意上面的layout也可以用patternLayout:
    <layout class=”ch.qos.logback.classic.PatternLayout”>
      <pattern>%date %-5level %logger{35} – %message%n</pattern>
    </layout>
触发事件:
     默认是OnErrorEvaluator,但可以自己定制。SMTPAppender仅维护一个Evaluator,这个Evaluator可以自己维护状态,例如可以实现一个CounterBasedEvaluator。
基于标记Marker的事件触发:
Marker notifyAdmin = MarkerFactory.getMarker(“NOTIFY_ADMIN”);
logger.error(notifyAdmin,
  “This is a serious an error requiring the admin’s attention”,
   new Exception(“Just testing”));
  <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>
  </appender>
还可以使用更加通用的JaninoEventEvaluator或者GEventEvaluator,他们提供更复杂更强大的甄别功能(也包含了OnMarkerEvaluator的功能)。
身份验证/STARTTLS/SSL
     SMTPAppender支持用户名和密码的加密验证。
     STARTTLS方式是先使用非加密方式建立连接,然后切换到SSL(常用于server-server交互)。
     SSL方式是直接建立SSL连接(一般用于client-sever交互)。
Appender configuration for Gmail (SSL)
<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>
  </appender>
SMTPAppender for Gmail (STARTTLS)
  <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>
  </appender>
Discriminator
     用MDCBasedDiscriminator做示例,根据MDC的值,维护多个缓存
  <appender name=”EMAIL” class=”ch.qos.logback.classic.net.SMTPAppender”>
    <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>
     上面的例子先利用了MDCInsertingServletFilter把请求方的hostname或ip设置到MDC中。
     每一个remoteHost都有一个自己的buffer,一旦某个remoteHost触发了发送邮件的请求,那么该buffer里的256条信息会被发送出去。
Buffer管理:
     上面的例子,每一个远程地址都有自己的buffer,会极大的消耗内存。
     默认情况下,logback内部最多允许64个buffer同时存在,LRU算法换出。超过30分钟未使用的buffer也会被换出。该值可以通过maxNumberOfBuffers来设置。
          这里有问题,通过各种手段都无法设置此值,貌似是个bug,我在尽我所能查遍所有资料后,在stackoverflowh和官方jira上提了这个问题:
     在高强度系统中,上面的2个保护机制不够,需要加入手工管理:通过明确指定“FINALIZE_SESSION”这个Marker,来告诉logback去释放对应的buffer,这样就可以安全的将maxNumberOfBuffers设置为512乃至1024。(具体设置办法参照SiftingAppender里描述的)
这里是官方的一个SMTPAppender的例子:
注意Marker可以叠加,所以如果有必要,可以把“发送邮件的Marker”和“终结Session(以便清理buffer)的Marker”关联在一起。
     Marker SMTP_TRIGGER = MarkerFactory.getMarker(“SMTP_TRIGGER”);
     SMTP_TRIGGER.add(FINALIZE_SESSION_MARKER);
———————————————————-
AsyncAppender
     类似于单独的一个事件分发器,所以必须引用另一个appender。
     用一个BlockingQueue缓冲事件,然后创建一个线程,从队列里获取事件并分发给引用的appender。
     默认情况下,如果队列达到80%的容量,则丢弃TRACE,DEBUG,INFO级别的日志。
     应用程序停止的时候,会通知LoggerContext停止,在停止各个Appender时,AsyncAppender会停止接收日志,并将信息flush到磁盘。
     配置参数:
          queueSize:默认256
          discardingThreshold:默认队列20%可用的时候开始选择性抛弃信息,设为0表示不抛弃
          includeCallerData:是否包括调用者信息(重开销),默认只复制线程名和MDC信息(因为切换了线程,其余信息会丢失)
     队列全满的时候,写日志动作会被block,直到队列有可用空间。
———————————————————-
自定义Appender
     略
———————————————————-
———————————————————-
Logback Access
     大部分Appender,在logback-classic与logback-access中使用方式类似。

logback_doc_manual_03_configuration

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
 
——————————————————-
与spring的配置:
貌似官网上没有介绍,但是作者在github上建立了一个扩展项目用于logback与spring结合。
目前最新版本是0.1.2,很多maven公开库里已经有了。
 
maven配置:
<dependency>  
     <groupId>org.logback-extensions</groupId>  
     <artifactId>logback-ext-spring</artifactId>  
     <version>0.1.2</version>  
</dependency>
 
web.xml配置日志框架启动监听器:
<!– logback配置文件 –>
<context-param>
     <param-name>logbackConfigLocation</param-name>
     <param-value>/WEB-INF/classes/logback.xml</param-value>
</context-param>
<!– logback加载监听器 –>
<listener>
     <listener-class>
          ch.qos.logback.ext.spring.web.LogbackConfigListener
     </listener-class>
</listener>
——————————————————-
logback中的配置:
 
可以用java程序配置,也可以用xml或者groovy脚本配置。
使用 http://logback.qos.ch/translator/ 可以把log4j的配置自动转换过来。
 
logback内部查找配置的过程:
     1,在classpath查找“logback.groovy”
     2,在classpath查找“logback-test.xml”
     3,在classpath查找“logback.xml”
     4,使用自身的BasicConfigurator做基本配置,所有日志被输出到控制台。
     —— 一般把“logback-test.xml”放到maven的测试路径,把“logback.xml”放到maven的正式路径,前者优先级更高。
 
logback的状态,加载配置文件的过程:
     代码打印:
          LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
          StatusPrinter.print(lc);
     配置文件形式打印:
          <configuration debug=”true”>
            … 
          </configuration>
     也可以通过设置StatusListener来监听logback的启动和打印状态——在生产环境挺有用,因为配置文件路径挺深。
 
通过在系统变量里加入”logback.configurationFile”指定logback配置文件路径:
     java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
 
设置自动扫描和遇到变更时重新加载配置文件(不指定周期的话默认每1分钟):
     <configuration scan=”true” scanPeriod=”30 seconds”> 
       … 
     </configuration> 
     每当N个(logback会自动调节)日志请求,logback会检查一下扫描周期是否已经到达,如到达再检查配置文件。
 
通过web访问状态信息:
在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
 
注册控制台状态监听器:
     java代码方式:
          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” />  
            …
          </configuration>
     系统变量方式:
          java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener
 
停止logback:
     java代码方式:
          LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
          loggerContext.stop();
     web应用:
          ServletContextListener.contextDestroyed()会自动调用上面的stop方法。
——————————————————-
配置文件格式:
     最外层是<configuration>元素,里面有<appender><logger><root>元素
 
<logger>元素:
     level属性可以这些值:TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF,也可以明确指定继承:INHERITED或者NULL
     包含若干<appender-ref>元素,指定appender的名字。
 
从DEBUG改为INFO级别:
<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>
 
  <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>
——————————————————-
配置Appender:
     name和class属性
     内嵌0个或1个layout元素,0个或多个encoder元素,0个或多个filter元素。还可以内嵌任意数量的appdenter类的属性(例如自定义的appender类)。
     layout元素应该内嵌具体的layout类,但是默认就是PatternLayoutEncoder。
 
Appender的累加性:
     logger会记录在所有绑定在它自身的appender,同时也会记录在该logger祖先的appender上,所以有可能重复记录!
 
配置Logger Context(可以用在“多个项目写入同一个日志文件”这种情况):
<configuration>
  <contextName>myAppName</contextName>
  …
</configuration>
——————————————————-
变量替换:
<configuration>
  <property name=”USER_HOME” value=”/home/sebastien” />
 
  <appender name=”FILE” class=”ch.qos.logback.core.FileAppender”>
    <file>${USER_HOME}/myApp.log</file>
    …
  </appender>
   …
</configuration>
——上面的变量定义在系统属性里也可以:java -DUSER_HOME=”/home/sebastien” MyApp2
 
变量也可以定义在文件里:
<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>
     …
   </appender>
   …
</configuration>
——对应路径的文件里应该加入“USER_HOME=/home/sebastien”
(这路径挺诡异啊,是maven的结构,那打包后岂不是不能用了?)
像这样引用classpath的还靠谱些:
<property resource=”resource1.properties” />
 
变量可以指定作用域:local,context,system
变量可以相互引用:
     USER_HOME=/home/sebastien
     fileName=myApp.log
     destination=${USER_HOME}/${fileName}
命名引用:如果”userid”会被替换为”alice”,那么”${${userid}.password}”会被替换为”alice.password”对应的值。
变量可以指定默认值:${aName:-golden}  ——这就指定了默认值golden
变量的默认值也可以引用变量: “${id:-${userid}}
 
预置变量:HOSTNAME,CONTEXT_NAME
 
可以通过timestamp元素定义一个当前的日期和时间的动态元素。
 
可以自己继承PropertyDefiner实现动态生成属性,现在内置了2个动态属性生成器:
     FileExistsPropertyDefiner     如果指定路径文件存在,则将指定属性设为“true”,反之亦然
     ResourceExistsPropertyDefiner     如果指定资源存在,则将指定属性设为“true”,反之亦然
 
条件语句:
   <!– 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>
 
判断条件只支持context变量和system变量,用property()或者p()来引用——如果没有设定对应变量,这两个方法会返回空串(而不是null)
isDefine()和isNull()分别判断变量是否设置和变量是否为空。
     <if condition=’property(“HOSTNAME”).contains(“torino”)’>
     …
     </if>
 
可以从JNDI读取变量值(作用域为local)。也可以将从JNDI读取的变量存入另一个不同作用域的变量。
     <configuration>
       <insertFromJNDI env-entry-name=”java:comp/env/appName” as=”appName” />
       …
     </configuration>
 
——————————————————-
文件包含(可以使用相对路径。当前路径已经在当前项目中定义,所以没必要与配置文件路径关联):
<configuration>
  <include file=”src/main/java/chapters/configuration/includedConfig.xml”/>
  …
</configuration>     
被包含的文件必须用<include>标签包裹:
<included>
  <appender name=”includedConsole” class=”ch.qos.logback.core.ConsoleAppender”>
    <encoder>
      <pattern>”%d – %m%n”</pattern>
    </encoder>
  </appender>
</included>
还可以关联资源(例如classpath下的某个文件):
     <include resource=”includedConfig.xml”/>
还可以关联URL:
可以指定此次文件包含为“可选的”:
     <include optional=”true” …./>
——————————————————-
LoggerContextListener
其中一个实现LevelChangePropagator会监听日志环境的合适的生命周期,并把日志级别的变化传播给JUL,这样JUL关闭的日志不会再传递给slf4j,用这种方式对性能冲击较小,适合jul-to-slf4j的桥接包。
 
 

logback_doc_manual_02_architecture

Posted in programming, Uncategorized by watchzerg on 7月 13, 2014
http://logback.qos.ch/manual/architecture.html

 
Logger, Appender and Layout
——————————————————-
Logger:
 
Logger继承关系:
com.foo是com.foo.Bar的双亲(parent),同时也是其祖先(ancestor)
java是java.util.Vector的祖先,但不是其双亲。
 
root Logger是所有logger的祖先,可以这样获取:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
 
日志级别,在ch.qos.logback.classic.Level中,有TRACE, DEBUG, INFO, WARN and ERROR
日志级别类是final的,如果想扩展,可以用Marker类。
 
如果一个logger没有指定级别,那么它继承最近的祖先的级别。
root Logger默认是debug级别。
 
日志级别:TRACE < DEBUG < INFO <  WARN < ERROR.
如果日志记录请求——例如logger.info()——的级别大于等于其logger的级别,那么该请求生效。
 
使用LoggerFactory.getLogger(String param)获取的logger,只要参数一致,那么获取到的logger也一致。
即使先创建子logger,再创建双亲logger,后者也会正确的插到logger树上。
 
——————————————————-
Appenders and Layouts:
     一个logger可以绑定多个appender。
     默认情况:每一个日志请求,会被发送到logger对应的所有Appender,以及更高层logger对应的所有appender上。
     如果把某个logger的additivity标志设为false,那么这个logger会记录自身和下层(孙子logger)的日志,但不会再向上传递——也就是把下层的logger的日志拦截到了当前这一层logger为止了。
 
     PatternLayout控制输出的格式,类似C语言的printf格式。
     
——————————————————-
参数化记录日志:
     与slf4j完全相同
 
——————————————————-
日志记录的调用时序图:
logback-1-1