slf4j 的介绍,使用与分析
来源:互联网 发布:发票数据管理软件 编辑:程序博客网 时间:2024/06/07 14:39
概述
slf4j的全称是Simple Loging Facade For Java,它仅仅是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如JDBC一样,只是一种规则而已。所以单独的slf4j是不能工作的,必须搭配其他具体的日志实现方案,比如apache的org.apache.log4j.Logger,jdk自带util.logging.Logger等等。
slf4j的优势
1.与客户端解耦
slf4j只是一种接口,它本身并不关心你底层使用的是什么日志实现方案,所以它支持各种日志实现方案。简单的说,只要我们在类库中使用slf4j打日志,那么底层使用什么日志实现方案是使用者决定的,怎么决定?依靠配置文件和jar库。
2.省内存
一般我们都是这样使用log4j日志的
public class TestLog4j { private static final Logger LOGGER = Logger.getLogger(TestLog4j.class);public static void main(String[] args) { String message = "Hello World."; LOGGER.info("This is a test message: " + message);}}注意到log4j的info函数有两种使用方式:
public void info(Object message)public void info(Object message, Throwable t)第一个参数是要输出的信息,假设要输出的是一个字符串,并且字符串中包含变量,则message参数就必须使用字符串相加操作,就比如上面测试代码的LOGGER.info()一样。姑且不说字符串相加是一个比较消耗性能的操作,字符串是一个不可变对象,一旦创建就不能被修改,创建的字符串会保存在String池中,占用内存。更糟糕的是如果配置文件中配置的日志级别是ERROR的话,这行info日志根本不会输出,则相加得到的字符串对象是一个非必须对象,白白浪费了内存空间。
再来看看slf4j的打日志的方式:
public class TestLog4j { private static final Logger LOGGER =LoggerFactory.getLogger(TestLog4j.class);public static void main(String[] args) { String message = "Hello World."; LOGGER.info("This is a test message: {}", message);}}如上,我们可以看出这时打日志的时候使用了{}占位符,这样就不会有字符串拼接操作,减少了无用String对象的数量,节省了内存。并且,在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。这里是使用SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar中的Log4j的适配器Log4jLoggerAdapter。
slf4j的使用与绑定原理
前面介绍了slf4j的优势,本节介绍怎么使用slf4j以及其中的原理,前文说到了,单独的slf4j是不能工作的,必须带上其他具体的日志实现方案。就以apache的log4j作为具体日志实现方案为例,如果在工程中要使用slf4j作为接口,并且要用log4j作为具体实现方案,那么我们需要做的事情如下:(下面的xxx表示具体版本号)
将slf4j-api-xxx.jar加入工程classpath中;
将slf4j-log4jxx-xxx.jar加入工程classpath中;
将log4j-xxx.jar加入工程classpath中;
将log4j.properties(log4j.xml)文件加入工程classpath中。
介绍一下工作原理:
首先,slf4j-api作为slf4j的接口类,使用在程序代码中,这个包提供了一个Logger类和LoggerFactory类,Logger类用来打日志,LoggerFactory类用来获取Logger;slf4j-log4j是连接slf4j和log4j的桥梁,怎么连接的呢?我们看看slf4j的LoggerFactory类的getLogger函数的源
码:
/** * Return a logger named corresponding to the class passed as parameter, using * the statically bound {@link ILoggerFactory} instance. * * @param clazz the returned logger will be named after clazz * @return logger */ public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); } /** * Return a logger named according to the name parameter using the statically * bound {@link ILoggerFactory} instance. * * @param name The name of the logger. * @return logger */ public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; performInitialization(); } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_FACTORY; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITIALIZATION:// support re-entrant behavior.// See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); }我们发现LoggerFactory.getLogger()首先获取一个ILoggerFactory接口,然后使用该接口获取具体的Logger。获取ILoggerFactory的时候用到了一个StaticLoggerBinder类,仔细研究我们会发现StaticLoggerBinder这个类并不是slf4j-api这个包中的类,而是slf4j-log4j包中的类,这个类就是一个中间类,它用来将抽象的slf4j变成具体的log4j,也就是说具体要使用什么样的日志实现方案,就得靠这个StaticLoggerBinder类。再看看slf4j-log4j包种的这个StaticLoggerBinder类创建ILoggerFactory长什么样子:
private final ILoggerFactory loggerFactory; private StaticLoggerBinder() { loggerFactory = new Log4jLoggerFactory(); try { Level level = Level.TRACE; } catch (NoSuchFieldError nsfe) { Util .report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version"); } } public ILoggerFactory getLoggerFactory() { return loggerFactory; }可以看到slf4j-log4j中的StaticLoggerBinder类创建的ILoggerFactory其实是一个org.slf4j.impl.Log4jLoggerFactory,这个类的getLogger函数是这样的:
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; } }就在其中创建了真正的org.apache.log4j.Logger,也就是我们需要的具体的日志实现方案的Logger类。就这样,整个绑定过程就完成了
- slf4j 的介绍,使用与分析
- Log4j 和 Slf4j 的介绍和使用
- slf4j 使用介绍
- slf4j 使用介绍
- slf4j 使用介绍
- log4j,slf4j及Commons Logging介绍与原理使用
- slf4j的介绍
- slf4j与log4j的配置和使用
- slf4j的优势与使用原理
- slf4j的优势与使用原理
- slf4j的优势与使用原理
- log4j与log4j2的配合使用、slf4j
- Slf4j MDC 使用和 基于 Logback 的实现分析
- slf4j日志的使用
- slf4j日志的使用
- SLF4J的使用
- slf4j的使用
- slf4j、log4j 的使用
- Linux练习(2)
- Zookeeper探究
- ubuntu搭建shadowsocks
- ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛
- phpstudy导入mysql
- slf4j 的介绍,使用与分析
- spring boot MVC 三 用户注册和登录的实现
- Android Studio插件使用详情
- 技术文章 | 6倍性能差100TB容量_阿里云POLARDB如何实现?
- Linux 常用命令
- python 处理 txt 文档数据
- LK光流算法
- 三种从sql server数据库里拿数据的方法
- bzoj1088: [SCOI2005]扫雷Mine