Java调试的变迁:从System.out.println到log4j,到logback, 到slf4j(2)

首先介绍下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);
2. 因为内部已优化,作者认为slf4j的logger不需要定义为static。
3. 可设置缓存后批量写日志文件(但服务器如果重启,可能会丢失未写到磁盘的记录)
4. MDC,用Filter,将当前用户名等业务信息放入MDC中,在日志format定义中即可使用该变量。
5. JMS Appender用于告警, DB Appender用于业务日志等可以使用插件,如生成Log代码的Eclipse插件Log4E。
6. tomcat和glassfish中,设定日志路径为../logs/xxxx.log 都能将日志放入应用服务器本身的logs目录。

最后把最近完善的一个logback.xml贴上,毕竟实际项目中的文件最能说明问题。

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>  

就先不做说明了,里面的配置还是比较明确的,关于jmsappender那部分,之前也做过简单说明。

SLF4J 介绍

Commons-logging:apache最早提供的日志的门面接口。避免和具体的日志方案直接耦合。类似于JDBC的api接口,具体的的JDBC driver实现由各数据库提供商实现。通过统一接口解耦,不过其内部也实现了一些简单日志方案。
Log4j:经典的一种日志解决方案。内部把日志系统抽象封装成Logger、appender、pattern等实现。我们可以通过配置文件轻松的实现日志系统的管理和多样化配置。
- Slf4j: 全称为Simple Logging Facade for Java:java简单日志门面。是对不同日志框架提供的一个门面封装。可以在部署的时候不修改任何配置即可接入一种日志实现方案。和commons-loging应该有一样的初衷。个人感觉设从计上更好一些,没有commons那么多潜规则。同时有两个额外特点:
1. 能支持多个参数,并通过{}占位符进行替换,避免老写logger.isXXXEnabled这种无奈的判断,带来性能提升见:http://www.slf4j.org/faq.html#logging_performance
2. OSGI机制更好兼容支持
一图胜千言,官网上的一个图:

从上图可以发现,选择还是很多的。
Logback: Lack作为一个通用可靠、快速灵活的日志框架,将作为Log4j的替代和SLF4J组成新的日志系统的完整实现。官网上称具有极佳的性能,在关键路径上执行速度是log4j的10倍,且内存消耗更少。

二、 SLF4J 的几种实际应用模式–之一:SLF4J+Log4J

SLF4J(Simple Logging Facade for Java) 是一个通用的日志框架,不能何以谓之 Facade(门面),所扮眼的角色相当于 Jakarta Commons Logging。就像 JCL 需要底层的日志实现,如 Log4J、java.util.logging、Simple Logger 等来完成具体的信息输出,事实上基本总是 JCL+Log4J 那么一个绝配。SLF4J 的原旨也是能支持多种下层日志框架实现,但最好的日志实现仍然是 Log4J,所以本篇讲述 SLF4J 的第一种用法 SLF4J+Log4J。
需要的配置文件和组件包,下面三个 jar 文件和一个 properties 文件都是要放在项目的 ClassPath 上。
1. slf4j-api-1.5.11.jar
2. slf4j-log4j12-1.5.11.jar
3. log4j-1.2.15.jar
4. log4j.properties(也可以是 log4j.xml,本例中用 log4j.propertes)
前两个包在 http://www.slf4j.org/download.html 处下载,后一个包在http://logging.apache.org/log4j/1.2/download.html 下载,可能包文件名中的版本号有些差,不要紧。
log4j.properties 以前该是怎么写,现在还是怎么写,比如一个最简单的内容,只向控制台输出日志信息,如下:

log4j.rootLogger=DEBUG,console  
log4j.appender.console=org.apache.log4j.ConsoleAppender  
log4j.appender.console.layout=org.apache.log4j.PatternLayout  
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n  
三、 SLF4J 的几种实际应用模式–之一:SLF4J+Logback

前面讲的 SLF4J 的用法之一是 SLF4J+Log4J,而这里要推出的组合是 SLF4J+LogBack。不用 Log4J?难道还有比 Log4J 更好的日志实现吗?是的,答案就是LogBack。假如你知道 LogBack 和 Log4J 是同出一位大师之手,你就不会觉得惊讶了。LogBack 对 Log4J 进行了增强,并投入了 SLF4J 的怀抱,它还被作者认定为 Log4J 的继任者。
为什么叫做 Logback,我就不知道了,反正不要叫做 Brokeback 就行。这里是推崇用 Logback 替代 Log4J 的十几个理由:Reasons to prefer logback over log4j,说的大至是更快;好测试;与 SLF4J 关系紧;文档丰富;能自动加载配置文件;多个 JVM 写一个日志文件,或其他 I/O 错误时不影响程序执行;配置文件中加入条件控制;强大的日志过滤;更强的日志切分功能;自动压缩、删除日志文件;异常栈中更多的数据信息。
Logback 分为三个模块:logback-core,logback-classic,logback-access。logback-core 是核心;logback-classic 改善了 log4j,且自身实现了 SLF4J API,所以即使用 Logback 你仍然可以使用其他的日志实现,如原始的 Log4J,java.util.logging 等;logback-access 让你方便的访问日志信息,如通过 http 的方式。
还得提一点,Logback 能更好的放到 OSGI 环境中。好了,简单绍完了 Logback,就来看看具体怎么用它。
需要的配置文件和组件包,下面三个 jar 文件和一个 xml文件都是要放在项目的 ClassPath 上。
1. slf4j-api-1.5.11.jar
2. logback-core-0.9.20.jar
3. logback-classic-0.9.20.jar
4. logback.xml 或 logback-test.xml (类似于 log4j.properties/log4j.xml)

四、SLF4J怎样定位到具体用哪个log

在应用中,通过LoggerFactory类的静态getLogger()获取logger。通过查看该类的代码可以看出,最终是通过StaticLoggerBinder.SINGLETON.getLoggerFactory()方法获取LoggerFactory然后,在通过该具体的LoggerFactory来获取logger的。类org.slf4j.impl.StaticLoggerBinder并不在slf4j-api-1.5.2.jar包中,仔细查看每个与具体日志系统对应的jar包,就会发现,相应的jar包都有一个org.slf4j.impl.StaticLoggerBinder的实现,不同的实现返回与该日志系统对应的LoggerFactory,因此就实现了所谓的静态绑定,达到只要选取不同jar包就能简单灵活配置的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值