目录
1.如果slf4j里引入了多种连接器和底层实现.那么真正执行的是谁?
2.项目日志包只加载这些,且没有配置文件,请问Spring的debug运行日志会显示出来吗?底层实现是哪个日志系统?
4.有些依赖源码里直接使用的log4j,而我现在希望由slf4j+logback来控制所有的日志怎么办?
前言:
编写目的:如果你觉得你的项目里日志系统像不同动物的屎一样混在一起,那么这篇文章是你所需要的
阅读要求:本文的读者要求至少使用过一段时间日志系统。对于没有使用过的,不建议你看。
其他:欢迎转载,但请注明出处。
分类
目前JAVA流行的日志系统有
Java.util.Logger 以后简称javaLog |
log4j |
logback |
NOPlogger,以后简称Nop |
slf4j |
common-logger 以后简称jcl |
介绍
以上6个日志系统,其中slf4j和jcl是接口是规则,相当于jdbc,其他4个是具体实现。
也就是说如果只引入了slf4j和jcl,那么日志系统是根本没有卵用的。
但是你可以不引入slf4j和jcl,而只引入其他实现,那么实现日志是可以的。(除了logback)
接下来逐一介绍。
javaLog
这个是JDK1.4的时候增加的日志类。使用起来很简单。看代码,内容很简单,打印了两行
import java.util.logging.Logger;
public class JDKLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger("aaaa");
System.out.println(logger.getLevel());//获取当前Log的等级
logger.info("111111");
logger.info("22222");
}
}
再看下现象,这个现象很有趣,所以要截图。
如果你用过tomcat,就知道这里的打印信息和tomcat默认打印的信息简直一摸一样。说明tomcat默认使用的就是JavaLog,只是日志等级默认却是null。
javalog使用好的话比较麻烦且基本概念落伍。
log4j
apache组织的日志系统。是最火爆的,看下如何使用
import org.apache.log4j.Logger;
public class Log4jLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Log4jLog.class);
logger.info("Log4j");
}
}
如果只是这样的话,那么绝对打印不了内容,只会打印红色的警告提示,因为对于Log4j来说,配置文件是必须的。
你可以加上一个log4j.properties,然后再打印就打出来了。
logback
logback是一个log4j作者二次开发的一个更为高效快速的日志系统。但是它不能单独使用,因为它没有入口,你必须使用接口来使用它,比如slf4j或者jcl
我们接下来用slf4j来看logback如何使用的,你需要这样作。
引入且只引入以下三个包:slf4j - api.jar,logback - core.jar 和logback - classic.jar。(不要混有其他日志包,原因在slf4j里面讲。)
然后代码这样写,先不要运行。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackLog {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogBackLog.class);
logger.info("aaaaaaaa");
System.out.println(logger.getClass());
}
}
如果你能认真一些,就能发现这个测试类里并没有引入logback的类,很奇怪的事情!不过还有更奇怪的事情,接下来请你运行。
18:06:08.894 [main] INFO com.bai.momo.LogBackLog - aaaaaaaa
class ch.qos.logback.classic.Logger
打印了日志,而且打印了logger的className,竟然从org.slf4j.Logger变成了 class ch.qos.logback.classic.Logger。
原因暂时不说,在slf4j里面讲。
现在你还有更需要注意的事情!
请把代码里的logger.info改为logger.debug,再次运行。
你会发现日志还是打印了。如果你了解一些关于日志级别的有关信息,那么你应该知道本例中的logger的级别就是DEBUG级别,否则打印不出来。
原因在于:logback会先去寻找配置文件logback-test.xml或logback.xml,如果没有找到,则会使用ch.qos.logback.core.ConsoleAppender这个默认的控制台输出配置。而logback的根记录器的级别默认是DEBUG。
如果你遇到过控制台debug日志无脑刷出,什么都控制不了,那么原因就在于此。那么请配置一个logback.xml来修改格式或者级别即可。一个简单的info级别的logback.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.padual.com/java/logback.xsd"
debug="false" scan="true" scanPeriod="30 second">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="utf-8">
<pattern>%n%-6p %m %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Nop
Nop就是静默处理,就是什么都不干。爱咋咋滴,也不打印也不输出。
你可以找任意一款NopLogger.java的源码,就会发现它里面都是空的。
Nop的作用是什么?在slf4j里说。
slf4j
重头戏到了。
slf4j是简单日志门面,它不提供具体的实现,只是它有和其他日志之间的连接器,姑且就叫做连接器吧。如下图:
先不看JCL.先看蓝色和绿色。
slf4j如果底层使用javaLog,需要slf4-jdk.jar
slf4如果底层使用log4j,需要使用slf4-log4j.jar
slf4如果底层使用logback,需要使用logback-classic.jar
而slf4j的核心jar包是slf4j - api.jar。
那么如果你想使用slf4j,请先导入slf4j - api.jar,然后选择其中一个底层日志系统,引入其包以及连接器的jar包。接着如下使用即可。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackLog {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogBackLog.class);
logger.info("aaaaaaaa");
System.out.println(logger.getClass());
}
}
这个代码片和上面讲logback的代码片一摸一样。这就是接口的魅力,如果你想更换底层日志系统,只需要更换底层日志包和连接器包即可。
自动搜寻底层日志系统的原理就是:其实每一个连接器里都有一个org.slf4j.impl.StaticLoggerBinder这个类,是相同的包相同的类哦。slf4j会自动搜索类路径下的org.slf4j.impl.StaticLoggerBinder,如果搜到了某个org.slf4j.impl.StaticLoggerBinder,就加载该StaticLoggerBinder所处的连接器,使用该连接器里所默认的底层日志系统。
注意以下几点:
1.如果你只引入了slf4j - api.jar,而不引入底层实现或连接器,那么运行上面的代码将不会打印日志,而log.getClass的结果是NopLogger,Nop作用就是这样,既然我不知道咋办,那我就啥都不干。
2.如果你更换了底层实现或连接器,那么logger,getClass将会改变,因为具体实现已经改变。
3.如果你更换成了log4j或者logback,请附带上她们的log4j.properties或logback.xml。
4.如果你引入了多种底层实现或连接器,那个现象,呵呵了~~~~~~解释一下,slf4j是严格不推荐你这样做的,但是现在的系统都有各种依赖,而依赖里的日志系统又不尽相同,所以有时我们也没有办法。该怎么做呢?请看问答
jcl
指的是org.apache.common-logging.jar包。
是apache早期的项目,2000年左右的时候就有了,期间也更新过几次。
它也是个日志的门面系统,同样的也有连接器,但最重要的是它的配置文件,说到这里,有同学可能会奇怪,为什么我一直在用common-logging.jar,而不需要配置文件,却能照样打印使用啊?
说来话长,请看jcl目前的搜索底层日志实现的执行流程,以下流程,顺序执行,如果匹配,则立即终止:
1.查找配置文件commons-logging.properties。如果有,看里面配置了什么底层实现。
2.查找System.getProperties("org.apache.commons.logging.LogFactory"),如果不是null,看结果代表了什么底层实现.
3.查找所有jar包的注册信息,如果里面有META-INF/services/org.apache.commons.logging.LogFactory这个文件,读取该文件的第一行即是配置了底层实现.
4.如果有log4j.jar.则设置为log4j为底层实现
5.如果是java1.4以后,则设置为javaLog
6.使用自带的默认的SimpleLog
而我们平时使用jcl一般都是配合Log4j使用,都是到了第四步终止。
而且即使没有log4j,还有第五步使用javaLog呢!所以使用JCL时完全不用担心的。
第三步则是jcl连接器的事情,如果有连接器,那么log4j就没有用了。
问答:
1.如果slf4j里引入了多种连接器和底层实现.那么真正执行的是谁?
我只知道现象,哪个连接器先加载,就执行谁.因为slf4j搜索连接器会搜索所有的连接器,然后放到一个数组里.但是好像默认取数组[0]作为真正的日志实现.
2.项目日志包只加载这些,且没有配置文件,请问Spring的debug运行日志会显示出来吗?底层实现是哪个日志系统?
slf4j - api.jar,
common-logging.jar,
log4j.jar,
logback-core.jar,
logback-classic.jar,
jcl-over-slf4j.jar
debug日志会显示出来,底层是logback
原因是:spring使用jcl作为日志门面,jcl会按照搜索顺序,在第三步停止,因为jcl-over-slf4j.jar是将slfj作为jcl的底层实现d的连接器,而slf4j启动起来也会搜索真正的底层实现,所以最终只搜到了logback的连接器(缺少log4到slf4j的连接器)。而logback.xml没有配置出来,所以会使用默认的控制台打印且默认级别是debug.
实际上就有了一种桥接的作用.
3.我们讲slf4j时那个连接器图里,还有个slf4j-jcl的连接器的jar,那个是将jcl作为slfj的底层实现的连接器,和jcl-over-slf4j.jar作用正好相反,如果将该jar投入到第二题环境里面。然后将logback-classic.jar删除掉,则会发生什么事情?会不会发生slf4j和jcl循环调用最终导致栈溢出呢?
自己去试试吧!
4.有些依赖源码里直接使用的log4j,而我现在希望由slf4j+logback来控制所有的日志怎么办?
jcl-over-slf4j.jar是jcl桥接到slf4j的.
log4j-over-slf4j.jar是log4j桥接到Slf4j里面的.
当然也有javaLog桥接到Slf4里面的.
切记使用这种模式的时候,小心自循环调用.
最优使用
强烈推荐slf4j作为门面,然后使用logback作为底层实现,因为logback十分高效,当然你可以随时切换底层实现。
由于一些依赖使用了jcl,那么请添加jcl-over-slf4j.jar,将jcl的日志系统实际上由slf4j把控。
结语:
写作原因是因为自己的项目里引入多种依赖,导致日志系统各种灵异事件,网上教程,也都是读起来无味之极,索性便花了两天略读源码,又去官网看文档,大抵才了解一些。
写了4个半小时,酣畅淋漓。
煎炒烹炸,盛盘出来,以飨诸君。