Logback介绍

Logback 分为三个模块:Core、Classic 和 Access。Core模块是其他两个模块的基础。 Classic模块扩展了core模块。 Classic模块相当于log4j的显著改进版。Logback-classic 直接实现了 SLF4J API。

要引入logback,由于Logback-classic依赖slf4j-api.jarlogback-core.jar,所以要把slf4j-api.jarlogback-core.jar、logback-classic.jar添加到要引入Logbac日志管理的项目的class path中.

Logback的配置

Logger、Appender和 Layout

Logback建立于三个主要类之上:Logger、Appender 和 Layout。Logger类是logback-classic模块的一部分,而Appender和Layout接口来自logback-core。作为一个多用途模块,logback-core 不包含任何logger。

Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、MySQL、 PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。Layout 负责把事件转换成字符串,格式化的日志信息的输出。

Logger context

各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各logger。

如果 logger的名称带上一个点号后是另外一个 logger的名称的前缀,那么,前者就被称为后者的祖先。如果logger与其后代 logger之间没有其他祖先,那么,前者就被称为子logger 之父。比如,名为"com.foo""的 logger 是名为"com.foo.Bar"之父。root logger 位于 logger 等级的最顶端,root logger 可以通过其名称取得,如下所示:

Logger rootLogger =LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以logger 名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个logger对象的引用。

有效级别与级别继承

Logger 可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于ch.qos.logback.classic.Level类。如果 logger没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。root logger 默认级别是 DEBUG。

打印方法与基本选择规则

打印方法决定记录请求的级别。例如,如果 L 是一个 logger 实例,那么,语句 L.info("..")是一条级别为 INFO 的记录语句。记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。

记录请求级别为 p,其 logger的有效级别为 q,只有则当 p>=q时,该请求才会被执行。

该规则是 logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR。

Logger、Appenders及layouts的关系

一个 logger 可以被关联多个 appender。方法 addAppender() 为指定的 logger 添加一个appender。对于 logger 的每个启用了的记录请求,都将被发送到 logger 里的全部 appender 及更高等级的 appender。换句话说,appender叠加性地继承了 logger 的层次等级。

Logger L的记录语句的输出会发送给 L及其祖先的全部 appender。如果 logger L的某个祖先 P设置叠加性标识为 false,那么,L的输出会发送给L 与 P之间(含P)的所有 appender,但不会发送给P的任何祖先的appender。

Logger 的叠加性默认为 true。如果希望定制输出格式。这时为 appender 关联一个 layout 即可。Layout负责根据用户意愿对记录请求进行格式化,appender 负责将格式化化后的输出发送到目的地。

例如,转换模式"%-4relative [%thread] %-5level %logger{32} - %msg%n"在 PatternLayout里会输出形如:

176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

第一个字段是自程序启动以来的逝去时间,单位是毫秒。

第二个地段发出记录请求的线程。

第三个字段是记录请求的级别。

第四个字段是与记录请求关联的 logger 的名称。

"-"之后是请求的消息文字。

Logback的默认配置

如果配置文件 logback-test.xml 和 logback.xml 都不存在,那么 logback 默认地会调用BasicConfigurator ,创建一个最小化配置。最小化配置由一个关联到根 logger 的ConsoleAppender组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 的PatternLayoutEncoder 进行格式化。root logger 默认级别是 DEBUG。

logback配置文件

Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素。

Logback默认配置的采用的步骤

1. 尝试在 classpath 下查找文件 logback-test.xml;

2. 如果文件不存在,则查找文件 logback.xml;

3. 如果两个文件都不存在,logback 用 Bas icConfigurator 自动对自己进行配置,这会导致记录输出到控制台。

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

最简单的配置方法就是使用默认配置。以下是logback用BasicConfigurator 配置的简单例子:

package com.ttpod.chapters.configuration;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass MyApp1 {

finalstatic Logger logger = LoggerFactory.getLogger(MyApp1.class);

publicstaticvoid main(String[] args) {

logger.info("Entering application.");    //进行另一个application中

Foo foo = new Foo();

foo.doIt();        //执行其它中的日志输出方法

logger.info("Exiting application.");    //退出另一个application

}

}

该类定义了一个静态变量 logger,然后实例化一个 Foo 对象。Foo 类如下

package com.ttpod.chapters.configuration;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass Foo {

staticfinal Logger logger = LoggerFactory.getLogger(Foo.class);

publicvoid doIt() {

logger.debug("Did it again!");    //定义一个debug级别的日志输出

}

…………

}

自动打印警告和错误消息

当解析配置文件有警告或出错时,logback 会在控制台上自动打印状态数据。如果没有警告或错误,还是想检查logback 的内部状态的话,可以调用 StatusPrinter 的 print()方法。示例如下:

finalstatic Logger logger = LoggerFactory.getLogger(MyApp2.class);

publicstaticvoid main(String[] args) {

// 在当前环境中假设 SLF4J 已经被绑定到logback

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

// 打印logback的内部状态

StatusPrinter.print(lc);

…………

}

}

对应的配置文件:

<?xmlversion="1.0"encoding="UTF-8"?>

<configuration>

<!--定义一个名为STDOUT的appender,并将其关联到ch.qos.logback.core.ConsoleAppender-->

<appendername="STDOUT"

class="ch.qos.logback.core.ConsoleAppender">

<!-- encoders 作用是将logger事件转换成字节数组,并将字节数组写入到输出流-->

<encoder>

个字符宽度

%msg:日志消息,%n是换行符-->

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">    <!-- root logger,定义级别为debug-->

<appender-refref="STDOUT"/>    <!--将名为STDOUT的appender添加到root logger下-->

</root>

</configuration>

控制台输出结果如下:

…………

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Workspaces/MyEclipse%208.5/logback_test/WebRoot/WEB-INF/classes/logback.xml]

20:12:33,484 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]

20:12:33,484 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]

20:12:33,500 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG

20:12:33,593 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@cb6009 - Registering current configuration as safe fallback point

…………

Logback自定义配置

配置root logger

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

假设我们不想看到"com.ttpod.file"包里的任何组件的任何 DEBUG 信息,可以设置如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<configuration>

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<!-- 设置configuration下的logger的级别为INFO,默认是继承root logger的debug级别 -->

<loggername="chapters.configuration"level="INFO"/>

<!-- 严格来说, root logger 的level属性没有必要设置,因为 -->

<!-- root logger的级别被默认设置为DEBUG -->

<rootlevel="DEBUG">

<!--

在root标签内没有引入chapters.configuration,所有在此包下不会

显示任何组件的任何 DEBUG 信息

-->

<appender-refref="STDOUT"/>    <!-- 将appender引入到root logger -->

</root>

</configuration>

注意:由名为"chapters.configuration.Foo"的 logger 生成的 DEBUG 级别的信息都被屏蔽了.同样,也可以为任意数量的 logger 设置级别。

配置 Appenders

Appender 用<appender>元素配置,该元素必要属性 name 和 class。 name 属性指定 appender 的名称,class 属性指定 appender 类的全限定名。 <appender>元素可以包含零个或多个<layout>元素、零个或多个<encoder>元素和零个或多个<filter>元素。除了这三个常用元素之外,还可以包含 appender 类的任意数量的 javabean

属性。下图演示了常用结构,注意对 javabean 属性的支持在图中不可见。

记录输出到多个 appender 很简单,先定义各种 appender,然后在 logger 里进行引用,就行了。如下面的配置文件所示:

<configuration>

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>myApp.log</file>

<!-- encoders are assigned by default the type

ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>

</encoder>

</appender>

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="FILE"/>

<appender-refref="STDOUT"/>

</root>

</configuration>

该配置文件定义了两个 appender,分别是"FILE"和"STDOUT"。"FILE"这个 appender 把记录输出到文件"myapp.log ",它的 encoder 是PatternLayoutEncoder,输出了日期、级别、线程名、logger 名、文件名及记录请求的行号、消息和行分隔符。"STDOUT"这个 appender 把记录输出到控制台,它的 encoder 只是输出消息和行分隔符。 myApp.log文件内容如下:

2011-12-25 16:56:48,593 INFO [main] c.t.c.c.MyApp3 [MyApp3.java:48] Entering application.

2011-12-25 16:56:48,593 DEBUG [main] c.t.c.c.Foo [Foo.java:24] Did it again!

2011-12-25 16:56:48,593 INFO [main] c.t.c.c.MyApp3 [MyApp3.java:52] Exiting application.

注意每个 appender 都有自己的 encoder。Encoder 通常不能被多个 appender 共享,layout也是。所以,logback 的配置文件里没有共享 encoder 或 layout 的语法。

Appender累积

默认情况下,appender 是可累积的:logger 会把记录输出到它自身的 appender 和它所有祖先的appender。因此,把同一 appender 关联到多个 logger 会导致重复输出,如下面的配置文件会导致重复的输出:

<configuration>

<appendername="STDOUT"

class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<loggername="chapters.configuration">

<appender-refref="STDOUT"/>

</logger>

<rootlevel="debug">

<appender-refref="STDOUT"/> <!—这会导致重复输出-->

</root>

</configuration>

输出结果如下:

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Entering application.

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Entering application.

20:53:29.328 [main] DEBUG com.ttpod.chapters.configuration.Foo - Did it again!

20:53:29.328 [main] DEBUG com.ttpod.chapters.configuration.Foo - Did it again!

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Exiting application.

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Exiting application.

覆盖默认的累积行为

如果你觉得默认的累积行为不合适,可以设置叠加性标识为 false 以关闭它。这样的话,logger 树里的某个分支可以输出到与其他 logger 不同的 appender。

示例:叠加性标识

<configuration>

…………

<loggername="com.ttpod.chapters.configuration.Foo"additivity="false">

<appender-refref="FILE"/>

</logger>

<rootlevel="debug">

<appender-refref="STDOUT"/>

</root>

</configuration>

输出结果:

Entering application.

Exiting application.

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

Layout格式化输出日志

配置自定义 layout

配置自定义layout与配置其他layout是一样的。 FileAppender和其子类需要一个encoder。如链接中的例子:http://logback.qos.ch/xref/chapters/layouts/MySampleLayout.html

此类中定义了一个处理格式的输出类,为了满足自定义的格式化的输出,把包裹了 MySampleLayout 的LayoutWrappingEncoder 实例传递给FileAppender。下面是配置文件:

<configurationdebug="true">

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoderclass="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<!—自定义的格式化输出处理类-->

<layoutclass="com.ttpod.chapters.layouts.MySampleLayout"/>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="STDOUT"/>

</root>

</configuration>

输出结果如下:

250 DEBUG [main] com.ttpod.chapters.introduction.HelloWorld1 - Hello world.

layout转换符

它和C语言的printf方法非常类似。格式转换由普通字符和转换字符组合而成。转换字符由%开始,紧跟着的是可选的格式修饰符和转换字符标示。使用%前缀的表示符号将被转换到实际的内容。如name, level, date, thread name.可用的转换符有:

转换符

描述

c

调用日志事件的所在记录器的名字,如一个logger的名字是my.test.bbb.ccc,调用的是WARN级别的日志输出,那么输出的是输出my.test.bbb.ccc,可以在其右边指定了精度,如%c{2}那么输出的是bbb.ccc

C

调用日志事件的所在的类名,和c转换符一样,可以在右边指定宽度,如%C{2}输出%C{2}

d

日志调用所发生的时间,日期格式在其后跟着的大括号内的格式指定如%d{yyyy-MM-dd HH:mm:ss},我现在输出的结果是2011-07-11 21:05:22,推荐使用的是log4j本身提供的日期格式,如%d{ISO8601},%d{ABSOLUTE},%d{DATE}

F

所处所在文件名,如上面说C转换符的例子,输出结果是LayoutTest.java

l

是的日志事件发生的位置信息,这个和虚拟机的实现有点关系,一般境况下能得到类,方法,行数源文件等信息,

L

只是输出触发日志事件代码所在的行号,性能损耗会小很多。

m

显示应用给日志提供的其他信息,如消息。logger.warn("Message 2");那么%m将得到的是Message 2

M

输出调用者所在的方法名

n

换行,和\r \r\n有相同功能,能识别系统的换行符,自动转换成\r或者\r\n,log4j推荐使用这个转换符,而不是\r或者\r\n

p

输出调用的日志的级别,如我是调用logger.debug方法,那么级别就是debug

r

输出自应用启动后第一次调用logger的日志输出方法,到输出该log信息耗费的毫秒数

t

输出所在线程的名字

x

输出产生的日志事件的线程的NDC(嵌套诊断上下文)

X

输出与生成的日志事件的线程关联的MDC(映射诊断上下文)。X转换符括号之间放置了一个key,就像在%X {clientNumber}中的clientNumberkey 一样。在MDC correspondingvalue将被输出。

%

写上%%后将直接输出一个%符号

layout格式修饰符

个字符宽度。

个,数据有10个字符,那么前面两个字符会被删除。

字符,没有左边用空格填充

字符,没有右边用空格填充

字符,超过左边的截取掉

字符,最多30字符,填充或截取规则略

字符,最多30字符,填充或截取规则略

在程序里启用logback记录

三个必须步骤:

1. 配置 logback 环境。

2. 在每个需要执行记录的类里,调用 org.slf4j.LoggerFactory 类的 getLogger()方法获

取一个 Logger 实例,以当前类名或类本身作为参数。

3. 调用取得的 logger 实例的打印方法,即 debug()、info()、warn()和 error(),把记录

输出到配置里的各 appender。

直接调用JoranConfigurator

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

(com/ttpod/file/ MyApp1.java)

package com.ttpod.file;

import org.slf4j.Logger;                //用于声明Logger

import org.slf4j.LoggerFactory;     //用于获取Logger以及LoggerContext

import ch.qos.logback.classic.LoggerContext;         //用于声明LoggerContext

import ch.qos.logback.classic.joran.JoranConfigurator;//用于定义Logback的配置机制

import ch.qos.logback.core.joran.spi.JoranException; //用于定义JoranException

import ch.qos.logback.core.util.StatusPrinter; //用于打印logback的内部状态

publicclass MyApp1 {

finalstatic Logger logger = LoggerFactory.getLogger(MyApp1.class);    //定义一个全局的记录器,通过LoggerFactory获取

publicstaticvoid main(String[] args) {

//通过getILoggerFactory()方法得到logger上下文件环境,logback默认获得当前应用的logger context

LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();    //得到当前应用中logger上下文

try {

JoranConfigurator configurator = new JoranConfigurator();    //定义一个(JoranConfigurator)配置器

configurator.setContext(context);    //将当前应用的logger context的关联到到configurator对象

context.reset();                        //清除以前的配置器中的所有内容

//configurator.doConfigure(args[0]);    //接收从命令行传入的参数,加载配置文件,并设置到配置器

configurator.doConfigure("src/com/ttpod/file/MyApp1Config.xml");

//配置文件的路径:src/com/ttpod/file/MyApp1Config.xml

catch (JoranException je) {

logger.error("JoranException occur at:"+je.getMessage());    //将此处异常也记录到日志

je.printStackTrace();    //在控制打印出异常跟踪信息

}

//打印出logger context中的error和供气ing,在此处作用相当于catch中的je.printStackTrace();

StatusPrinter.printInCaseOfErrorsOrWarnings(context);

//流程进入有日志生成的类中

logger.info("Entering application.demo class Foo >>>");

Foo foo = new Foo();

foo.doIt();    //执行foo中的一个生成了日志的方法,输出日志

logger.info("Exiting application. demo class Foo <<<");

}

}

其它类Foo类,代码如下:

(com/ttpod/file/ Foo.java)

package com.ttpod.file;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass Foo {

staticfinal Logger logger = LoggerFactory.getLogger(Foo.class);

publicvoid doIt() {

logger.debug("test logger in other class!");

}

publicvoid createLoggingRequest() {

subMethodToCreateRequest();

}

//这个方法执行后创建一条异常信息

privatevoid subMethodToCreateRequest() {

logger.error("error-level request", new Exception("test exception"));

}

}

此程序通过参数传配置文件的名字,对程序的日志输出配置,本例如下:

(com/ttpod/file/ MyApp1Config.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 指定日志输出到文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>${destination}</file>

<encoder>

<!-- %msg表示日志信息,%n表示换行 -->

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="FILE"/><!—添加到根logger-->

</root>

</configuration>

对应的properties文件,嵌套的设置了日志文件的输出路径及文件名,如下:

(com/ttpod/file/ variables.properties)

#指定USER_HOME目录

USER_HOME=./log

#指定日志文件名

fileName=myApp.log

#生成日志文件的目的地

destination=${USER_HOME}/${fileName}

配置好后,右键MyApp3 -> run as… -> run configurations -> 选择MyApp3,及arguments ->

配置program Aguments:src/com/ttpod/MyApp1Config.xml,以指定配置文件,然后运行即可,即可在当前就应用的/log文件夹下生成myApp.log ,输出如下:

Entering application.demo class Foo >>>

test logger in other class!

Exiting application. demo class Foo <<<

运用滚动策略与触发策略

RollingFileAppender 继承 FileAppender,能够滚动记录文件。例如,RollingFileAppender能先记录到文件"log.txt",然后当符合某个条件时,变成记录到其他文件。 RollingFileAppender 有两个与之互动的重要子组件。第一个是RollingPolicy,负责滚动。第二个是 TriggeringPolicy,决定是否以及何时进行滚动。所以,RollingPolicy 负责"什么", TriggeringPolicy 负责"何时"。

要想 RollingFileAppender 起作用,必须同时设置 RollingPolicy 和 TriggeringPolicy。不过,如果RollingPolicy 也实现了 TriggeringPolicy 接口,那么只需要设置 RollingPolicy。

如下测试程序应用滚动与触发策略,来处理日志文件。

(com/ttpod/file/MyApp2.java)

package com.ttpod.file;

import java.util.Date;

publicclass MyApp2 {

static Timer t;

publicstaticvoid main(String[] args) {

// 调用LoggerFactory 类的静态方法getLogger取得一个Logger实例,

final Logger logOut = LoggerFactory.getLogger("SystemOut"); // 定义一个名为SystemOut的logger

final Logger logErr = LoggerFactory.getLogger("SystemErr"); // 定义一个名为SystemErr的logger

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

//configurator.doConfigure(args[0]);

configurator.doConfigure("src/com/ttpod/file/logback.xml");

catch (JoranException je) {

je.printStackTrace();

}

// 打印logback的内部状态

StatusPrinter.print(lc);    //固定部分

t = new Timer(); // 定义一个定时器

TimerTask tt = new TimerTask() { // 用定义器执行计划(schedule),并定义一个继承了TimerTask的匿名内部类

@Override

publicvoid run() {

for (int i = 100; i > 0; i--) {

// Timer t=null;

MyApp2.t.cancel();

logOut.info("测试Logback日志的使用"); // 定义了一个info级别的日志消息

logErr.error("发生错误"); // 定义了一个error级别的日志消息

}

System.out.println("~~~~~~~~~~~~~ ");

}

};

毫秒执行一次计划

}

}

// 没有自定义它的logback所需的配置文件,程序启动时会自动加载classpath目录下的查找

// logback-test.xml;如果没找到则logback-test.xml,则继续在classpath下查找lobback.xml。

此测试类,在定时器中定义了一个执行计划,按照执行计划生成日志文件。在file包中添加相的logback配置文件,如下:

(com/ttpod/file/logback.xml)

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPE configuration>

<configuration>

<jmxConfigurator />

<!-- 配置文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!--logback的版本必须是0.9.21以上才支持-->

<timestampkey="byDay"datePattern="yyyyMMdd"/>

<!-- 控制台输出日志 -->

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<layoutclass="ch.qos.logback.classic.PatternLayout">

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</layout>

</appender>

<!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件压缩(.zip)备份)-->

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<!-- 文件的路径及文件名 -->

<File>${USER_HOME}/SystemOut.log</File>

<!-- 定义窗口滚动策略 -->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<!-- 每触发一次,自动压缩日志文件,%i在1-3之间循环变化 -->

<FileNamePattern>${USER_HOME}/SystemOut_%i.log.zip</FileNamePattern>

<MinIndex>1</MinIndex><!-- %i的最小值为1-->

<MaxIndex>3</MaxIndex><!-- %i的最大值为3-->

</rollingPolicy>

<!-- 触发策略,通常rollingPolicy与triggeringPolicy联合使用 -->

<triggeringPolicy

class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<MaxFileSize>10KB</MaxFileSize>

</triggeringPolicy>

<!-- 格式化输出 -->

<!-- %d表示日期,格式为:yyyy-MM-dd HH:mm:ss.SSS ;%thread:线程名;

个字符的宽度显示级别; %logger:显示logger名;%msg:日志消息;%n:换行

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

</layout>

-->

<!-- 或者用下面的形式格式(推荐)-->

<encoder>

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</encoder>

<!-- 以html文件输出 -->

<!--

<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<layout class="ch.qos.logback.classic.html.HTMLLayout">

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</layout>

</encoder>

-->

</appender>

<!-- 输出ERROR级别的日志到文件(同样采用文件大小策略进行文件输出,超过指定大小对文件压缩(.zip)备份) -->

<appendername="FILE-ERROR"class="ch.qos.logback.core.rolling.RollingFileAppender">

<filterclass="ch.qos.logback.classic.filter.LevelFilter">

<level>ERROR</level><!-- 指定要对级别为ERROR日志信息过滤 -->

<OnMismatch>DENY</OnMismatch>     <!-- 如果不匹配,则拒绝处理 -->

<OnMatch>ACCEPT</OnMatch>         <!-- 如果匹配,则立即处理 -->

</filter>

<File>${USER_HOME}/SystemErr.log</File><!-- 文件名即路径 -->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<FileNamePattern>${USER_HOME}/SystemErr_%i.log.zip</FileNamePattern>

<MinIndex>1</MinIndex>

<MaxIndex>3</MaxIndex>

</rollingPolicy>

<triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<MaxFileSize>10KB</MaxFileSize>

</triggeringPolicy>

<!-- 格式化输出 -->

<!--

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

</layout>

-->

<encoder>

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</encoder>

</appender>

<!--这里指定logger name 是为jmx设置日志级别做铺垫 -->

<!-- 分别将STDOUT与FILE appender都添加到自定义的logger中,否则将不会输出到appender指定的目的地-->

<loggername="SystemOut"level="INFO">

<appender-refref="STDOUT"/>

<appender-refref="FILE"/>

</logger>

<loggername="SystemErr"level="INFO">

<appender-refref="STDOUT"/>

<appender-refref="FILE-ERROR"/>

</logger>

</configuration>

执行以上程序,在控制台也会不断的输出以下日志信息:

14:42:34.875 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:34.875 [Timer-0] ERROR SystemErr - 发生错误

14:42:34.937 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:34.937 [Timer-0] ERROR SystemErr - 发生错误

14:42:35.000 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:35.000 [Timer-0] ERROR SystemErr - 发生错误

14:42:35.062 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:35.062 [Timer-0] ERROR SystemErr - 发生错误

生成的日志文件如下图:

FixedWindowRollingPolicy当发生滚动时,FixedWindowRollingPolicy 根据如下固定窗口(window)算法重命名文件。选项"fileNamePattern"代表归档(滚动)记录文件的文件名模式。该选项是必需的,且必需在模式的某处包含标志"%i"。如示例3中的MyApp3-RollingFixedWindow.xml 。

TimeBasedRollingPolicy或许是最受流行的滚动策略。它根据时间来制定滚动策略,例如根据日或月。TimeBasedRollingPolicy 既负责滚动也负责触发滚动。实际上,TimeBasedRollingPolicy 同时实现了 RollingPolicy 接口和 TriggeringPolicy 接口。和 FixedWindowRollingPolicy一样,TimeBasedRollingPolicy 也支持自动压缩文件。如果"fileNamePattern"选项以".gz"或".zip"结尾,就表示需要压缩。如示例3中的MyApp3-RollingTimeBased.xml 。

SizeAndTimeBasedFNATP按照日期进行归档的同时限制每个记录文件的大小,特别是当后处理工具对记录文件大小有限制时。Logback 为此提供了 SizeAndTimeBasedFNATP ,它是TimeBasedRollingPolicy 的子组件,FNATP 代表"FNATP stands for File Naming And Triggering Policy"。下面的例子MyApp3-sizeAndTime.xml演示了基于大小和时间的记录文件归档。

(com/ttpod/file/MyApp3.java)

package com.ttpod.file;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

import ch.qos.logback.classic.LoggerContext;

import ch.qos.logback.classic.joran.JoranConfigurator;

import ch.qos.logback.core.joran.spi.JoranException;

import ch.qos.logback.core.util.StatusPrinter;

import com.ttpod.file.Foo;;

publicclass MyApp3 {

publicstaticvoid main(String[] args) throws InterruptedException {

Logger logger = (Logger) LoggerFactory.getLogger(MyApp3.class);

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

configurator.doConfigure(args[0]);

catch (JoranException je) {

je.printStackTrace();

}    //固定部分

//打印logback的内部状态

StatusPrinter.print(lc);

logger.debug("**Hello {}", new Foo());

//客户端的每个记录请求添加唯一戳(uniquely stamp),运用MDC(Mapped Diagnostic Context)

//在分布式应用程序中,可以区分并处理不同客户端的记录输出,而不增加logger的开销

MDC.put("testKey", "testValueFromMDC");        //在appender中用%X{testKey},可以输出MDC中test对应的值

MDC.put("testKey2", "value2");

for (int i = 0; i < 10; i++) {

logger.debug("logging statement " + i);

Thread.sleep(100);

}

Foo foo = new Foo();

foo.createLoggingRequest();

}

}

配置文件:

(com/ttpod/file/MyApp3-RollingFixedWindow.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/testFile.log</file>

<!--滚动策略-->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<!—自动压缩备份-->

<fileNamePattern>${USER_HOME}/testFile.%i.log.zip</fileNamePattern>

<minIndex>1</minIndex>

<maxIndex>3</maxIndex><!--%i在1-3之间循环变化-->

</rollingPolicy>

<!—触发策略-->

<triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<!--日志文件大小限制在5kB以内,否则就触发滚动-->

<maxFileSize>5kB</maxFileSize>

</triggeringPolicy>

<encoder><!--格式化输出-->

<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

多次运行MyApp3,logback配置参数传入MyApp3-RollingFixedWindow.xml,即可发现在当前应用的log目录下生成了如:testFile.1.log.zip、testFile.2.log.zip、testFile.3.log.zip,testFile.log文件。备份的压缩文件中的日志文件的文件名默认后缀跟了归档日期,如下图:

(com/ttpod/file/MyApp3-RollingTimeBased.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 基于时间的滚动策略 -->

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/logFile.log</file>

<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<!-- 每天滚动

<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

-->

<!-- 每分钟滚动 -->

<fileNamePattern>${USER_HOME}/logFile.%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>

天-->

<maxHistory>30</maxHistory>

</rollingPolicy>

<encoder>

<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

<!--

运行:

java chapters.appenders.ConfigurationTester

src/com/ttpod/chapters/appenders/conf/logback-RollingTimeBased.xml

-->

多次运行MyApp3,logback配置参数传入MyApp3-RollingTimeBased.xml,若设置一个执行计谋,程序不停止,则每分钟可触发一次日志备份,即可发现在当前应用的log目录下生成了按配置的格式的文件,如:

(com/ttpod/file/MyApp3-sizeAndTime.xml)

<configuration>

<!-- 基于时间与大小的归档 -->

<statusListenerclass="ch.qos.logback.core.status.OnConsoleStatusListener"/>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 基于时间与大小的归档 -->

<appendername="ROLLING"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/mylog.txt</file>

<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<!-- 每天滚动

<fileNamePattern>${USER_HOME}/mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>

-->

<minIndex>1</minIndex>

<maxIndex>3</maxIndex>

<!-- 每分钟滚动 -->

<fileNamePattern>${USER_HOME}/mylog-%d{yyyy-MM-dd_HH-mm}.%i.txt</fileNamePattern>

天-->

<maxHistory>30</maxHistory>

<timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

<!-- 当文件大小超过5kB时触发滚动,这里设置5kb只是便于测试 -->

<maxFileSize>5kB</maxFileSize>

</timeBasedFileNamingAndTriggeringPolicy>

</rollingPolicy>

<encoder>

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="ROLLING"/>

</root>

</configuration>

运行时,logback配置参数传入src/com/ttpod/file/MyApp3-sizeAndTime.xml即可。当日志文件超过5kB时,或到另一天时,程序又被执行了,则会触发滚动,按照此前配置规则备原来的文件。输出的文件如下图所示:

以网页形式输出日志文件

HTMLLayout 与 logback-classic 里的 HTMLLayout 相似。默认情况下,PatternLayout 创建包含下列数据的表格:远程IP;日期;请求的URL;状态吗;Content Length 。

以下有个示例,演示以HTMLLayout格式化后,以htm文件输出日志。如下:

(com/ttpod/html/HTMLTest1.java)

package com.ttpod.html;

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;

publicclass HtmlTest1 {

publicstaticvoid main(String[] args) throws InterruptedException {

Logger logger = LoggerFactory.getLogger(HtmlTest1.class);

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

configurator.doConfigure(args[0]);

//configurator.doConfigure("./src/com/ttpod/html/htmlLayoutConfig1.xml");

catch (JoranException je) {

je.printStackTrace();    //固定部分

}

StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

for (int i = 0; i < 6; i++) {

if (i % 5 == 0) {

logger.warn("a warning message " + i);    //声明一条warn级别的日志消息

//logger.info("a warning message " + i);

else {

logger.debug("hello world number" + i);

}

}

logger.error("Finish off with fireworks", new Exception("Just testing"));

}

}

用以下配置文件格式化输出到properties文件指向的目的地。

(com/ttpod/html/htmlLayoutConfig1.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/html/variables.properties"/>

<!-- 指定日志输出到文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<encoderclass="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<layoutclass="ch.qos.logback.classic.html.HTMLLayout">

<pattern>%relative%thread%mdc%level%logger%msg</pattern>

</layout>

</encoder>

<file>${destination}</file>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

<!-- 以以下方式运行

java com.ttpod.html.HtmlTest1 src/com/ttpod/html/htmlLayoutConfig1.xml

-->

属性文件内容如下:

(com/ttpod/html/ variables.properties)

#指定USER_HOME目录

USER_HOME=./log

#指定日志文件名

fileName=htmlTest.htm

#生成日志文件的目的地

destination=${USER_HOME}/${fileName}

运行html/HTMLTest1,并传入配置参数:src/com/ttpod/html/htmlLayoutConfig1.xml,可以发现在在当前应用的/log目录下生成一个htmlTest.htm日志文件。输出日志结果如下图:

以邮件形式输出日志

以邮件形式输出日志,依赖于SMTPAppender ,SMTPAppender在固定大小的缓冲里积累记录时间,当用户指定的事件发生后,就通过email发出这些事件。默认情况下,email发送是由级别为 ERROR 或更高级别的记录事件触发的。

下面的演示程序 chapters.appenders.mail.EMail,生成大量记录消息,然后生成一个错误消息。该程序有两个参数,第一个是需要生成的记录消息的数量,第二个是 logback 配置文件。该程序生成的最后一条记录事件,即 ERROR 级别的事件,将触发 email的发送。

(com/ttpod/mail/Email.java)

package com.ttpod.mail;

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.util.StatusPrinter;

publicclass EMail {

staticpublicvoid main(String[] args) throws Exception {

if (args.length != 2) {

usage("Wrong number of arguments.");

}

int runLength = Integer.parseInt(args[0]);

String configFile = args[1];

//int runLength = Integer.parseInt("300");

//String configFile="src/com/ttpod/mail/gmailSSL.xml";

//String configFile = "src/com/ttpod/mail/gmailSTARTTLS.xml";

//自定义配置文件,得到日志固定格式

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

JoranConfigurator configurator = new JoranConfigurator();

lc.reset();

configurator.setContext(lc);

configurator.doConfigure(configFile);

Logger logger = LoggerFactory.getLogger(EMail.class);

for (int i = 1; i <= runLength; i++) {

if ((i % 10) < 9) {

logger.debug("This is a debug message. Message number: " + i);

else {

logger.warn("This is a warning message. Message number: " + i);

}

}

//定义一条error级别的日志输出

logger.error("At last an error.", new Exception("Just testing"));

//打印logger内部状态

StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

}

staticvoid usage(String msg) {    //当参数传入错误时,提供的处理惯例

System.err.println(msg);

System.err.println("Usage: java " + EMail.class.getName() +

" runLength configFile\n" +

" runLength (integer) the number of logs to generate\n" +

" configFile a logback configuration file in XML format." +

" XML files must have a '.xml' extension.");

System.exit(1);    //退出程序

}

}

如下配置文件从属性文件中读取发送邮件定义的属性,配置由邮件输出日志,如下:

<configuration>

<!-- 指定属性文件的位置 -->

<propertyfile="src/com/ttpod/mail/gmailSSL.properties"/>

<!-- 目的指定向email的appender -->

<appendername="EMAIL"class="ch.qos.logback.classic.net.SMTPAppender">

<SMTPHost>${SMTPHOST}</SMTPHost><!-- 邮件服务器地址 -->

<SMTPPort>${SMTPPORT}</SMTPPort><!--SMTPPORT端口-->

<SSL>true</SSL>

<Username>${EMAIL_USERNAME}</Username><!-- 用户名 -->

<Password>${EMAIL_PASSWORD}</Password><!-- 密码 -->

<To>${EMAIL-DESTINATION}</To><!-- 目标接收人 -->

<To>${ANOTHER_EMAIL_DESTINATION}</To><!-- additional destinations are possible -->

<From>${EMAIL_USERNAME}</From><!-- 发件人 -->

<Subject>TESTING: %logger{20} - %m</Subject><!-- 主题 -->

<encoder><!-- 一般采用这种方式格式化输出 -->

<layoutclass="ch.qos.logback.classic.html.HTMLLayout">

<!-- 采用什么渲染方式,这采取的是HTML方式 -->

<Pattern>%date %-5level %logger - %message%n</Pattern>

</layout>

</encoder>

<!--

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%date %-5level %logger - %message%n</Pattern>

</layout>

-->

</appender>

<!-- 输出到控制台,以便将日志也打印到控制台-->

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<!-- 输出到文件,将日志记录到本地文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>./log/mailtest.log</file>

<encoder>

<!-- %msg表示日志信息,%n表示换行 -->

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="EMAIL"/>

<appender-refref="STDOUT"/>

<appender-refref="FILE"></appender-ref>

</root>

</configuration>

属性文件如下设置,以供自定的logback配置文件读取。

SMTPHOST=smtp.gmail.com

SMTPPORT=

EMAIL_USERNAME = someone123@gmail.com

EMAIL_PASSWORD = password123

EMAIL-DESTINATION = someone456@ttpod.com

ANOTHER_EMAIL_DESTINATION = some456@126.com

传入配置文件参数,运行,当出现也error级别以及以上级别的日志时,就会触发日志发送到邮件,但目前此测试例子,可能由于本地tomcat服务器缓存邮件,以致邮件无法发送到外网服务器,所以在这里没给出具体发送成功的结果。但可以通过它生成对应的HTML文件,可以看到邮件内容,如下图:

如果未指定选项"Evaluator",则 SMTPAppender 被默认分配一个OnErrorEveluator(ch.qos.logback.classic.boolex.OnErrorEvaluator)实例,当遇到级别为ERROR 或更高级别

的事件后,触发 email 传输。此处最后一条消息是一个error级别的记录,触发了邮件的发送。

另外,还可以将日志写入数据库要把记录事件写入数据库,依赖于DBAppender ,DBAppender把记录事件写入数据库的三张表。三张表分别是 logging_event、logging_event_property 和logging_event_exception。在使用 DBAppender 之前,这三张表必须已经被创建。关于将日志记录写入到数据库中,更多可参考logback手册中的DBAppender。

Logback介绍

Logback 分为三个模块:Core、Classic 和 Access。Core模块是其他两个模块的基础。 Classic模块扩展了core模块。 Classic模块相当于log4j的显著改进版。Logback-classic 直接实现了 SLF4J API。

要引入logback,由于Logback-classic依赖slf4j-api.jarlogback-core.jar,所以要把slf4j-api.jarlogback-core.jar、logback-classic.jar添加到要引入Logbac日志管理的项目的class path中.

Logback的配置

Logger、Appender和 Layout

Logback建立于三个主要类之上:Logger、Appender 和 Layout。Logger类是logback-classic模块的一部分,而Appender和Layout接口来自logback-core。作为一个多用途模块,logback-core 不包含任何logger。

Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、MySQL、 PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。Layout 负责把事件转换成字符串,格式化的日志信息的输出。

Logger context

各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各logger。

如果 logger的名称带上一个点号后是另外一个 logger的名称的前缀,那么,前者就被称为后者的祖先。如果logger与其后代 logger之间没有其他祖先,那么,前者就被称为子logger 之父。比如,名为"com.foo""的 logger 是名为"com.foo.Bar"之父。root logger 位于 logger 等级的最顶端,root logger 可以通过其名称取得,如下所示:

Logger rootLogger =LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以logger 名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个logger对象的引用。

有效级别与级别继承

Logger 可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于ch.qos.logback.classic.Level类。如果 logger没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。root logger 默认级别是 DEBUG。

打印方法与基本选择规则

打印方法决定记录请求的级别。例如,如果 L 是一个 logger 实例,那么,语句 L.info("..")是一条级别为 INFO 的记录语句。记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。

记录请求级别为 p,其 logger的有效级别为 q,只有则当 p>=q时,该请求才会被执行。

该规则是 logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR。

Logger、Appenders及layouts的关系

一个 logger 可以被关联多个 appender。方法 addAppender() 为指定的 logger 添加一个appender。对于 logger 的每个启用了的记录请求,都将被发送到 logger 里的全部 appender 及更高等级的 appender。换句话说,appender叠加性地继承了 logger 的层次等级。

Logger L的记录语句的输出会发送给 L及其祖先的全部 appender。如果 logger L的某个祖先 P设置叠加性标识为 false,那么,L的输出会发送给L 与 P之间(含P)的所有 appender,但不会发送给P的任何祖先的appender。

Logger 的叠加性默认为 true。如果希望定制输出格式。这时为 appender 关联一个 layout 即可。Layout负责根据用户意愿对记录请求进行格式化,appender 负责将格式化化后的输出发送到目的地。

例如,转换模式"%-4relative [%thread] %-5level %logger{32} - %msg%n"在 PatternLayout里会输出形如:

176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

第一个字段是自程序启动以来的逝去时间,单位是毫秒。

第二个地段发出记录请求的线程。

第三个字段是记录请求的级别。

第四个字段是与记录请求关联的 logger 的名称。

"-"之后是请求的消息文字。

Logback的默认配置

如果配置文件 logback-test.xml 和 logback.xml 都不存在,那么 logback 默认地会调用BasicConfigurator ,创建一个最小化配置。最小化配置由一个关联到根 logger 的ConsoleAppender组成。输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 的PatternLayoutEncoder 进行格式化。root logger 默认级别是 DEBUG。

logback配置文件

Logback 配置文件的语法非常灵活。正因为灵活,所以无法用 DTD 或 XML schema 进行定义。尽管如此,可以这样描述配置文件的基本结构:以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素。

Logback默认配置的采用的步骤

1. 尝试在 classpath 下查找文件 logback-test.xml;

2. 如果文件不存在,则查找文件 logback.xml;

3. 如果两个文件都不存在,logback 用 Bas icConfigurator 自动对自己进行配置,这会导致记录输出到控制台。

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

最简单的配置方法就是使用默认配置。以下是logback用BasicConfigurator 配置的简单例子:

package com.ttpod.chapters.configuration;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass MyApp1 {

finalstatic Logger logger = LoggerFactory.getLogger(MyApp1.class);

publicstaticvoid main(String[] args) {

logger.info("Entering application.");    //进行另一个application中

Foo foo = new Foo();

foo.doIt();        //执行其它中的日志输出方法

logger.info("Exiting application.");    //退出另一个application

}

}

该类定义了一个静态变量 logger,然后实例化一个 Foo 对象。Foo 类如下

package com.ttpod.chapters.configuration;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass Foo {

staticfinal Logger logger = LoggerFactory.getLogger(Foo.class);

publicvoid doIt() {

logger.debug("Did it again!");    //定义一个debug级别的日志输出

}

…………

}

自动打印警告和错误消息

当解析配置文件有警告或出错时,logback 会在控制台上自动打印状态数据。如果没有警告或错误,还是想检查logback 的内部状态的话,可以调用 StatusPrinter 的 print()方法。示例如下:

finalstatic Logger logger = LoggerFactory.getLogger(MyApp2.class);

publicstaticvoid main(String[] args) {

// 在当前环境中假设 SLF4J 已经被绑定到logback

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

// 打印logback的内部状态

StatusPrinter.print(lc);

…………

}

}

对应的配置文件:

<?xmlversion="1.0"encoding="UTF-8"?>

<configuration>

<!--定义一个名为STDOUT的appender,并将其关联到ch.qos.logback.core.ConsoleAppender-->

<appendername="STDOUT"

class="ch.qos.logback.core.ConsoleAppender">

<!-- encoders 作用是将logger事件转换成字节数组,并将字节数组写入到输出流-->

<encoder>

个字符宽度

%msg:日志消息,%n是换行符-->

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">    <!-- root logger,定义级别为debug-->

<appender-refref="STDOUT"/>    <!--将名为STDOUT的appender添加到root logger下-->

</root>

</configuration>

控制台输出结果如下:

…………

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]

20:12:33,359 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Workspaces/MyEclipse%208.5/logback_test/WebRoot/WEB-INF/classes/logback.xml]

20:12:33,484 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]

20:12:33,484 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]

20:12:33,500 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG

20:12:33,593 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.

20:12:33,593 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@cb6009 - Registering current configuration as safe fallback point

…………

Logback自定义配置

配置root logger

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

假设我们不想看到"com.ttpod.file"包里的任何组件的任何 DEBUG 信息,可以设置如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<configuration>

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<!-- 设置configuration下的logger的级别为INFO,默认是继承root logger的debug级别 -->

<loggername="chapters.configuration"level="INFO"/>

<!-- 严格来说, root logger 的level属性没有必要设置,因为 -->

<!-- root logger的级别被默认设置为DEBUG -->

<rootlevel="DEBUG">

<!--

在root标签内没有引入chapters.configuration,所有在此包下不会

显示任何组件的任何 DEBUG 信息

-->

<appender-refref="STDOUT"/>    <!-- 将appender引入到root logger -->

</root>

</configuration>

注意:由名为"chapters.configuration.Foo"的 logger 生成的 DEBUG 级别的信息都被屏蔽了.同样,也可以为任意数量的 logger 设置级别。

配置 Appenders

Appender 用<appender>元素配置,该元素必要属性 name 和 class。 name 属性指定 appender 的名称,class 属性指定 appender 类的全限定名。 <appender>元素可以包含零个或多个<layout>元素、零个或多个<encoder>元素和零个或多个<filter>元素。除了这三个常用元素之外,还可以包含 appender 类的任意数量的 javabean

属性。下图演示了常用结构,注意对 javabean 属性的支持在图中不可见。

记录输出到多个 appender 很简单,先定义各种 appender,然后在 logger 里进行引用,就行了。如下面的配置文件所示:

<configuration>

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>myApp.log</file>

<!-- encoders are assigned by default the type

ch.qos.logback.classic.encoder.PatternLayoutEncoder -->

<encoder>

<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>

</encoder>

</appender>

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="FILE"/>

<appender-refref="STDOUT"/>

</root>

</configuration>

该配置文件定义了两个 appender,分别是"FILE"和"STDOUT"。"FILE"这个 appender 把记录输出到文件"myapp.log ",它的 encoder 是PatternLayoutEncoder,输出了日期、级别、线程名、logger 名、文件名及记录请求的行号、消息和行分隔符。"STDOUT"这个 appender 把记录输出到控制台,它的 encoder 只是输出消息和行分隔符。 myApp.log文件内容如下:

2011-12-25 16:56:48,593 INFO [main] c.t.c.c.MyApp3 [MyApp3.java:48] Entering application.

2011-12-25 16:56:48,593 DEBUG [main] c.t.c.c.Foo [Foo.java:24] Did it again!

2011-12-25 16:56:48,593 INFO [main] c.t.c.c.MyApp3 [MyApp3.java:52] Exiting application.

注意每个 appender 都有自己的 encoder。Encoder 通常不能被多个 appender 共享,layout也是。所以,logback 的配置文件里没有共享 encoder 或 layout 的语法。

Appender累积

默认情况下,appender 是可累积的:logger 会把记录输出到它自身的 appender 和它所有祖先的appender。因此,把同一 appender 关联到多个 logger 会导致重复输出,如下面的配置文件会导致重复的输出:

<configuration>

<appendername="STDOUT"

class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<loggername="chapters.configuration">

<appender-refref="STDOUT"/>

</logger>

<rootlevel="debug">

<appender-refref="STDOUT"/> <!—这会导致重复输出-->

</root>

</configuration>

输出结果如下:

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Entering application.

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Entering application.

20:53:29.328 [main] DEBUG com.ttpod.chapters.configuration.Foo - Did it again!

20:53:29.328 [main] DEBUG com.ttpod.chapters.configuration.Foo - Did it again!

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Exiting application.

20:53:29.328 [main] INFO c.t.chapters.configuration.MyApp2 - Exiting application.

覆盖默认的累积行为

如果你觉得默认的累积行为不合适,可以设置叠加性标识为 false 以关闭它。这样的话,logger 树里的某个分支可以输出到与其他 logger 不同的 appender。

示例:叠加性标识

<configuration>

…………

<loggername="com.ttpod.chapters.configuration.Foo"additivity="false">

<appender-refref="FILE"/>

</logger>

<rootlevel="debug">

<appender-refref="STDOUT"/>

</root>

</configuration>

输出结果:

Entering application.

Exiting application.

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

Layout格式化输出日志

配置自定义 layout

配置自定义layout与配置其他layout是一样的。 FileAppender和其子类需要一个encoder。如链接中的例子:http://logback.qos.ch/xref/chapters/layouts/MySampleLayout.html

此类中定义了一个处理格式的输出类,为了满足自定义的格式化的输出,把包裹了 MySampleLayout 的LayoutWrappingEncoder 实例传递给FileAppender。下面是配置文件:

<configurationdebug="true">

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoderclass="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<!—自定义的格式化输出处理类-->

<layoutclass="com.ttpod.chapters.layouts.MySampleLayout"/>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="STDOUT"/>

</root>

</configuration>

输出结果如下:

250 DEBUG [main] com.ttpod.chapters.introduction.HelloWorld1 - Hello world.

layout转换符

它和C语言的printf方法非常类似。格式转换由普通字符和转换字符组合而成。转换字符由%开始,紧跟着的是可选的格式修饰符和转换字符标示。使用%前缀的表示符号将被转换到实际的内容。如name, level, date, thread name.可用的转换符有:

转换符

描述

c

调用日志事件的所在记录器的名字,如一个logger的名字是my.test.bbb.ccc,调用的是WARN级别的日志输出,那么输出的是输出my.test.bbb.ccc,可以在其右边指定了精度,如%c{2}那么输出的是bbb.ccc

C

调用日志事件的所在的类名,和c转换符一样,可以在右边指定宽度,如%C{2}输出%C{2}

d

日志调用所发生的时间,日期格式在其后跟着的大括号内的格式指定如%d{yyyy-MM-dd HH:mm:ss},我现在输出的结果是2011-07-11 21:05:22,推荐使用的是log4j本身提供的日期格式,如%d{ISO8601},%d{ABSOLUTE},%d{DATE}

F

所处所在文件名,如上面说C转换符的例子,输出结果是LayoutTest.java

l

是的日志事件发生的位置信息,这个和虚拟机的实现有点关系,一般境况下能得到类,方法,行数源文件等信息,

L

只是输出触发日志事件代码所在的行号,性能损耗会小很多。

m

显示应用给日志提供的其他信息,如消息。logger.warn("Message 2");那么%m将得到的是Message 2

M

输出调用者所在的方法名

n

换行,和\r \r\n有相同功能,能识别系统的换行符,自动转换成\r或者\r\n,log4j推荐使用这个转换符,而不是\r或者\r\n

p

输出调用的日志的级别,如我是调用logger.debug方法,那么级别就是debug

r

输出自应用启动后第一次调用logger的日志输出方法,到输出该log信息耗费的毫秒数

t

输出所在线程的名字

x

输出产生的日志事件的线程的NDC(嵌套诊断上下文)

X

输出与生成的日志事件的线程关联的MDC(映射诊断上下文)。X转换符括号之间放置了一个key,就像在%X {clientNumber}中的clientNumberkey 一样。在MDC correspondingvalue将被输出。

%

写上%%后将直接输出一个%符号

layout格式修饰符

个字符宽度。

个,数据有10个字符,那么前面两个字符会被删除。

字符,没有左边用空格填充

字符,没有右边用空格填充

字符,超过左边的截取掉

字符,最多30字符,填充或截取规则略

字符,最多30字符,填充或截取规则略

在程序里启用logback记录

三个必须步骤:

1. 配置 logback 环境。

2. 在每个需要执行记录的类里,调用 org.slf4j.LoggerFactory 类的 getLogger()方法获

取一个 Logger 实例,以当前类名或类本身作为参数。

3. 调用取得的 logger 实例的打印方法,即 debug()、info()、warn()和 error(),把记录

输出到配置里的各 appender。

直接调用JoranConfigurator

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

(com/ttpod/file/ MyApp1.java)

package com.ttpod.file;

import org.slf4j.Logger;                //用于声明Logger

import org.slf4j.LoggerFactory;     //用于获取Logger以及LoggerContext

import ch.qos.logback.classic.LoggerContext;         //用于声明LoggerContext

import ch.qos.logback.classic.joran.JoranConfigurator;//用于定义Logback的配置机制

import ch.qos.logback.core.joran.spi.JoranException; //用于定义JoranException

import ch.qos.logback.core.util.StatusPrinter; //用于打印logback的内部状态

publicclass MyApp1 {

finalstatic Logger logger = LoggerFactory.getLogger(MyApp1.class);    //定义一个全局的记录器,通过LoggerFactory获取

publicstaticvoid main(String[] args) {

//通过getILoggerFactory()方法得到logger上下文件环境,logback默认获得当前应用的logger context

LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();    //得到当前应用中logger上下文

try {

JoranConfigurator configurator = new JoranConfigurator();    //定义一个(JoranConfigurator)配置器

configurator.setContext(context);    //将当前应用的logger context的关联到到configurator对象

context.reset();                        //清除以前的配置器中的所有内容

//configurator.doConfigure(args[0]);    //接收从命令行传入的参数,加载配置文件,并设置到配置器

configurator.doConfigure("src/com/ttpod/file/MyApp1Config.xml");

//配置文件的路径:src/com/ttpod/file/MyApp1Config.xml

catch (JoranException je) {

logger.error("JoranException occur at:"+je.getMessage());    //将此处异常也记录到日志

je.printStackTrace();    //在控制打印出异常跟踪信息

}

//打印出logger context中的error和供气ing,在此处作用相当于catch中的je.printStackTrace();

StatusPrinter.printInCaseOfErrorsOrWarnings(context);

//流程进入有日志生成的类中

logger.info("Entering application.demo class Foo >>>");

Foo foo = new Foo();

foo.doIt();    //执行foo中的一个生成了日志的方法,输出日志

logger.info("Exiting application. demo class Foo <<<");

}

}

其它类Foo类,代码如下:

(com/ttpod/file/ Foo.java)

package com.ttpod.file;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

publicclass Foo {

staticfinal Logger logger = LoggerFactory.getLogger(Foo.class);

publicvoid doIt() {

logger.debug("test logger in other class!");

}

publicvoid createLoggingRequest() {

subMethodToCreateRequest();

}

//这个方法执行后创建一条异常信息

privatevoid subMethodToCreateRequest() {

logger.error("error-level request", new Exception("test exception"));

}

}

此程序通过参数传配置文件的名字,对程序的日志输出配置,本例如下:

(com/ttpod/file/ MyApp1Config.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 指定日志输出到文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>${destination}</file>

<encoder>

<!-- %msg表示日志信息,%n表示换行 -->

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="FILE"/><!—添加到根logger-->

</root>

</configuration>

对应的properties文件,嵌套的设置了日志文件的输出路径及文件名,如下:

(com/ttpod/file/ variables.properties)

#指定USER_HOME目录

USER_HOME=./log

#指定日志文件名

fileName=myApp.log

#生成日志文件的目的地

destination=${USER_HOME}/${fileName}

配置好后,右键MyApp3 -> run as… -> run configurations -> 选择MyApp3,及arguments ->

配置program Aguments:src/com/ttpod/MyApp1Config.xml,以指定配置文件,然后运行即可,即可在当前就应用的/log文件夹下生成myApp.log ,输出如下:

Entering application.demo class Foo >>>

test logger in other class!

Exiting application. demo class Foo <<<

运用滚动策略与触发策略

RollingFileAppender 继承 FileAppender,能够滚动记录文件。例如,RollingFileAppender能先记录到文件"log.txt",然后当符合某个条件时,变成记录到其他文件。 RollingFileAppender 有两个与之互动的重要子组件。第一个是RollingPolicy,负责滚动。第二个是 TriggeringPolicy,决定是否以及何时进行滚动。所以,RollingPolicy 负责"什么", TriggeringPolicy 负责"何时"。

要想 RollingFileAppender 起作用,必须同时设置 RollingPolicy 和 TriggeringPolicy。不过,如果RollingPolicy 也实现了 TriggeringPolicy 接口,那么只需要设置 RollingPolicy。

如下测试程序应用滚动与触发策略,来处理日志文件。

(com/ttpod/file/MyApp2.java)

package com.ttpod.file;

import java.util.Date;

publicclass MyApp2 {

static Timer t;

publicstaticvoid main(String[] args) {

// 调用LoggerFactory 类的静态方法getLogger取得一个Logger实例,

final Logger logOut = LoggerFactory.getLogger("SystemOut"); // 定义一个名为SystemOut的logger

final Logger logErr = LoggerFactory.getLogger("SystemErr"); // 定义一个名为SystemErr的logger

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

//configurator.doConfigure(args[0]);

configurator.doConfigure("src/com/ttpod/file/logback.xml");

catch (JoranException je) {

je.printStackTrace();

}

// 打印logback的内部状态

StatusPrinter.print(lc);    //固定部分

t = new Timer(); // 定义一个定时器

TimerTask tt = new TimerTask() { // 用定义器执行计划(schedule),并定义一个继承了TimerTask的匿名内部类

@Override

publicvoid run() {

for (int i = 100; i > 0; i--) {

// Timer t=null;

MyApp2.t.cancel();

logOut.info("测试Logback日志的使用"); // 定义了一个info级别的日志消息

logErr.error("发生错误"); // 定义了一个error级别的日志消息

}

System.out.println("~~~~~~~~~~~~~ ");

}

};

毫秒执行一次计划

}

}

// 没有自定义它的logback所需的配置文件,程序启动时会自动加载classpath目录下的查找

// logback-test.xml;如果没找到则logback-test.xml,则继续在classpath下查找lobback.xml。

此测试类,在定时器中定义了一个执行计划,按照执行计划生成日志文件。在file包中添加相的logback配置文件,如下:

(com/ttpod/file/logback.xml)

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPE configuration>

<configuration>

<jmxConfigurator />

<!-- 配置文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!--logback的版本必须是0.9.21以上才支持-->

<timestampkey="byDay"datePattern="yyyyMMdd"/>

<!-- 控制台输出日志 -->

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<layoutclass="ch.qos.logback.classic.PatternLayout">

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

</layout>

</appender>

<!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件压缩(.zip)备份)-->

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<!-- 文件的路径及文件名 -->

<File>${USER_HOME}/SystemOut.log</File>

<!-- 定义窗口滚动策略 -->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<!-- 每触发一次,自动压缩日志文件,%i在1-3之间循环变化 -->

<FileNamePattern>${USER_HOME}/SystemOut_%i.log.zip</FileNamePattern>

<MinIndex>1</MinIndex><!-- %i的最小值为1-->

<MaxIndex>3</MaxIndex><!-- %i的最大值为3-->

</rollingPolicy>

<!-- 触发策略,通常rollingPolicy与triggeringPolicy联合使用 -->

<triggeringPolicy

class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<MaxFileSize>10KB</MaxFileSize>

</triggeringPolicy>

<!-- 格式化输出 -->

<!-- %d表示日期,格式为:yyyy-MM-dd HH:mm:ss.SSS ;%thread:线程名;

个字符的宽度显示级别; %logger:显示logger名;%msg:日志消息;%n:换行

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

</layout>

-->

<!-- 或者用下面的形式格式(推荐)-->

<encoder>

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</encoder>

<!-- 以html文件输出 -->

<!--

<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<layout class="ch.qos.logback.classic.html.HTMLLayout">

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</layout>

</encoder>

-->

</appender>

<!-- 输出ERROR级别的日志到文件(同样采用文件大小策略进行文件输出,超过指定大小对文件压缩(.zip)备份) -->

<appendername="FILE-ERROR"class="ch.qos.logback.core.rolling.RollingFileAppender">

<filterclass="ch.qos.logback.classic.filter.LevelFilter">

<level>ERROR</level><!-- 指定要对级别为ERROR日志信息过滤 -->

<OnMismatch>DENY</OnMismatch>     <!-- 如果不匹配,则拒绝处理 -->

<OnMatch>ACCEPT</OnMatch>         <!-- 如果匹配,则立即处理 -->

</filter>

<File>${USER_HOME}/SystemErr.log</File><!-- 文件名即路径 -->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<FileNamePattern>${USER_HOME}/SystemErr_%i.log.zip</FileNamePattern>

<MinIndex>1</MinIndex>

<MaxIndex>3</MaxIndex>

</rollingPolicy>

<triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<MaxFileSize>10KB</MaxFileSize>

</triggeringPolicy>

<!-- 格式化输出 -->

<!--

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>

</layout>

-->

<encoder>

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%relative%thread%mdc%level%logger%msg</pattern>

</encoder>

</appender>

<!--这里指定logger name 是为jmx设置日志级别做铺垫 -->

<!-- 分别将STDOUT与FILE appender都添加到自定义的logger中,否则将不会输出到appender指定的目的地-->

<loggername="SystemOut"level="INFO">

<appender-refref="STDOUT"/>

<appender-refref="FILE"/>

</logger>

<loggername="SystemErr"level="INFO">

<appender-refref="STDOUT"/>

<appender-refref="FILE-ERROR"/>

</logger>

</configuration>

执行以上程序,在控制台也会不断的输出以下日志信息:

14:42:34.875 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:34.875 [Timer-0] ERROR SystemErr - 发生错误

14:42:34.937 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:34.937 [Timer-0] ERROR SystemErr - 发生错误

14:42:35.000 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:35.000 [Timer-0] ERROR SystemErr - 发生错误

14:42:35.062 [Timer-0] INFO SystemOut - 测试Logback日志的使用

14:42:35.062 [Timer-0] ERROR SystemErr - 发生错误

生成的日志文件如下图:

FixedWindowRollingPolicy当发生滚动时,FixedWindowRollingPolicy 根据如下固定窗口(window)算法重命名文件。选项"fileNamePattern"代表归档(滚动)记录文件的文件名模式。该选项是必需的,且必需在模式的某处包含标志"%i"。如示例3中的MyApp3-RollingFixedWindow.xml 。

TimeBasedRollingPolicy或许是最受流行的滚动策略。它根据时间来制定滚动策略,例如根据日或月。TimeBasedRollingPolicy 既负责滚动也负责触发滚动。实际上,TimeBasedRollingPolicy 同时实现了 RollingPolicy 接口和 TriggeringPolicy 接口。和 FixedWindowRollingPolicy一样,TimeBasedRollingPolicy 也支持自动压缩文件。如果"fileNamePattern"选项以".gz"或".zip"结尾,就表示需要压缩。如示例3中的MyApp3-RollingTimeBased.xml 。

SizeAndTimeBasedFNATP按照日期进行归档的同时限制每个记录文件的大小,特别是当后处理工具对记录文件大小有限制时。Logback 为此提供了 SizeAndTimeBasedFNATP ,它是TimeBasedRollingPolicy 的子组件,FNATP 代表"FNATP stands for File Naming And Triggering Policy"。下面的例子MyApp3-sizeAndTime.xml演示了基于大小和时间的记录文件归档。

(com/ttpod/file/MyApp3.java)

package com.ttpod.file;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.slf4j.MDC;

import ch.qos.logback.classic.LoggerContext;

import ch.qos.logback.classic.joran.JoranConfigurator;

import ch.qos.logback.core.joran.spi.JoranException;

import ch.qos.logback.core.util.StatusPrinter;

import com.ttpod.file.Foo;;

publicclass MyApp3 {

publicstaticvoid main(String[] args) throws InterruptedException {

Logger logger = (Logger) LoggerFactory.getLogger(MyApp3.class);

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

configurator.doConfigure(args[0]);

catch (JoranException je) {

je.printStackTrace();

}    //固定部分

//打印logback的内部状态

StatusPrinter.print(lc);

logger.debug("**Hello {}", new Foo());

//客户端的每个记录请求添加唯一戳(uniquely stamp),运用MDC(Mapped Diagnostic Context)

//在分布式应用程序中,可以区分并处理不同客户端的记录输出,而不增加logger的开销

MDC.put("testKey", "testValueFromMDC");        //在appender中用%X{testKey},可以输出MDC中test对应的值

MDC.put("testKey2", "value2");

for (int i = 0; i < 10; i++) {

logger.debug("logging statement " + i);

Thread.sleep(100);

}

Foo foo = new Foo();

foo.createLoggingRequest();

}

}

配置文件:

(com/ttpod/file/MyApp3-RollingFixedWindow.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/testFile.log</file>

<!--滚动策略-->

<rollingPolicyclass="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">

<!—自动压缩备份-->

<fileNamePattern>${USER_HOME}/testFile.%i.log.zip</fileNamePattern>

<minIndex>1</minIndex>

<maxIndex>3</maxIndex><!--%i在1-3之间循环变化-->

</rollingPolicy>

<!—触发策略-->

<triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">

<!--日志文件大小限制在5kB以内,否则就触发滚动-->

<maxFileSize>5kB</maxFileSize>

</triggeringPolicy>

<encoder><!--格式化输出-->

<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

多次运行MyApp3,logback配置参数传入MyApp3-RollingFixedWindow.xml,即可发现在当前应用的log目录下生成了如:testFile.1.log.zip、testFile.2.log.zip、testFile.3.log.zip,testFile.log文件。备份的压缩文件中的日志文件的文件名默认后缀跟了归档日期,如下图:

(com/ttpod/file/MyApp3-RollingTimeBased.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 基于时间的滚动策略 -->

<appendername="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/logFile.log</file>

<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<!-- 每天滚动

<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

-->

<!-- 每分钟滚动 -->

<fileNamePattern>${USER_HOME}/logFile.%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>

天-->

<maxHistory>30</maxHistory>

</rollingPolicy>

<encoder>

<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>

</encoder>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

<!--

运行:

java chapters.appenders.ConfigurationTester

src/com/ttpod/chapters/appenders/conf/logback-RollingTimeBased.xml

-->

多次运行MyApp3,logback配置参数传入MyApp3-RollingTimeBased.xml,若设置一个执行计谋,程序不停止,则每分钟可触发一次日志备份,即可发现在当前应用的log目录下生成了按配置的格式的文件,如:

(com/ttpod/file/MyApp3-sizeAndTime.xml)

<configuration>

<!-- 基于时间与大小的归档 -->

<statusListenerclass="ch.qos.logback.core.status.OnConsoleStatusListener"/>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/file/variables.properties"/>

<!-- 基于时间与大小的归档 -->

<appendername="ROLLING"class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${USER_HOME}/mylog.txt</file>

<rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<!-- 每天滚动

<fileNamePattern>${USER_HOME}/mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>

-->

<minIndex>1</minIndex>

<maxIndex>3</maxIndex>

<!-- 每分钟滚动 -->

<fileNamePattern>${USER_HOME}/mylog-%d{yyyy-MM-dd_HH-mm}.%i.txt</fileNamePattern>

天-->

<maxHistory>30</maxHistory>

<timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

<!-- 当文件大小超过5kB时触发滚动,这里设置5kb只是便于测试 -->

<maxFileSize>5kB</maxFileSize>

</timeBasedFileNamingAndTriggeringPolicy>

</rollingPolicy>

<encoder>

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="ROLLING"/>

</root>

</configuration>

运行时,logback配置参数传入src/com/ttpod/file/MyApp3-sizeAndTime.xml即可。当日志文件超过5kB时,或到另一天时,程序又被执行了,则会触发滚动,按照此前配置规则备原来的文件。输出的文件如下图所示:

以网页形式输出日志文件

HTMLLayout 与 logback-classic 里的 HTMLLayout 相似。默认情况下,PatternLayout 创建包含下列数据的表格:远程IP;日期;请求的URL;状态吗;Content Length 。

以下有个示例,演示以HTMLLayout格式化后,以htm文件输出日志。如下:

(com/ttpod/html/HTMLTest1.java)

package com.ttpod.html;

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;

publicclass HtmlTest1 {

publicstaticvoid main(String[] args) throws InterruptedException {

Logger logger = LoggerFactory.getLogger(HtmlTest1.class);

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

try {

JoranConfigurator configurator = new JoranConfigurator();

configurator.setContext(lc);

lc.reset();

configurator.doConfigure(args[0]);

//configurator.doConfigure("./src/com/ttpod/html/htmlLayoutConfig1.xml");

catch (JoranException je) {

je.printStackTrace();    //固定部分

}

StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

for (int i = 0; i < 6; i++) {

if (i % 5 == 0) {

logger.warn("a warning message " + i);    //声明一条warn级别的日志消息

//logger.info("a warning message " + i);

else {

logger.debug("hello world number" + i);

}

}

logger.error("Finish off with fireworks", new Exception("Just testing"));

}

}

用以下配置文件格式化输出到properties文件指向的目的地。

(com/ttpod/html/htmlLayoutConfig1.xml)

<configuration>

<!-- 指定属性文件路径 -->

<propertyfile="src/com/ttpod/html/variables.properties"/>

<!-- 指定日志输出到文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<encoderclass="ch.qos.logback.core.encoder.LayoutWrappingEncoder">

<layoutclass="ch.qos.logback.classic.html.HTMLLayout">

<pattern>%relative%thread%mdc%level%logger%msg</pattern>

</layout>

</encoder>

<file>${destination}</file>

</appender>

<rootlevel="DEBUG">

<appender-refref="FILE"/>

</root>

</configuration>

<!-- 以以下方式运行

java com.ttpod.html.HtmlTest1 src/com/ttpod/html/htmlLayoutConfig1.xml

-->

属性文件内容如下:

(com/ttpod/html/ variables.properties)

#指定USER_HOME目录

USER_HOME=./log

#指定日志文件名

fileName=htmlTest.htm

#生成日志文件的目的地

destination=${USER_HOME}/${fileName}

运行html/HTMLTest1,并传入配置参数:src/com/ttpod/html/htmlLayoutConfig1.xml,可以发现在在当前应用的/log目录下生成一个htmlTest.htm日志文件。输出日志结果如下图:

以邮件形式输出日志

以邮件形式输出日志,依赖于SMTPAppender ,SMTPAppender在固定大小的缓冲里积累记录时间,当用户指定的事件发生后,就通过email发出这些事件。默认情况下,email发送是由级别为 ERROR 或更高级别的记录事件触发的。

下面的演示程序 chapters.appenders.mail.EMail,生成大量记录消息,然后生成一个错误消息。该程序有两个参数,第一个是需要生成的记录消息的数量,第二个是 logback 配置文件。该程序生成的最后一条记录事件,即 ERROR 级别的事件,将触发 email的发送。

(com/ttpod/mail/Email.java)

package com.ttpod.mail;

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.util.StatusPrinter;

publicclass EMail {

staticpublicvoid main(String[] args) throws Exception {

if (args.length != 2) {

usage("Wrong number of arguments.");

}

int runLength = Integer.parseInt(args[0]);

String configFile = args[1];

//int runLength = Integer.parseInt("300");

//String configFile="src/com/ttpod/mail/gmailSSL.xml";

//String configFile = "src/com/ttpod/mail/gmailSTARTTLS.xml";

//自定义配置文件,得到日志固定格式

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

JoranConfigurator configurator = new JoranConfigurator();

lc.reset();

configurator.setContext(lc);

configurator.doConfigure(configFile);

Logger logger = LoggerFactory.getLogger(EMail.class);

for (int i = 1; i <= runLength; i++) {

if ((i % 10) < 9) {

logger.debug("This is a debug message. Message number: " + i);

else {

logger.warn("This is a warning message. Message number: " + i);

}

}

//定义一条error级别的日志输出

logger.error("At last an error.", new Exception("Just testing"));

//打印logger内部状态

StatusPrinter.printInCaseOfErrorsOrWarnings(lc);

}

staticvoid usage(String msg) {    //当参数传入错误时,提供的处理惯例

System.err.println(msg);

System.err.println("Usage: java " + EMail.class.getName() +

" runLength configFile\n" +

" runLength (integer) the number of logs to generate\n" +

" configFile a logback configuration file in XML format." +

" XML files must have a '.xml' extension.");

System.exit(1);    //退出程序

}

}

如下配置文件从属性文件中读取发送邮件定义的属性,配置由邮件输出日志,如下:

<configuration>

<!-- 指定属性文件的位置 -->

<propertyfile="src/com/ttpod/mail/gmailSSL.properties"/>

<!-- 目的指定向email的appender -->

<appendername="EMAIL"class="ch.qos.logback.classic.net.SMTPAppender">

<SMTPHost>${SMTPHOST}</SMTPHost><!-- 邮件服务器地址 -->

<SMTPPort>${SMTPPORT}</SMTPPort><!--SMTPPORT端口-->

<SSL>true</SSL>

<Username>${EMAIL_USERNAME}</Username><!-- 用户名 -->

<Password>${EMAIL_PASSWORD}</Password><!-- 密码 -->

<To>${EMAIL-DESTINATION}</To><!-- 目标接收人 -->

<To>${ANOTHER_EMAIL_DESTINATION}</To><!-- additional destinations are possible -->

<From>${EMAIL_USERNAME}</From><!-- 发件人 -->

<Subject>TESTING: %logger{20} - %m</Subject><!-- 主题 -->

<encoder><!-- 一般采用这种方式格式化输出 -->

<layoutclass="ch.qos.logback.classic.html.HTMLLayout">

<!-- 采用什么渲染方式,这采取的是HTML方式 -->

<Pattern>%date %-5level %logger - %message%n</Pattern>

</layout>

</encoder>

<!--

<layout class="ch.qos.logback.classic.PatternLayout">

<Pattern>%date %-5level %logger - %message%n</Pattern>

</layout>

-->

</appender>

<!-- 输出到控制台,以便将日志也打印到控制台-->

<appendername="STDOUT"class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

</pattern>

</encoder>

</appender>

<!-- 输出到文件,将日志记录到本地文件-->

<appendername="FILE"class="ch.qos.logback.core.FileAppender">

<file>./log/mailtest.log</file>

<encoder>

<!-- %msg表示日志信息,%n表示换行 -->

<pattern>%msg%n</pattern>

</encoder>

</appender>

<rootlevel="debug">

<appender-refref="EMAIL"/>

<appender-refref="STDOUT"/>

<appender-refref="FILE"></appender-ref>

</root>

</configuration>

属性文件如下设置,以供自定的logback配置文件读取。

SMTPHOST=smtp.gmail.com

SMTPPORT=

EMAIL_USERNAME = someone123@gmail.com

EMAIL_PASSWORD = password123

EMAIL-DESTINATION = someone456@ttpod.com

ANOTHER_EMAIL_DESTINATION = some456@126.com

传入配置文件参数,运行,当出现也error级别以及以上级别的日志时,就会触发日志发送到邮件,但目前此测试例子,可能由于本地tomcat服务器缓存邮件,以致邮件无法发送到外网服务器,所以在这里没给出具体发送成功的结果。但可以通过它生成对应的HTML文件,可以看到邮件内容,如下图:

如果未指定选项"Evaluator",则 SMTPAppender 被默认分配一个OnErrorEveluator(ch.qos.logback.classic.boolex.OnErrorEvaluator)实例,当遇到级别为ERROR 或更高级别

的事件后,触发 email 传输。此处最后一条消息是一个error级别的记录,触发了邮件的发送。

另外,还可以将日志写入数据库要把记录事件写入数据库,依赖于DBAppender ,DBAppender把记录事件写入数据库的三张表。三张表分别是 logging_event、logging_event_property 和logging_event_exception。在使用 DBAppender 之前,这三张表必须已经被创建。关于将日志记录写入到数据库中,更多可参考logback手册中的DBAppender。

Logback学习笔记的更多相关文章

  1. Logback 学习笔记

    来源:http://webinglin.github.io/2015/06/04/Logback-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/ Logback背景 Log ...

  2. springmvc学习笔记(简介及使用)

    springmvc学习笔记(简介及使用) 工作之余, 回顾了一下springmvc的相关内容, 这次也为后面复习什么的做个标记, 也希望能与大家交流学习, 通过回帖留言等方式表达自己的观点或学习心得. ...

  3. SpringBoot学习笔记(13):日志框架

    SpringBoot学习笔记(13):日志框架——SL4J 快速开始 说明 SpringBoot底层选用SLF4J和LogBack日志框架. SLF4J的使用 SpringBoot的底层依赖关系 1. ...

  4. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  5. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  6. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  7. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  8. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  9. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

随机推荐

  1. 使用 IIS 在 Windows 上托管 ASP.NET Core(Windows安装实践)

    原文地址 https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.0&tabs= ...

  2. 简易网页 html

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. autofac JSON文件配置

    autofac是比较简单易用的IOC容器.下面我们展示如何通过json配置文件,来进行控制反转. 需要用到以下程序集.可以通过nugget分别安装 Microsoft.Extensions.Confi ...

  4. Cookie的创建与删除

    Cookie 为 Web 应用程序保存用户相关信息提供了一种有用的方法.例如,当用户访问站点时,可以利用 Cookie 保存用户首选项或其他信息,这样,当用户下次再访问站点时,应用程序就可以检索以前保 ...

  5. java—不同的用户登录以后可以看到不同的菜单(后台可以实现对用户菜单的管理) 1 (55)

    实现不同的用户登录以后可以看到不同的菜单.(后台可以实现对用户菜单的管理.) 第一步:分析数据结构        1:用户表 表名:users 列名 类型 说明 id Varchar(32) 主键 n ...

  6. 八.linux系统文件属性知识

    1.文件属性权限是12位,现在只看9位,其中每3个一组,为:属主权限.属组权限.其他权限,其中r可读,w可写,x可执行,如图: 2.文件属性之软硬链接   linux系统中有两种链接,为硬链接(ln) ...

  7. 【12c OCP】CUUG OCP认证071考试原题解析(34)

    34.choose two View the Exhibit and examine the structure of the PRODUCT_INFORMATION and INVENTORIES ...

  8. 关于Mybatis 反向生成后 查询结果全部为null 解决办法

    今天遇到了一个问题,就是mybatis通过反向生成工具 生成的pojo类(实体类) xml文件 以及Mapper之后查询时结果为null 我写的代码怎么看都没有错 就是没有结果 后来在排除错误的时候发 ...

  9. P4211[BZOJ 3626] [LNOI2014]LCA

    题目描述 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每 ...

  10. isset()、empty()、is_NULL()的区别

    1,isset():变量不存在,或变量为null,返回false,否则返回true: 2,empty():变量不存在,或变量为null,返回true,另外"".0."0& ...