我们前面已经讲过了 SLF4J 的两种用法:SLF4J+Log4J  和 SLF4J+Logback,那是在比较理想的情况下,所用组件只使用了 SLF4J 这一种统一日志框架的时候。可是 JCL 一直影响深远,SLF4J 渐入佳境的时个,在你的项目中很可能所用的组件,它们分别用了 JCL 和 SLF4J 两种组件。比如说在项目中用了 Hibernate 3.5 和 Struts,或其他 Apache 的一些开源组件,你大约也不想用了 SLF4J 的组件日志信息输出到 A 处,用了 JCL 的组件日志输出到 B 处,那你自己写的代码中的日志信息该往哪儿写呢? 

中国人一直都在追求大一统,不喜欢城邦制而便于分而制之。但说到日志输出还是得统一到单一通道中,一方面多个通道浪费资源,另方面也便于配置和管理。那么既然 SLF4J 是趋势,当 SLF4J 和 JCL 被丢到一个坛子里,首先会让 SLF4J 为主,JCL 为辅,也就是要把 JCL 桥接到 SLF4J 上来,通过 SLF4J 统一输出日志信息。于是也就是这篇要介绍的 SLF4J 使用模式:JCL-Over-SLF4J+SLF4J。 

从前面对 SLF4J 的认识可知,即使把 JCL 转嫁到 SLF4J,还是无法输出日志,还需要一种日志实现,下层该用 Log4J 还得用 Log4J,想用 Logback 还是要用 Logback。所以到了 SLF4J 后还得往下走,也就是前面那两条路 SLF4J+Log4J 和 SLF4J+Logback,本篇使用 SLF4J 的模式具体就要分为: 

JCL-Over-SLF4J+ SLF4J+Log4J  和 JCL-Over-SLF4J+ SLF4J+Logback,这两种实现方式差不多。只是分别用的 jar 包和配置文件不同,SLF4J+Log4J 和 SLF4J+Logback 原来要哪些文件现在还是需要那些文件,只是都要加上 jcl-over-slf4j-1.5.11.jar 包。这里说明 JCL-Over-SLF4J+ SLF4J+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 
5. jcl-over-slf4j-1.5.11.jar

第 1 和第 5 个包在 http://www.slf4j.org/download.html 处下载,第二第三个包在 http://logback.qos.ch/download.html 下载,可能包文件名中的版本号有些差异。 

下面是一个最简单的 logback.xml 文件内容 

 <?xml version="1.0" encoding="UTF-8"?>

<configuration>

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">

<encoder charset="GBK">

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

</encoder>

</appender>

<root level="DEBUG">

<appender-ref ref="stdout" />

</root>

</configuration>

为了看看效果,我们在输入的 pattern 中加入了 [Consociate],来检验是否统一到单一的日志通道中去了。 

使用 了 JCL 和 SLF4J  的代码

package com.unmi;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class TestJCLOverSlf4j {

//SLF4J 的 Logger

private static final Logger logger = LoggerFactory.getLogger("From SLF4J");

//JCL 的 Log

private static final Log log = LogFactory.getLog("From JCL");

//分别用上面的 logger 和 log 输出日志,从输出可以看到它们统一到一个通道中了

public static void main(String[] args) {

logger.info("Hello {}","From SLF4J");

log.info("Hello From JCL");

}

}

我们在上面代码中,既使用了 JCL 统一日志框架,也使用了 SLF4J 的统一日志框架。要注意一点,从 JCL 桥接过来的 log 不能输出参数化消息了。上面代码使用了 org.apache.commons.logging.Log,import org.apache.commons.logging.LogFactory,但你却用不着引入 commons-logging.jar 包。 

执行上面的代码,看到输出: 

[Consociate] 23:19:39.890 [main] INFO  From SLF4J - Hello From SLF4J 
[Consociate] 23:19:39.921 [main] INFO  From JCL - Hello From JCL 

很明显示 JCL 框架和 SLF4J 框架的日志输出都统一到了一个通道中来了,为什么呢? SLF4J 使用的是 Logback 输出的信息,这一点没问题的,而 JCL 是不认识 Logback 的,所以 JCL 框架的输出必然是绕道到 SLF4J,最后也是由 Logback 输出的。 

实现分析: 

我们打开 jcl-over-slf4j-1.5.11.jar,看到里面有两个包 org.apache.commons.logging 和 org.apache.commons.logging.impl,并有相应的类,这就是为什么,虽然在代码中有: 

import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 

却不用把 commons-logging.jar 包引入到类路径上的原因。 

再深入下 jcl-over-slf4j-1.5.11.jar,看到其中还有个文件 /META-INF/services/org.apache.commons.logging.LogFactory,内容为: 

org.apache.commons.logging.impl.SLF4JLogFactory

# Axis gets at JCL through its own mechanism as defined by Commons Discovery, which 
# in turn follows the instructions found at: 
http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service Provider 

JCL 运行时使用了 SLF4JLogFactory,从而完成了 JCL 的日志实现委托给了 SLF4J,再由 SLF4J 进一步完成具体的日志输出。 

采用 JCL-Over-SLF4J+ SLF4J+Log4J 使用模式也是相似的,这里就不详述了。总结下就是 JCL 把 SLF4J 当作它的日志实现。 

再来想象个问题:如果我们把这两个包 jcl-over-slf4j-1.5.11.jar 和 slf4j-jcl-1.5.11.jar 都放到 ClassPath 下会有什么情况呢?JCL 代理给 SLF4J,SLF4J 又绑定到 JCL,对了,死循环,StackOverFlow 错误: 

SLF4J: Detected both jcl-over-slf4j.jar AND slf4j-jcl.jar on the class path, preempting StackOverflowError. 
SLF4J: See also http://www.slf4j.org/codes.html#jclDelegationLoop for more details. 
java.lang.ExceptionInInitializerError 
 at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:82) 
 at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:51) 
 at org.slf4j.LoggerFactory.getSingleton(LoggerFactory.java:230) 
 at org.slf4j.LoggerFactory.bind(LoggerFactory.java:121) 
 at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:112) 
 at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:275) 
 at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:248) 
 at com.unmi.TestJCLOverSlf4j.<clinit>(TestJCLOverSlf4j.java:10) 
Caused by: java.lang.IllegalStateException: Detected both jcl-over-slf4j.jar AND slf4j-jcl.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#jclDelegationLoop for more details. 
 at org.slf4j.impl.JCLLoggerFactory.<clinit>(JCLLoggerFactory.java:64) 
 ... 8 more 
Exception in thread "main" 

原因是引用了两个关于log4j的jar,造成死循环

log4j-over-slf4j-1.7.7.jar

slf4j-log4j12-1.7.7.jar

但只引用slf4j-log4j12-1.7.7.jar会提示org.apache.log4j.Level这个class找不到

要使用log4j记录日志,其实只用引用log4j-over-slf4j-1.7.7.jar就可以了

SLF4J 的几种实际应用模式--之三:JCL-Over-SLF4J+SLF4J的更多相关文章

  1. redis解决方案之三种集群模式的概念与部署

    上篇文章为大家总结了redis命令并讲述了持久化,今天我们来看一下redis的三种集群模式:主从复制,哨兵集群,Cluster集群 本篇文章先介绍redis-cluster集群模式,然后再依次介绍它的 ...

  2. activity的四种加载模式

    在android里,有4种activity的启动模式,分别为: standard, singleTop, singleTask和singleInstance, 其中standard和singleTop ...

  3. Activity有四种加载模式(转)

    Activity有四种加载模式: standard singleTop singleTask singleInstance 在多Activity开发中,有可能是自己应用之间的Activity跳转,或者 ...

  4. 简单区分VMware的三种网络连接模式(bridged、NAT、host-only)

    艺搜简介 VMware在安装时默认安装了两块虚拟网卡,VMnet1和VMnet8,另外还有VMnet0.这些虚拟网卡的配置都是由Vmware虚拟机自动生成的,一般来说不需要用户自行设置. Vmware ...

  5. 五种I/O 模式,select、epoll方法的理解,BIO、NIO、AIO理解 相关文章

    一.io方式 Linux网络编程 五种I/O 模式及select.epoll方法的理解 web优化必须了解的原理之I/o的五种模型和web的三种工作模式 五种I/O 模式——阻塞(默认IO模式),非阻 ...

  6. VMware虚拟系统 bridged、NAT、host-only三种网络连接模式

    目录 前言 bridged(桥接模式) NAT(网络地址转换模式) host-only(仅主机模式) 总结 前言 如果你想利用VMWare安装虚拟机,或想创建一个与网内其他机器相隔离的虚拟系统,进行特 ...

  7. 活动 Activity 四种加载模式

    singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不发送给新的实例.(注意是栈顶,不在栈顶照样创建新实例!) singleTas ...

  8. 3 weekend110的job提交的逻辑及YARN框架的技术机制 + MR程序的几种提交运行模式

    途径1: 途径2: 途径3: 成功! 由此,可以好好比较下,途径1和途径2 和途径3 的区别. 现在,来玩玩weekend110的joba提交的逻辑之源码跟踪 原来如此,weekend110的job提 ...

  9. 五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O

    五种I/O 模式——阻塞(默认IO模式),非阻塞(常用语管道),I/O多路复用(IO多路复用的应用场景),信号I/O,异步I/O 五种I/O 模式:[1]        阻塞 I/O          ...

随机推荐

  1. Iris的R语言命令工具箱(1)

    Iris的R语言命令工具箱(1) 最近在做数据分析,使用了R语言做了些数据处理和可视化,在此记下遇到过的问题.应用过的命令.处理方式以及工具包- *版权声明:本文为博主原创文章,转载请注明本文地址.h ...

  2. ajax提交的javascript代码

    var xhr=xhr(); function xhr(){ if(window.XMLHttpRequest){ return   window.XMLHttpRequest(); }else if ...

  3. 众人拾柴火焰高之Tomcat集群

    一人拾柴火不旺,众人拾柴火焰高.Tomcat服务器也是一样,一台服务器再强大能承受的访问也是有限的.要提供高并发.高可用的服务,就必须横向扩展,多台Tomcat组成一个集群,根据实际的访问量动态增减服 ...

  4. Hbase数据库安装

    一.环境准备 1.Ubuntu14.04-server 2.ssh 3.jdk1.6 4.hbase-0.98.19-hadoop2-bin.tar.gz(下载地址http://www.apache. ...

  5. Spring 中使用Quartz实现任务调度

    前言:Spring中使用Quartz 有两种方式,一种是继承特定的基类:org.springframework.scheduling.quartz.QuartzJobBean,另一种则不需要,(推荐使 ...

  6. Java虚拟机中Java内存区域

      Java虚拟机所管理的内存将会包括以下几个运行时数据区域. 程序计数器 可以看作是当前线程所执行的字节码的行号指示器. 每一个线程都需要有一个独立的程序计数器. 如果线程正在执行的是一个Java方 ...

  7. Azure 基础:用 PowerShell 自动发布 CloudServices

    在软件的开发过程中,自动化的编译和部署能够带来很多的优势.下面我们聊聊如何自动发布云应用程序到 azure 上的 cloud services. 打包要发布的内容 首先使用 msbuild 编译 *. ...

  8. 在VMware Workstation 9中安装Mac OS X 10.8 Mountain Lion

    本文环境: CPU:Intel Core i7 920: OS:Windows 7: 内存:8G: 玩Hackintosh各有各的理由,不管什么理由,利用虚拟机安装Mac OS X都是一个可行的办法. ...

  9. 《剑指offer》— JavaScript(1)二维数组中的查找

    二维数组中的查找 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ** ...

  10. BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)

    首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...