Layout是logback的一个组件,负责将日志事件转换为string,在layout接口中定义了format方法,用于将事件转换为String。Layout的接口大体如下:
public interface Layout<E> extends ContextAware, LifeCycle { String doLayout(E event); String getFileHeader(); String getPresentationHeader(); String getFileFooter(); String getPresentationFooter(); String getContentType(); }这个接口基本可以满足大部分的转换需求。
Logback-classic
logback-classic 可以处理 IloggingEvent的子类事件。
编写自己的Layout
我们采用logback-class的架构来实现一个可以打印出 应用以运行时间,日志级别,调用的线程,日志名称,日志内容的功能。
例子如下:
package chapters.layouts; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.LayoutBase; public class MySampleLayout extends LayoutBase<ILoggingEvent> { public String doLayout(ILoggingEvent event) { StringBuffer sbuf = new StringBuffer(128); sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime()); sbuf.append(" "); sbuf.append(event.getLevel()); sbuf.append(" ["); sbuf.append(event.getThreadName()); sbuf.append("] "); sbuf.append(event.getLoggerName(); sbuf.append(" - "); sbuf.append(event.getFormattedMessage()); sbuf.append(CoreConstants.LINE_SEP); return sbuf.toString(); } }注意到mysampleLayout继承自layoutbase。layoutbase对所有layout实例的基本状态进行了管理,比如layout何时启动或者停止,信息的头部,尾部,内容类型的信息。layoutbase是一个非常通用的类,开发者一般可以通过继承该类来实现所期望的输出格式。
mysample类中唯一的方法是dolayout,该方法中首先初始化了一乐stringbuffer,然后将一系列event的属性进行添加。之后将buffer转换为string,然后进行输出。
在上面的例子,dolayout方法中忽略了事件中的异常,在实际的应用中,一般都会需要对异常信息进行展示。
配置 自定义的layout
自定义的layout 可以按照其他组件的方式进行配置。在前文我们说过,FileAppender及其子类需要一个encoder,满足这个需求,我们用layoutwrappingencoder来封装我们的mysampleout然后将其传递给fileappender。配置文件,如下所示:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="chapters.layouts.MySampleLayout" /> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>最终的到日志输出格式如下:
0 DEBUG [main] chapters.layouts.SampleLogging - Everything's going well 0 ERROR [main] chapters.layouts.SampleLogging - maybe not quite...
可以在layout或者其他任何的logback组件中通过添加setter的方式来添加属性。如下面的列子就是在前面的例子基础上进行了属性添加,具体代码及配置文件如下:
package chapters.layouts; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.LayoutBase; public class MySampleLayout2 extends LayoutBase<ILoggingEvent> { String prefix = null; boolean printThreadName = true; public void setPrefix(String prefix) { this.prefix = prefix; } public void setPrintThreadName(boolean printThreadName) { this.printThreadName = printThreadName; } public String doLayout(ILoggingEvent event) { StringBuffer sbuf = new StringBuffer(128); if (prefix != null) { sbuf.append(prefix + ": "); } sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime()); sbuf.append(" "); sbuf.append(event.getLevel()); if (printThreadName) { sbuf.append(" ["); sbuf.append(event.getThreadName()); sbuf.append("] "); } else { sbuf.append(" "); } sbuf.append(event.getLoggerName()); sbuf.append(" - "); sbuf.append(event.getFormattedMessage()); sbuf.append(LINE_SEP); return sbuf.toString(); } }
配置文件如下:
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="chapters.layouts.MySampleLayout2"> <prefix>MyPrefix</prefix> <printThreadName>false</printThreadName> </layout> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>需要注意的是,printThreadName属性是一个boolean类型,而不是string类型。
PatternLayout
logback classic 提供了一个配置性非常好的实现类 :patternLayout。就像所有的layout一样,patternlayout也是实现将日志事件转换为String进行输出。但是他的输出可以通过他的转换模式进行定制。
转换模式与C语言的printf()方法的格式控制非常类似。一个转换模式由 输出字符 和 转换控制表达式组成。你可以随意的在转换模式中插入任何字符。 每一个转换表达式以‘%’开头,然后在一对大括号中添加各种格式控制参数。其中有一些转换词可以用来控制时间属性,等级,线程等等。
由于Fileappender及其子类必须有一个encoder。因此,当将fileappender及其子类与patternlayout联合起来使用时,必须提供用encoder将patternlayout进行封装。由于fileappender和patterlayout的组合使用非常频繁,因此logback提供了一个名为patternlayoutencoder的encoder实现类,用于将patternlayout进行封装,然后可以作为一个encoder来进行使用。下面的例子展示了consoleappender和patternlayoutencoder的联合使用。
package chapters.layouts; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.ConsoleAppender; public class PatternSample { static public void main(String[] args) throws Exception { Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); LoggerContext loggerContext = rootLogger.getLoggerContext(); // we are not interested in auto-configuration loggerContext.reset(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(loggerContext); encoder.setPattern("%-5level [%thread]: %message%n"); encoder.start(); ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>(); appender.setContext(loggerContext); appender.setEncoder(encoder); appender.start(); rootLogger.addAppender(appender); rootLogger.debug("Message 1"); rootLogger.warn("Message 2"); } }输出结果将如下所示:
DEBUG [main]: Message 1
WARN [main]: Message 2
需要注意的是,在上面的转换模式“%-5level [%thread]: %message%n”中,在输出字符及转换表达式之间并没有分隔符。当进行转换模式的解析时,patternlayout能够将输出字符(空格,括号,冒号)和转换表达式进行区分。在上面的例子中,转换表达式“%5level”表示日志的级别信息将会只能输出五个字符长度。后面将会对格式进行进行更加详细的介绍。
在 patternlayout 小括号是有特殊意义的,在输出字符中应尽量避免使用。就像在前面提到了,转换表达式可能将一些 配置参数在大括号中进行配置。比如%logger{10},这里logger是转换词,而10就是他的属性。
下面的表格对 转换词及其属性进行了介绍。当多个转换词在同一个单元中列出时,他们就是别名的关系。
Conversion Word(转换词) | Effect(作用) | ||||||||||||||||||||||||
c{length} lo{length} logger{length} | Outputs the name of the logger at the origin of the logging event. This conversion word takes an integer as its first and only option. The converter's abbreviation algorithm will shorten the logger name, usually without significant loss of meaning. Setting the value of length option to zero constitutes an exception. It will cause the conversion word to return the sub-string right to the rightmost dot character in the logger name. The next table provides examples of the abbreviation algorithm in action. 输出日志事件的名称,这个转换词的唯一属性是数字类型的。将会将日志的名称进行缩短。当将这个值设为0,将会导致异常。下面是一些截取的例子:
Please note that the rightmost segment in a logger name is never abbreviated, even if its length is longer than the length option. Other segments may be shortened to at most a single character but are never removed. 需要注意的是,最右侧的类名是不会被缩短的,即使设置的长度比类名还要端。其他部分的会被缩短,但不会被删除。 | ||||||||||||||||||||||||
C{length} class{length} | Outputs the fully-qualified class name of the caller issuing the logging request. Just like the %logger conversion word above, this conversion takes an integer as an option to shorten the class name. Zero carries special meaning and will cause the simple class name to be printed without the package name prefix. By default the class name is printed in full. Generating the caller class information is not particularly fast. Thus, its use should be avoided unless execution speed is not an issue. 将调用日志的类的全路径进行输出。就像%logger一样,采用一个整型数字作为属性。如果将值设置为0的话,将会只输出类名,而路径名称就不会输出。默认情况下会输出全路径。总的来说,这个属性的运行效率不高,建议不要采用。 | ||||||||||||||||||||||||
contextName cn | Outputs the name of the logger context to which the logger at the origin of the event was attached to. 将日志的上下文名称进行输出 | ||||||||||||||||||||||||
d{pattern} date{pattern} d{pattern, timezone} date{pattern, timezone} | Used to output the date of the logging event. The date conversion word admits a pattern string as a parameter. The pattern syntax is compatible with the format accepted by You can specify the string "ISO8601" for the ISO8601 date format. Note that the %date conversion word defaults to the ISO 8601 date format in the absence of a pattern parameter. Here are some sample parameter values. They assume that the actual date is Friday 20th of October, 2006 and that the author has returned to working on this document just after lunch. 输出日志的时间,data 属性将 pattern string 作为一个参数,其构造方式和simpledateformat相同。,下面是一些例子
The second parameter specifies a timezone. For example, the '%date{HH:mm:ss.SSS, Australia/Perth} would print the time in the time zone of Perth, Australia, the world's most isolated city. Note that in the absence of the timezone parameter, the default timezone of the host Java platform is used. If the specified timezone identifier is unknown or misspelled, the GMT timezone is assumed as dictated by the TimeZone.getTimeZone(String) method specification. COMMON ERROR Given that the comma ',' character is interpreted as the parameter separator, the pattern 第二参数用以确定一个时区3,如 the '%date{HH:mm:ss.SSS, Australia/Perth},将会输出Perth, Australia的时间。注意,默认采用的时区是主机java平台采用的时区。如果时区没有或者拼写错误,则将设置为GMT的时区。 注意:在这里面,逗号被视为分隔符,如HH:mm:ss,SSS将被 视为采用HH:mm:ss的时间输出格式,采用ssss作为时区。如果需要在时间样式里面输出逗号,需要将其放在引号里面。如%date{"HH:mm:ss,SSS"}. | ||||||||||||||||||||||||
F / file | Outputs the file name of the Java source file where the logging request was issued. Generating the file information is not particularly fast. Thus, its use should be avoided unless execution speed is not an issue 不解释,建议不用 | ||||||||||||||||||||||||
caller{depth}caller{depth, evaluator-1, ... evaluator-n} | Outputs location information of the caller which generated the logging event. The location information depends on the JVM implementation but usually consists of the fully qualified name of the calling method followed by the caller's source, the file name and line number between parentheses. A integer can be added to the caller conversion specifier's options to configure the depth of the information to be displayed. For example, %caller{2} would display the following excerpt: 0 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) And %caller{3} would display this other excerpt: 16 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) Caller+2 at mainPackage.ConfigTester.main(ConfigTester.java:38) This conversion word can also use evaluators to test logging events against a given criterion before computing caller data. For example, using %caller{3, CALLER_DISPLAY_EVAL} will display three lines of stacktrace, only if the evaluator called CALLER_DISPLAY_EVAL returns a positive answer. Evaluators are described below. 输出产生日志事件的调用者的信息。调用者的信息取决与具体的JVM实现。但是通常包括完全路径名,方法名,文件名,方法的line number。 一个整型参数可以用以控制信息展示的深度,如:%caller{2} 将展示两层。 %caller{3} 就会展示三层。这个参数词还可以以指定的条件进行日志输出,如%caller{3, CALLER_DISPLAY_EVAL} 只有当CALLER_DISPLAY_EVAL满足了才会输出日志。 | ||||||||||||||||||||||||
L / line | Outputs the line number from where the logging request was issued. Generating the line number information is not particularly fast. Thus, its use should be avoided unless execution speed is not an issue. 输出日志的 line number,尽量少用 | ||||||||||||||||||||||||
m / msg / message | Outputs the application-supplied message associated with the logging event. 事件相关的信息 | ||||||||||||||||||||||||
M / method | Outputs the method name where the logging request was issued. Generating the method name is not particularly fast. Thus, its use should be avoided unless execution speed is not an issue. 不解释,建议少用 | ||||||||||||||||||||||||
n | Outputs the platform dependent line separator character or characters. This conversion word offers practically the same performance as using non-portable line separator strings such as "\n", or "\r\n". Thus, it is the preferred way of specifying a line separator. 指定平台的换行符。 | ||||||||||||||||||||||||
p / le / level | Outputs the level of the logging event. 日志级别。 | ||||||||||||||||||||||||
r / relative | Outputs the number of milliseconds elapsed since the start of the application until the creation of the logging event. 输出自系统启动到日志时间发生的耗时。 | ||||||||||||||||||||||||
t / thread | Outputs the name of the thread that generated the logging event.线程不解释 | ||||||||||||||||||||||||
X{key:-defaultVal} mdc{key:-defaultVal} | Outputs the MDC (mapped diagnostic context) associated with the thread that generated the logging event. If the mdc conversion word is followed by a key between braces, as in %mdc{userid}, then the MDC value corresponding to the key 'userid' will be output. If the value is null, then the default valuespeficied after the :- operator is output. If no deault value is specified than the empty string is output. If no key is given, then the entire content of the MDC will be output in the format "key1=val1, key2=val2". See the chapter on MDC for more details on the subject. 这个属性很少用到,用到再研究吧。 |
csdn的破表格编辑器不会自动加行,继续上面的表。
ex{depth} exception{depth} throwable{depth} ex{depth, evaluator-1, ..., evaluator-n} exception{depth, evaluator-1, ..., evaluator-n} throwable{depth, evaluator-1, ..., evaluator-n} | Outputs the stack trace of the exception associated with the logging event, if any. By default the full stack trace will be output. The throwable conversion word can followed by one of the following options:
Here are some examples: 将与日志事件相关的异常的stack trace进行输出。默认情况下,输出全部的stack trace。 throwable中可以设置以下的一项值: short 只输出stack trace的第一行 full 全部输出 any 输出指定的行()
This conversion word can also use evaluators to test logging events against a given criterion before creating the output. For example, using %ex{full, EX_DISPLAY_EVAL} will display the full stack trace of the exception only if the evaluator called EX_DISPLAY_EVAL returns a negative answer. Evaluators are described further down in this document. | ||||||||||
xEx{depth} xException{depth} xThrowable{depth} xEx{depth, evaluator-1, ..., evaluator-n} xException{depth, evaluator-1, ..., evaluator-n} xThrowable{depth, evaluator-1, ..., evaluator-n} |
Same as the %throwable conversion word above with the addition of class packaging information. If you do not specify %xThrowable or another throwable-related conversion word in the conversion pattern, At the end of each stack frame of the exception, a string consisting of the jar file containing the relevant class followed by the "Implementation-Version" as found in that jar's manifest will be added. This innovative technique was originally suggested by James Strachan. If the information is uncertain, then the class packaging data will be preceded by a tilde, i.e. the '~' character. 与%throwable 相同 |
如果没有指定%xThrowable 或 另一个 throwable-related 转换词。patternlayout将会自动这个属性添加,以统计stack trace的信息。如果不想stack trace信息输出的话,可以用 $nopex替代%xThrowable。
在每个stack trace的结尾,将会输出相关类所在的jar包。如果包信息不能确定的话,就用波浪号进行表示,如下面的例子。
java.lang.NullPointerException at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]
at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06]
at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06]
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na]
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na]
nopexnopexception
Although it pretends to handle stack trace data, this conversion word does not output any data, thus, effectively ignoring exceptions.
The %nopex conversion word allows the user to override PatternLayout
's internal safety mechanism which silently adds the %xThrowable conversion keyword in the absence of another conversion word handling
Outputs the marker associated with the logger request.
In case the marker contains children markers, the converter displays the parent as well as childrens' names according to the format shown below.
parentName [ child1, child2 ]
没用
property{key}Outputs the value associated with a property named key. The the relevant docs on how to define ion entitled define variables and variable scopes. If key is not a property of the logger context, then keywill be looked up in the System properties.
There is no default value for key. If it is omitted, the returned value will be "Property_HAS_NO_KEY", expliciting the error condition.
没用 replace(p){r, t}Replaces occurrences of 'r', a regex, with its replacement 't' in the string produces by the sub-pattern 'p'. For example, "%replace(%msg){'\s', ''}" will remove all spaces contained in the event message.
The pattern 'p' can be arbitrarily complex and in particular can contain multiple conversion keywords. For instance, "%replace(%logger %msg){'\.', '/'}" will replace all dots in the logger or the message of the event with a forward slash.
替换作用,很少用到吧 rEx { depth }rootException { depth }
rEx {depth, evaluator-1, ..., evaluator-n}
rootException {depth, evaluator-1, ..., evaluator-n}
Outputs the stack trace of the exception associated with the logging event, if any. The root cause will be output first instead of the standard "root cause last". Here is a sample output (edited for space):
java.lang.NullPointerException at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3] at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3] Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat': at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) [spring-2.0.jar:2.0] at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) [spring-2.0.jar:2.0] at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) [tomcat-6.0.26.jar:6.0.26]
The %rootException converter admits the same optional parameters as the %xException converter described above, including depth and eveluators. It outputs also packaging information. In short, %rootException is very similar to %xException, only the order of exception output is reversed.
Tomasz Nurkiewicz, the author of %rootException converter, documents his contribution in a blog entry entitled "Logging exceptions root cause first".
如果设置,错误的更原因将会最先输出
% character has special meaning
%的输出
通过前面可以看出,%在logback中有特殊含义,当需要将其进行输出是,采用反斜杠的方式:\%。
Restrictions on literals immediately following conversion words
[
和
] 以及-。然而,总有2b的情况,如"%date%nHello" 就会报错,%nHello无法解析究竟是做什么的,这时应该改为:"%date%n{}Hello"。
Format modifiers
在默认情况下, 相关信息会按照上述的设置进行输出。但是有的时候需要另外对格式进行调整(最大最小宽度)。formate modifier属性就是用来在百分号和转换词之间进行设置。第一个format modifier 是左对齐婊子通过(-)减号标记。然后是最小宽度表示是一个十进制的数值。
如果其中还包含其他字符,那么将会填充其中,直到最小字符的长度。默认是填充左侧,但是也可以设置为右侧填充。如果数据的长度大于最小长度限制,这个区域就扩大,以适应数据长度,数据是不会被删除的。
当然这种行为可以通过maximum进行控制,当数据的长度大于maximum是,将会从数据的开始部分进行截取。例如,maximum值是8,而数据长度是10,那么最开始的两个字符将会被截取。这与c语言中的从后面截取是不同的。 可以通过将减号符号放置在filed的后面实现从后面截取。下面是例子:
Format modifier | Left justify | Minimum width | Maximum width | Comment |
---|---|---|---|---|
%20logger | false | 20 | none | Left pad with spaces if the logger name is less than 20 characters long. 当数据小于20时,在左侧填充空格 |
%-20logger | true | 20 | none | Right pad with spaces if the logger name is less than 20 characters long. 当数据小于20时,将会在右侧填充空格,实现了左对齐 |
%.30logger | NA | none | 30 | Truncate from the beginning if the logger name is longer than 30 characters. 当数据大于30个字符,将从前面开始截取 |
%20.30logger | false | 20 | 30 | Left pad with spaces if the logger name is shorter than 20 characters. However, if logger name is longer than 30 characters, then truncate from the beginning. 当数据小于20从左侧填充,当数据大于30,从开始进行截取 |
%-20.30logger | true | 20 | 30 | Right pad with spaces if the logger name is shorter than 20 characters. However, if logger name is longer than 30 characters, then truncate from the beginning. 当数据小于20,从右侧开始填充,当数据大于30,从开始进行截取 |
%.-30logger | NA | none | 30 | Truncate from the end if the logger name is longer than 30 characters. 当数据大于30,从尾部进行截取。 |
下面的例子显示了一下输出结果,注意中括号,只是为了展示宽度而添加的。、
Format modifier | Logger name | Result |
---|---|---|
[%20.20logger] | main.Name | [ main.Name] |
[%-20.20logger] | main.Name | [main.Name ] |
[%10.10logger] | main.foo.foo.bar.Name | [o.bar.Name] |
[%10.-10logger] | main.foo.foo.bar.Name | [main.foo.f] |
Output just one letter for the level 用一个字母表示日志等级
%.-1level
".
Parentheses are special
%-30(%d{HH:mm:ss.SSS} [%thread]) %-5level %logger{32} - %msg%n
就将会构造一个子样式 "%d{HH:mm:ss.SSS} [%thread]",这个样式作为一个整体被控制为当字符少于30会进行右侧填充。
如果没有子组,结果输出如下:
If without the grouping the output was
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
添加了子组的样式,"%-30()" ,日志输出如下所示:
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
13:09:30 [pool-1-thread-1] INFO ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
显然第二种的样式,输出更加便于阅读。如果希望输出括号则需要加上反斜杠,如下所示:\(%d{HH:mm:ss.SSS} [%thread]\).
Coloring
%cyan(%logger{15}) 表示日志的名称将为缩短为15个字符,并且以cyan 的颜色显示。%highlight属性将会根据日志的等级输出不同颜色,n bold-red for events of level ERROR, in red for WARN, in BLUE for INFO, and in the default color for other levels.
View as .groovy
<configuration debug="true"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- On Windows machines setting withJansi to true enables ANSI color code interpretation by the Jansi library. This requires org.fusesource.jansi:jansi:1.8 on the class path. Note that Unix-based operating systems such as Linux and Mac OS X support ANSI color codes by default. --> <withJansi>true</withJansi> <encoder> <pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
输出如下所示:
[main] WARN c.l.TrivialMain - a warning message 0 [main] DEBUG c.l.TrivialMain - hello world number1 [main] DEBUG c.l.TrivialMain - hello world number2 [main] INFO c.l.TrivialMain - hello world number3 [main] DEBUG c.l.TrivialMain - hello world number4 [main] WARN c.l.TrivialMain - a warning message 5 [main] ERROR c.l.TrivialMain - Finish off with fireworks
Evaluators
As mentioned above, option lists come in handy when a conversion specifier is required to behave dynamically based on one or more EventEvaluator
objects. EventEvaluator
objects have the responsibility to determine whether a given logging event matches the criteria of the evaluator.
Let us review an example involving a EventEvaluator
. The next configuration file outputs the logging events to the console, displaying date, thread, level, message and caller data. Given that extracting the caller data of a logging event is on the expensive side, we will do so only when the logging request originates from a specific logger, and when the message contains a certain string. Thus, we make sure that only specific logging requests will have their caller information generated and displayed. In other cases, where the caller data is superfluous, we will not penalize application performance.
Evaluators and in particular evaluation expressions are presented in a dedicated section of the chapter on filters which you MUST read if you want to use evaluators in any meaningful way. Also note that the examples below are implicitly based on JaninoEventEvaluator
which requires the Janino library. Please see the corresponding section of the setup document.
<configuration> <evaluator name="DISP_CALLER_EVAL"> <expression>logger.contains("chapters.layouts") && \ 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> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
The above evaluation expression matches events which emanate from a logger with a name containing the string "chapters.layouts" and the message contains the string "who calls thee". Due to XML encoding rules, the & character cannot be written as is, and needs to be escaped as &.
The following class makes use of some of the characteristics mentioned in above configuration file.
Example: Sample usage of EventEvaluators (logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java)
package chapters.layouts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; public class CallerEvaluatorExample { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class); LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); try { JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); configurator.doConfigure(args[0]); } catch (JoranException je) { // StatusPrinter will handle this } StatusPrinter.printInCaseOfErrorsOrWarnings(lc); for (int i = 0; i < 5; i++) { if (i == 3) { logger.debug("who calls thee?"); } else { logger.debug("I know me " + i); } } } }
The above application does nothing particularly fancy. Five logging requests are issued, the third one emitting the message "who calls thee?"
The command
java chapters.layouts.CallerEvaluatorExample src/main/java/chapters/layouts/callerEvaluatorConfig.xml
will yield
0 [main] DEBUG - I know me 0 0 [main] DEBUG - I know me 1 0 [main] DEBUG - I know me 2 0 [main] DEBUG - who calls thee? Caller+0 at chapters.layouts.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28) 0 [main] DEBUG - I know me 4
When a logging request is issued, the corresponding logging event is evaluated. Only the third logging event matches the evaluation criteria, causing its caller data to be displayed. For other logging events, the evaluation criteria do not match and no caller data is printed.
One can change the expression to correspond a real world scenario. For instance, one could combine the logger name and request level. Thus, logging requests of level WARN and up, originating from a sensitive part of an application, e.g. a financial transaction module, would have their caller data displayed.
Important: With the caller conversion word, caller data is output when the expression evaluates totrue.
Let us consider at a different situation. When exceptions are included in a logging request, their stack trace is also output. However, one might want to suppress the stack trace for some specific exceptions.
The Java code shown below creates three log requests, each with an exception. The second exception is different from the others: it contains the string "do not display this" and it is of typechapters.layouts.TestException
. As its message commands, let us now prevent the second exception from being printed.
Example: Sample usage of EventEvaluators (logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java)
package chapters.layouts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; public class ExceptionEvaluatorExample { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class); LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); try { JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); lc.reset(); configurator.doConfigure(args[0]); } catch (JoranException je) { // StatusPrinter will handle this } StatusPrinter.printInCaseOfErrorsOrWarnings(lc); for (int i = 0; i < 3; i++) { if (i == 1) { logger.debug("logging statement " + i, new TestException( "do not display this")); } else { logger.debug("logging statement " + i, new Exception("display")); } } } }
In the next configuration file, the evaluation expression matches events containing a throwable of typechapters.layouts.TextException
, precisely the type of exceptions we wish to suppress.
<configuration> <evaluator name="DISPLAY_EX_EVAL"> <expression>throwable != null && 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> <root level="debug"> <appender-ref ref="STDOUT" /> </root> </configuration>
With this configuration, each time an instance of the chapters.layouts.TestException is included within a logging request, the stack trace will be suppressed.
Launching the command
java chapters.layouts.ExceptionEvaluatorExample src/main/java/chapters/layouts/exceptionEvaluatorConfig.xml
will yield
logging statement 0java.lang.Exception: display at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]logging statement 1logging statement 2java.lang.Exception: display at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]
Notice how the second log statement has no stack trace. We effectively suppressed the stack trace for the TextException
. The text between square brackets at the end of each stack trace line is packaging information discussed earlier.
NOTE With the %ex conversion specifier, the stack trace is displayed when the expression evaluates tofalse.
Creating a custom conversion specifier
Up to this point we have presented the built-in conversion words in PatternLayout
. But it is also possible to add conversion words of your own making.
Building a custom conversion specifier consists of two steps.
Step 1
First, you must extend the ClassicConverter
class. ClassicConverter
objects are responsible for extracting information out of ILoggingEvent
instances and producing a String. For example, LoggerConverter
, the converter underlying the %logger conversion word, extracts the name of the logger from ILoggingEvent
and returns it as a String. It might abbreviate the logger name in the process.
Here is a customer converter which returns the time elapsed since its creaton in nanoseconds:
Example: Sample Converter Example (src/main/java/chapters/layouts/MySampleConverter.java)public class MySampleConverter extends ClassicConverter { long start = System.nanoTime(); @Override public String convert(ILoggingEvent event) { long nowInNanos = System.nanoTime(); return Long.toString(nowInNanos-start); } }
This implementation is pretty straightforward. The MySampleConverter
class extends ClassicConverter
, and implements the convert
method which returns the number of nano-seconds elapsed since its creation.
Step 2
In the second step, we must let logback know about the new Converter
. For this purpose, we need to declare the new conversion word in the configuration file, as shown below:
<configuration> <conversionRule conversionWord="nanos" converterClass="chapters.layouts.MySampleConverter" /> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-6nanos [%thread] - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
Once the new conversion word has been declared in the configuration file, we can refer to it withinPatternLayout
pattern, as with any other conversion word.
The command:
should yield output akin to:
4868695 [main] DEBUG - Everything's going well 5758748 [main] ERROR - maybe not quite...
The reader might want to take a look at other Converter
implementations such as MDCConverter
to learn about more complex behaviours, such as option handling. For creating your own coloring schemes have a look at HighlightingCompositeConverter
.
HTMLLayout
采用html样式输出,不介绍,将配置文件列出,可以与patternlayout的配置文件进行一下对比使用:可以看出encoder和layout的实现类都要进行设置,而之前的都是采用了默认设置,因此会更加简洁。
<configuration debug="true">
<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>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
Log4j XMLLayout
XML的输出方式,不介绍,配置文件如下,做一下对比。
View as .groovy<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>test.xml</file> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.log4j.XMLLayout"> <locationInfo>true</locationInfo> </layout> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
后续的logback-access部分不进行介绍。layout部分至此结束。