在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的关系及使用方法。
如上图,在开发过程中常用的日志框架大概包括:log4j、jul、jcl、slf4j几种,其中log4j和jul是两种具体的日志实现框架,而jcl和slf4j则不是一个具体的实现框架,而只是日志框架的搬运工,他们定义了日志的接口,底层具体使用的是log4j或jul,具体关系可以参考上图所示。朋友们是不是有个疑问,既然有了Log4j和jul这种日志实现框架,那为啥还要有jcl和slf4j两个框架呢?这里根据具体的项目来说明。
在实际的项目开发过程中,随着项目的变动,什么新需求,人员变动都是很正常的,假如有一个项目很早的时候架构师使用的是jul作为日志框架,当项目运行一段实践后,新来了一个项目经理,接到了一个新的需求,需要更换成log4j日志框架(原因有很多),请问这时候需要怎么办呢?如果要更换,那么修改的成本就比较高了,需要修改整个系统了,复杂度可想而知。可是如果使用的是jcl或者slf4j这种框架,那情况就完全不一样了,因为jcl和slf4j采用的是面向“接口编程”的设计方式,它不是一种具体的日志实现,可以灵活支持其他的日志实现框架,这样如果需要从原来的使用jul更换成log4j,那就轻松多了,只要修改下依赖就可以搞定了。
好了,废话少说,下面详细描述每种日志框架的具体应用及实现原理:
一、log4j
log4j是java日志框架的一种具体实现,可以说是目前java项目中使用最广泛的日志框架,使用步骤如下:
1.添加log4j依赖
log4j
log4j
1.2.17
2.使用demo
import org.apache.log4j.Logger;
public class Log4jTest {
private final static Logger logger = Logger.getLogger("logTest");
public static void main(String[] args) {
logger.info("log4j test");
}
}
二、jul
jul全程是java Util Log,它是jdk自带的日志实现,不需要引入其他依赖包,直接使用即可。
import java.util.logging.Logger;
public class JulLogTest {
private final static Logger logger = Logger.getLogger("JulLogTest");
public static void main(String[] args) {
logger.info("jul test");
}
}
三、jcl
jcl是java commons logging的简称,它不是具体的日志实现框架,而是一种日志框架的抽象,使用步骤如下:
1.添加commons logging依赖包
注意log4j的依赖包不是必须的,如果没有引入log4j依赖包,则使用的是jul日志实现:
commons-logging
commons-logging
1.2
log4j
log4j
1.2.17
2.使用demo
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JclLogTest {
private final static Log log = LogFactory.getLog("jclLogTest");
public static void main(String[] args) {
log.info("jcl test");
}
}
3.原理分析
jcl是如何实现灵活使用不同的日志实现框架的呢?从上述实例中,咱们进入LogFactory的具体实现类LogFactoryImpl的getInstance方法,这个方法是获取具体Log对象的方法:
public Log getInstance(String name) throws LogConfigurationException {
Log instance = (Log) instances.get(name);
if (instance == null) {
instance = newInstance(name);
instances.put(name, instance);
}
return instance;
}
在newInstance方法中有一段代码:
instance = discoverLogImplementation(name);
这段代码调用discoverLogImplementation去发现日志的具体实现:
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
result = createLogFromClass(classesToDiscover[i], logCategory, true);
}
在discoverLogImplementation方法中,默认情况下,会进入如上代码逻辑,其中classesToDiscover数组列出了jcl默认支持的日志实现类,如下代码所示,好了,现在大家清楚了嘛?jcl是循环加载这个数组中的对象,加载到了就返回了,所以从代码中咱们可以看出,jcl优先的日志实现是有先后顺序的,优先使用的是log4j:
private static final String[] classesToDiscover = {
"org.apache.commons.logging.impl.Log4JLogger",
"org.apache.commons.logging.impl.Jdk14Logger",
"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
"org.apache.commons.logging.impl.SimpleLog"
};
四、slf4j
1.添加slf4j依赖包
org.slf4j
slf4j-api
1.7.2
org.slf4j
slf4j-log4j12
1.7.2
runtime
org.slf4j
slf4j-jcl
1.7.2
runtime
org.slf4j
slf4j-jdk14
1.7.2
runtime
2.使用demo
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jLogTest {
private final static Logger logger = LoggerFactory.getLogger("Slf4jLogTest");
public static void main(String[] args) {
logger.info("slf4j test");
}
}
3.原理分析
slf4j采用的是绑定机制来确定具体的日志实现框架,具体如何绑定呢?这里我们进入LoggerFactory.getLogger方法:
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
这里返回的Logger对象是从ILoggerFactory接口的具体实现类返回的,继续进入getILoggerFactory方法,在该方法中我们可以看到有如下代码:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
好了,核心就是这个StaticLoggerBinder实现类,这个实现类在slf4j-log4j12、slf4j-jcl、slf4j-jdk14这三个不同的依赖jar包中都有自己的实现,从而通过不同的日志工厂类(ILoggerFactory)得到不同的Logger对象。
以上就是咱们项目过程中常用的日志框架关系整理,欢迎大家一起来交流学习。