内 容:

现如今,日志框架层出不穷,JDKLogger、Log4j、Logback等这些是最常用的了。然而现在越来越多的框架中,都会在使用日志框架的同时,还会使用到一个门面(slf4j-api.jar),使用这个门面的的最方便的地方大抵是它提供格式化字符串的功能。

slf4j 与其他日志框架的关系

在应用程序中,直接使用slf4j-api.jar就可以完成日志的记录,而不用在代码里直接使用某一种日志框架了(虽然最终记录日志还是有日志框架来完成的)。

下面是使用了slf4j时,应用程序、slf4j、slf4j-adapter.jar、日志框架之间的调用关系:

下面是一个简单的示例:

package com.fjn.frame.slf4j;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class HelloWorld { public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}

LoggerFactory.getLogger(xxx)要分为两个过程:

1、实例化ILoggerFactory, 这个步骤只是在第一次进行。

2、根据ILoggerFactory实例创建Logger实例。

下面就分别来说说这两个过程:

ILoggerFactory 实例化的过程

在使用slf4j时,并不需要指定具体使用哪种日志框架,只需要给定相关的适配器包就可以了。那么如何拿到真正的ILoggerFactory实现,并实例化的呢?

1、在LogFactory的classloader的搜索路径下查找 ” org/slf4j/impl/StaticLoggerBinder.class” 类

private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

  private static void singleImplementationSanityCheck() {
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
.getClassLoader();
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader
.getResources(STATIC_LOGGER_BINDER_PATH);
}
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order during iteration
Set implementationSet = new LinkedHashSet();
while (paths.hasMoreElements()) {
URL path = (URL) paths.nextElement();
implementationSet.add(path);
}
if (implementationSet.size() > 1) {
Util.report("Class path contains multiple SLF4J bindings.");
Iterator iterator = implementationSet.iterator();
while(iterator.hasNext()) {
URL path = (URL) iterator.next();
Util.report("Found binding in [" + path + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
}

如果有多个就会打印:

2、将slf4j与指定的实现进行绑定,这一步的操作,通常是:

1)单实例化StaticLoggerBinder。

2)检查StaticLoggerBinder的版本,其实就是需要有一个静态的非final的变量(这个变量不是必须得有的),

StaticLoggerBinder.REQUESTED_API_VERSION

3、获取到ILoggerFactory的实例

一般来说,是返回一个static ILoggerFactory impl=new   XXXLoggerFactory();

由ILoggerFactory来创建Logger实例

LoggerFactory创建Logger实例,其实由日志框架本身的LogManager创建一个Logger,然后包装成LoggerAdapter。例如Log4j的适配包中的Log4jLoggerFactory#getLogger(String name)的实现如下:

public Logger getLogger(String name) {
Logger slf4jLogger = loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
log4jLogger = LogManager.getRootLogger();
else
log4jLogger = LogManager.getLogger(name); Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}

下面用是一张简易的关系图,显示了适配器的实现:

自定义日志框架适配器

在一些公司,肯定还有自己的Logger框架,如果也希望通过slf4j来做日志,就需要写相关的适配器包了。通过上述的两个过程的了解,很容易就能知道如何自定义适配器了。

自定义适配器中,必须包括3个组件:

· StaticLoggerBinder

这个类需要遵守下列规约:

1)  类名必须是org.slf4j.impl.StaticLoggerBinder

2)  这个类必须是单例的,必须有getSingleton()方法

3)  尽可能的有 public static String REQUESTED_API_VERSION 字段,并且不能是final的。

4)  要实现org.slf4j.spi.LoggerFactoryBinder接口。

· LoggerFactoryImpl

这个类要实现org.slf4j.ILoggerFactory接口

· LoggerAdapter

这个类要实现org.slf4j.Logger接口。

如何选取多个Log框架

SLF4j原则上只会绑定一个适配器,所以通常我们的项目中,最好只使用一种日志框架。然而事与愿违,现在项目中通常都会引入大量第三方框架等,这些框架使用的日志框架又不尽相同,所以引入多个日志框架的情况也是不可避免的。SLF4j初始化时,如果找到了多个相关的适配器,只会使用找到的第一个适配器。

在项目中要尽量减少引入的log框架的数量,通常会使用maven exclusion:

<dependencies>
<dependency>
<groupId> org.apache.cassandra</groupId>
<artifactId>cassandra-all</artifactId>
<version>0.8.1</version> <exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions> </dependency>
</dependencies>

如何将项目从Commons-Logging迁移到SLF4j


如果项目中(或者引入的第三方jar中)已经使用了commons-logging,要迁移至SLF4j。

需要做两个事:

1、将项目中已使用commons-logging的模块、以及第三方jar。使用maven exclustions排除掉。

2、引入jcl-over-slf4j.jar。

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>

原因:

项目(或者第三方jar)中使用的是commons-logging的API:org.apache.commons.logging.Log、org.apache.commons.logging.LogFactory。

这两个类在jcl-over-slf4j.jar中都有。不过修改了其实现。

在修改后的org.apache.commons.logging.LogFactory的实现是将原来的LogFactory查找过程完全替换成了jcl-over-slf4j.jar包中的一个SLF4jLogFactory:

package org.apache.commons.logging;

import java.util.Hashtable;

import org.apache.commons.logging.impl.SLF4JLogFactory;

/**
* <p>
* Factory for creating {@link Log} instances, which always delegates to an
* instance of {@link SLF4JLogFactory}.
*
* </p>
*
* @author Craig R. McClanahan
* @author Costin Manolache
* @author Richard A. Sitze
* @author Ceki G&uuml;lc&uuml;
*/ public abstract class LogFactory { static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = "http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j"; static LogFactory logFactory = new SLF4JLogFactory(); public static LogFactory getFactory() throws LogConfigurationException {
return logFactory;
} ...
... }

而SLF4jLogFactory的实现是委托给slf4j的LoggerFactory去定位日志框架并取得Logger对象,最后将取得的Logger对象再包装成commons-logging中的Log实现:

public class SLF4JLogFactory extends LogFactory {
... public Log getInstance(String name) throws LogConfigurationException {
Log instance = loggerMap.get(name);
if (instance != null) {
return instance;
} else {
Log newInstance;
Logger slf4jLogger = LoggerFactory.getLogger(name); // 又委托给了Slf4j的 LoggerFactory
if (slf4jLogger instanceof LocationAwareLogger) {
newInstance = new SLF4JLocationAwareLog((LocationAwareLogger) slf4jLogger);
} else {
newInstance = new SLF4JLog(slf4jLogger);
}
Log oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
} }

所以迁移时,一定要完全排除commons-logging的jar,并引入 jcl-over-slf4j.jar

SLF4j:Log facade abstract的更多相关文章

  1. slf4j(simple logging facade for java)

    http://www.tuicool.com/articles/IfeUfq   slf4j(simple logging facade for java)是Java的简单的日志门面,它 不是具体的日 ...

  2. 转:Java中abstract和interface的区别

    转自:Java中abstract和interface的区别 abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java ...

  3. Golang 源码剖析:log 标准库

    Golang 源码剖析:log 标准库 原文地址:Golang 源码剖析:log 标准库 日志 输出 2018/09/28 20:03:08 EDDYCJY Blog... 构成 [日期]<空格 ...

  4. 学习:Log中'main', 'system', 'radio', 'events'

    在Android中不同的log写到不同的设备中,共有/dev/log/system, /dev/log/main, /dev/log/radion, /dev/log/events四中类型.其中默认L ...

  5. 转:Log Explorer使用说明恢复被误删除的数据

    一.介绍 Log Explorer主要用于对MSSQLServer的事物分析和数据恢复.你可以浏览日志.导出数据.恢复被修改或者删除的数据(包括执行过update,delete,drop和trunca ...

  6. IIS 日志分析工具:Log Parser Studio

    1.安装Log Parser,下载地址:http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=24659 ...

  7. 复分析可视化方法:笔记:log(z)的可视化微分法

    当z转过θ时,我们来看看发生了什么: 左图中的空心箭头代表z的变化量,其长度为rδ,方向为pi/2+θ: 右图中的实心箭头代表log(z)的变化量,其长度为δ,方向为pi/2. 因此,从左图空心箭头到 ...

  8. web 安全相关(一):Log注入(转)

    在网上有很多关于安全的文章,但是 OWASP 开发指导 涵 盖了几乎所有关于Web站点安全的东西.(注:OWASP(开放Web应用安全项目- Open Web Application Security ...

  9. 面试题思考:interface和abstract的区别

    抽象类(abstract) 含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象. 含有abstract方法的类必须定义为abstract class,abstra ...

随机推荐

  1. java 开发中经常问到得懒汉模式 (单利模式)

    //懒汉模式 class Single { public static Single s = null; public Single (){} public static Single getInst ...

  2. 1Z0-053 争议题目解析686

    1Z0-053 争议题目解析686 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 686.You execute the following FLASHBACK TABLE com ...

  3. java多线程--定时器Timer的使用

    定时的功能我们在手机上见得比较多,比如定时清理垃圾,闹钟,等等.定时功能在java中主要使用的就是Timer对象,他在内部使用的就是多线程的技术. Time类主要负责完成定时计划任务的功能,就是在指定 ...

  4. (十九)WebGIS中I查询的原理及设计(包含AGS、GeoServer、Supermap)

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 我们在使用arcmap时,经常会用到被称为I查询的工具.具体 ...

  5. HTML5 Application cache初探和企业应用启示

    Application Cache 在自己做的开源项目( https://github.com/etoah/Lucien ) 用到了HTML5 的Application Cache,现总结如下: 目录 ...

  6. C++控制台贪吃蛇代码

    游戏截图: 以下是3个代码文件: Snake_Class.h文件: #ifndef SNAKE #define SNAKE #include<windows.h> #include< ...

  7. nginx 添加nginx-http-concat模块

    github地址:https://github.com/alibaba/nginx-http-concat/tree/master 简单的描述一下吧,网上说的安装新的模块需要重新编译nginx,具体的 ...

  8. SQL性能优化常见措施(Lock wait timeout exceeded)

    SQL性能优化常见措施 目 录 1.mysql中explain命令使用 2.mysql中mysqldumpslow的使用 3.mysql中修改my.ini配置文件记录日志 4.mysql中如何加索引 ...

  9. [moka同学笔记]php 获取时间(今天,昨天,三天内,本周,上周,本月,三年内,半年内,一年内,三年内)

    <?php /** * php 获取时间(今天,昨天,三天内,本周,上周,本月,三年内,半年内,一年内,三年内) * * author:ihelloworld2010@gmail.com * d ...

  10. 新闻类网站rss接口的编写心得

    使用的是Jdom中的相关API,具体步骤如下 要求的格式: <rss xmlns:content="http://purl.org/rss/1.0/modules/content/&q ...