为啥有logback?
Logback为取代log4j而生。
Logback由log4j的创立者Ceki Gülcü设计。以十多年设计工业级记录系统的经验为基础,所创建的logback比现有任何记录系统更快、占用资源更少,有时差距非常大
0. 需要添加的 JAR 包
logback-core.jar – logback 核心包,必备
slf4j-api.jar – 通用日志接口包,可以在logback与其他记录系统如log4j和java.util.logging (JUL)之间轻松互相切换
Logback-classic – 实现了 SLF4J API,扩展了core模块
1. 系统启动,默认家在classpath下的logback.xml 或者 logback-test.xml。 这里可以把默认配置文件的位置作为系统属性进行指定
java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
2. 在根元素configuration中配置属性scan=”true”后,当配置文件修改后自动重新加载,默认每分钟扫描一次。
configuration元素的 scanPeriod 属性控制扫描周期,其值可以带时间单位,包括:milliseconds、seconds、minutes和hours。
如果没写明时间单位,则默认为毫秒。示例:
<configuration debug="true" scan="true" scanPeriod="30 minutes"> </configuration>
为提高性能,不会在每个logger被调用时去检查是否需要扫描,而是每隔16次记录操作进行一次检查。
简言之,当配置文件改变后,它会被延时重新加载,延时时间由扫描间隔时间和一些logger调用所决定
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
3. Logback-classic带了一个叫ViewStatusMessagesServlet的Servlet,它以HTML表格的格式打印与当前LoggerContext关联的StatusManager的内容
<servlet> <servlet-name>ViewStatusMessages</servlet-name> <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ViewStatusMessages</servlet-name> <url-pattern>/lbClassicStatus</url-pattern> </servlet-mapping>
访问地址是http://host/yourWebapp/lbClassicStatus。
4. 可以这样描述配置文件的基本结构:
以<configuration>开头,后面有零个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素
a. Logger是用<logger>元素配置的。<logger>元素有且仅有一个name属性、一个可选的level属性和一个可选的additivity属性。
Logger: Level属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。
还可以是一个特殊的字符串“INHERITED”或其同义词“NULL”,表示强制继承上级的级别
<logger>元素可以包含零个或多个<appender-ref ref=”logger的name” >元素,表示这个appender会被添加到该logger。强调一下,每个用<logger>元素声明的logger,首先会移除所有appender,然后才添加引用了的appender,所以如果logger没有引用任何appender,就会失去所有appender。
b. <root>元素配置根logger。该元素有一个level属性。没有name属性,因为已经被命名为“ROOT”。
ROOT: Level属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。注意不能设置为“INHERITED” 或“NULL”。
c. 配置Appender:
Appender用 <appender>元素配置,该元素必要属性name和class。name属性指定appender的名称,class属性指定appender类的全限定名。
<appender>元素可以包含零个或多个<layout>元素、零个或多个<encoder>元素和零个或多个<filter>元素。
<layout>元素的class属性是必要的,表示将被实例化的layout类的全限定名。因为太常用了,所以当当layout是PatternLayout时,可以省略class属性。
<encoder>元素class属性是必要的,表示将被实例化的encoder类的全限定名。因为太常用了,所以当当encoder是PatternLayoutEncoder时,可以省略class属性。
注意每个appender都有自己的encoder。Encoder通常不能被多个appender共享,layout也是。所以,logback的配置文件里没有共享encoder或layout的语法。
5.常用Appender(负责写记录事件的组件):
a. ConsoleAppender, FileAppender, RollingFileAppender, FixedWindowRollingPolicy
ConsoleAppender把事件添加到控制台,更准确地说是System.out或System.err,默认为前者。ConsoleAppender按照用户指定的encoder对事件进行格式化。System.out和System.err都是java.io.PrintStream类型,因此,它们被包裹在有缓冲I/O操作的OutputStreamWriter里
b. 通过时间戳保证唯一文件名(比如对于不断启动的短生命周期的程序):
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" /> <appender name="logfile" class="ch.qos.logback.core.FileAppender"> <file>logfile-${bySecond}.log</file> <append>false</append> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender>
timestamp元素有两个属性:key和datePattern。属性key是变量名,对余下的配置元素可用。属性datePattern表示把当前时间(解析配置文件的时间)转换成字符串时使用的日期模式,遵从java.text.SimpleDateFormat里的约定。
c. RollingFileAppender继承FileAppender,能够滚动记录文件。例如,RollingFileAppender能先记录到文件“log.txt”,然后当符合某个条件时,变成记录到其他文件。
RollingFileAppender有两个与之互动的重要子组件。第一个是RollingPolicy,负责滚动。第二个是TriggeringPolicy,决定是否以及何时进行滚动。所以,RollingPolicy负责“什么”, TriggeringPolicy负责“何时”。要想RollingFileAppender起作用,必须同时设置RollingPolicy和TriggeringPolicy。不过,如果RollingPolicy也实现了TriggeringPolicy接口,那么只需要设置RollingPolicy
<appender name="logfile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${log.base}.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${log.base}.%d{yyyy-MM-dd}.log.zip</FileNamePattern> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </layout> </appender>
同样也可以指定 maxHistory 属性,控制被保留的归档文件的最大数量,超出数量就删除旧文件
6. Encoder负责两件事,一是把事件转换为字节数组,二是把字节数组写入输出流。encoder不但可以完全控制待写出的字节的格式,而且可以控制字节何时及是否被写出。 在logback 0.9.19版之前没有encoder。
Layout,只负责把事件转换为字符串。此外,因为layout不能控制事件何时被写出,所以不能成批地聚集事件。相比之下,encoder不但可以完全控制待写出的字节的格式,而且可以控制字节何时及是否被写出。目前,PatternLayoutEncoder是唯一有用的encoder,它基本上是封装了PatternLayout,让PatternLayout负责大多数工作
注意:在之前的版本里,多数appender依靠layout来把事件转换成字符串并用java.io.Writer把字符串输出。在之前的版本里,用户需要在FileAppender里嵌入一个PatternLayout。
<appender name="logfile" class="ch.qos.logback.core.FileAppender"> <File>logfile.log</File> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </layout> </appender>
而从0.9.19版开始,FileAppender和其子类使用encoder,不接受layout。 如
<appender name="logfile" class="ch.qos.logback.core.FileAppender"> <file>logfile.log</file> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender>
对于console 同样no longer admits a layout as a sub-component, set an encoder instead.
对于这个变化总的来说:既然PatternLayout是最常用的layout,logback便提供了PatternLayoutEncoder,它扩展了LayoutWrappingEncoder,且仅使用PatternLayout。从logback 0.9.19版起,FileAppender或其子类在只要用到PattternLayout时,都必须换成PatternLayoutEncoder。
7. Layout负责把事件转换成字符串。Layout接口的format()方法的参数是代表任何类型的事件,返回字符串
8. 附上几段appender的配置
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender>
<appender name="logfile2" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${bySecond}.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>%d{yyyy-MM-dd-HH-mm-ss}.log</FileNamePattern> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </encoder> </appender>
=========================================================================
Logback浅析
Logback是由 log4j创始人Ceki Gülcü 设计的又一个开源日志组件。logback当前分成三个模块:logback-core,logback- classic和logback-access。
2、Logback的核心对象:Logger、Appender、Layout
Logback主要 建立于 Logger 、 Appender 和 Layout 这三个类之上。
Logger:日志的记录器,把它关联到应用的对应的 context 上后,主要用于存放日志对象,也可以定义日志类型、级别。Logger对象一般多定义为静态常量,如:
package com.logs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyApp {
final static Logger logger = LoggerFactory.getLogger("MyApp.class");
public static void main(String[] args) {
logger.trace("trace");
logger.debug("debug str");
logger.info("info str");
logger.warn("warn");
logger.error("error");
}
}
Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、 PostreSQL、Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
Layout: 负责把事件转换成字符串,格式化的日志信息的输出。具体的Layout通配符,可以直接查看帮助文档。
3、Level 有效级别
Logger可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN和ERROR,定义于ch.qos.logback.classic.Level类。程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。 如果设置级别为INFO,则优先级高于等于INFO级别(如:INFO、 WARN、ERROR)的日志信息将可以被输出,小于该级别的如DEBUG将不会被输出。 为确保所有logger都能够最终继承一个级别,根logger总是有级别,默认情况下,这个级别是DEBUG。
4、 三值逻辑
Logback的过滤器基于三值逻辑(ternary logic),允许把它们组装或成链,从而组成任意的复合过滤策略。过滤器很大程度上受到Linux的iptables启发。这里的所谓三值逻辑是说,过滤器的返回值只能是ACCEPT、DENY和NEUTRAL的其中一个。
如果返回DENY,那么记录事件立即被抛弃,不再经过剩余过滤器;
如果返回NEUTRAL,那么有序列表里的下一个过滤器会接着处理记录事件;
如果返回ACCEPT,那么记录事件被立即处理,不再经过剩余过滤器。
5、Filter 过滤器
Logback-classic提供两种类型的过滤器:常规过滤器和TuroboFilter过滤器。Logback整体流程:Logger 产生日志信息;Layout修饰这条msg的显示格式;Filter过滤显示的内容;Appender具体的显示,即保存这日志信息的地方。
6、具体使用案例
Java项目中一般都会应用比如struts、spring、hibernate等开源框架,而这些框架很多是应用log4j记录日志的,所以我们考虑用log4j + slf4j + logback 。这样我们需要导入log4j-over-slf4j-1.6.4.jar 、 logback-classic-1.0.1.jar 、 logback-core-1.0.1.jar 、 slf4j-api-1.6.4.jar ,如果你要用到EvaluatorFilter过滤器来过滤日志Msg中的特殊字符需要导入其依赖包 janino-2.3.2.jar 。其logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 控制台输出 --> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </encoder> </appender> <!-- 时间滚动输出 level为 DEBUG 日志 --> <appender name="file—debug" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY </onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>D:/logs/debug.%d{yyyy-MM-dd}.log</FileNamePattern> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </encoder> </appender> <!-- 时间滚动输出 level为 ERROR 日志 --> <appender name="file—error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY </onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>D:/logs/error.%d{yyyy-MM-dd}.log</FileNamePattern> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </encoder> </appender> <!-- 特定过滤含有某字符串的日志 --> <appender name="file-str" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator> <expression>message.contains("str")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>D:/logs/contains.%d{yyyy-MM-dd}.log </FileNamePattern> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </encoder> </appender> <!-- 数据库输出 --> <appender name="db" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://host_name:3306/datebase_name</url> <user>username</user> <password>password</password> </connectionSource> </appender> <logger name="java.sql.Connection"> <level value="DEBUG" /> </logger> <logger name="java.sql.Statement"> <level value="DEBUG" /> </logger> <logger name="com.ibatis"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.common.jdbc.SimpleDataSource"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.common.jdbc.ScriptRunner"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate"> <level value="DEBUG" /> </logger> <logger name="com.danga.MemCached"> <level value="INFO" /> </logger> <logger name="org.springframework.test"> <level value="DEBUG" /> </logger> <logger name="org.apache.struts2"> <level value="DEBUG" /> </logger> <root level="DEBUG"> <appender-ref ref="stdout" /> <appender-ref ref="file—debug" /> <appender-ref ref="file—error" /> <appender-ref ref="file-str" /> <appender-ref ref="db" /> </root> </configuration>
首先介绍下logback的前世今生。
slf4j由log4j作者Ceki开发,逐步取代apahce commons logging。
logback由log4j作者Ceki开发,逐步取代log4j。
slf4j等于commons-logging,是各种日志实现的通用入口,会根据classpath中存在下面哪一个Jar来决定具体的日志实现库。
logback相比较log4j的优势
slf4j支持参数化的logger.error("帐号ID:{}不存在", userId);
告别了if(logger.isDebugEnable()) 时代。
另外logback的整体性能比log4j也较佳,hibernate等项目已经采用了slf4j。
slf4j和logback的使用
1.如果日志的参数超过3个,需要写成
Object[] params = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", params);
3.可设置缓存后批量写日志文件(但服务器如果重启,可能会丢失未写到磁盘的记录)
4.MDC,用Filter,将当前用户名等业务信息放入MDC中,在日志format定义中即可使用该变量。
5.JMS Appender用于告警, DB Appender用于业务日志等可以使用插件,如生成Log代码的Eclipse插件Log4E。
6.tomcat和glassfish中,设定日志路径为../logs/xxxx.log 都能将日志放入应用服务器本身的logs目录。
最后把最近完善的一个logback.xml贴上,毕竟实际项目中的文件最能说明问题。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <substitutionProperty name="log.base" value="d:\\logback\\logback" /> <jmxConfigurator /> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator name="myEval"> <expression>message.contains("dao")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </layout> </appender> <appender name="logfile-dao" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator name="myEval_dao"> <expression>message.contains("dao")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <Encoding>UTF-8</Encoding> <File>${log.base}_dao.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${log.base}.%d{yyyy-MM-dd}_dao.log.zip </FileNamePattern> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </layout> </appender> <appender name="logfile-service" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator name="myEval_service"> <expression>message.contains("service.impl")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <Encoding>UTF-8</Encoding> <File>${log.base}_service.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${log.base}.%d{yyyy-MM-dd}_service.log.zip </FileNamePattern> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> </layout> </appender> <appender name="jms_dao" class="ch.qos.logback.classic.net.JMSQueueAppender"> <InitialContextFactoryName> org.apache.activemq.jndi.ActiveMQInitialContextFactory </InitialContextFactoryName> <ProviderURL>tcp://192.168.1.120:61616</ProviderURL> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator name="myEval_service"> <expression>message.contains("dao")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <QueueConnectionFactoryBindingName>ConnectionFactory </QueueConnectionFactoryBindingName> <QueueBindingName>cms_dao_log</QueueBindingName> </appender> <appender name="jms_service" class="ch.qos.logback.classic.net.JMSQueueAppender"> <InitialContextFactoryName> org.apache.activemq.jndi.ActiveMQInitialContextFactory </InitialContextFactoryName> <ProviderURL>tcp://192.168.1.120:61616</ProviderURL> <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> <evaluator name="myEval_service"> <expression>message.contains("service.impl")</expression> </evaluator> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <QueueConnectionFactoryBindingName>ConnectionFactory </QueueConnectionFactoryBindingName> <QueueBindingName>cms_service_log</QueueBindingName> </appender> <logger name="com.cms5.cmsservice.jms"> <level value="DEBUG" /> </logger> <logger name="java.sql.PreparedStatement"> <level value="DEBUG" /> </logger> <logger name="java.sql.Connection"> <level value="DEBUG" /> </logger> <logger name="java.sql.Statement"> <level value="DEBUG" /> </logger> <logger name="com.ibatis"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.common.jdbc.SimpleDataSource"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.common.jdbc.ScriptRunner"> <level value="DEBUG" /> </logger> <logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate"> <level value="DEBUG" /> </logger> <logger name="com.danga.MemCached"> <level value="INFO" /> </logger> <logger name="org.springframework.test"> <level value="DEBUG" /> </logger> <logger name="org.apache.struts2"> <level value="DEBUG" /> </logger> <root> <level value="INFO" /> <!--<appender-ref ref="stdout" /> --> <appender-ref ref="logfile-dao" /> <appender-ref ref="logfile-service" /> <appender-ref ref="jms_dao" /> <appender-ref ref="jms_service" /> </root> </configuration>