java日志学习(持续更新)
1.Java实现日志
java日志体系大体可以分为三个部分:日志门面接口、桥接器、日志框架具体实现。
原生日志实现
(http://www.importnew.com/16331.html)
Java日志API由以下三个核心组件组成:
Loggers:Logger负责捕捉事件并将其发送给合适的Appender。
Appenders:也被称为Handlers,负责将日志事件记录到目标位置。在将日志事件输出之前,Appenders使用Layouts来对事件进行格式化处理。
Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
当Logger记录一个事件时,它将事件转发给适当的Appender。然后Appender使用Layout来对日志记录进行格式化,并将其发送给控制台、文件或者其它目标位置。另外,Filters可以让你进一步指定一个Appender是否可以应用在一条特定的日志记录上。在日志配置中,Filters并不是必需的,但可以让你更灵活地控制日志消息的流动。
上面是java utils logging(jul)的组件。
2.其他框架
参考:
- (https://my.oschina.net/xianggao?tab=newest&catalogId=3413124)
- (https://blog.csdn.net/nowuseeme/article/category/6745678)
- (https://www.cnblogs.com/yudar/p/5113655.html)
- (https://www.cnblogs.com/chenhongliang/p/5312517.html)
对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j,Log4j 2,Commons Logging,Slf4j,Logback,Jul。
java常用日志框架类别介绍:
1、Log4j
Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。all<debug<info<warn<error<fatal<off,logger以名字为标识(类似于树结构),存在父子关系,
Threshold门槛优先级>Level优先级,如果子Logger设置了自己的Threshold,则会使用自己的Threshold,如果没有设置,则继续向上查询,直到找到一个父类的Threshold或者rootLogger的level
Appender具有累加继承性,可以通过setAdditivity管理累加性,level和Treshold具有继承性,
参考:(https://www.cnblogs.com/crazyacking/p/5456347.html#_label00)
(https://my.oschina.net/xianggao/blog/515216)
架构(https://my.oschina.net/xianggao/blog/518059)
2、Log4j 2
Apache Log4j 2是apache开发的一款Log4j的升级产品。
日志级别:TRACE, DEBUG,INFO, WARN, ERROR 以及FATAL。
参考:
(https://github.com/apache/logging-log4j2)
(http://logging.apache.org/log4j/2.x/)
架构(https://my.oschina.net/xianggao/blog/523020)
配置文件(https://my.oschina.net/xianggao/blog/523401)
3、Commons
Logging Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
参考:
(https://github.com/apache/commons-logging)
(http://commons.apache.org/proper/commons-logging/)
(https://blog.csdn.net/u011794238/article/details/50749260)
(https://blog.csdn.net/SakuraInLuoJia/article/details/53534949?utm_source=blogxgwz0#二-commons-logging简单日志实现)
4、Slf4j
类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。
参考:
(https://github.com/qos-ch/slf4j)
(https://www.slf4j.org/)
代码更改(https://www.slf4j.org/migrator.html)
5、Logback
一套日志组件的实现(slf4j阵营)。日志级别TRACE < DEBUG < INFO < WARN < ERROR,
参考:
(https://github.com/qos-ch/logback)
(https://logback.qos.ch/documentation.html)
分析(https://my.oschina.net/xianggao/blog/522590)
配置(https://www.cnblogs.com/warking/p/5710303.html)
(https://www.cnblogs.com/cjsblog/p/9113131.html)
6、Jul
(Java Util Logging),自Java1.4以来的官方日志实现。日志级别OFF(2^31-1)—>SEVERE(1000)—>WARNING(900)—>INFO(800)—>CONFIG(700)—>FINE(500)—>FINER(400)—>FINEST(300)—>ALL(-2^31),配置文件位置$JAVA_HOME/jre/lib/logging.properties。
参考:
(https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html)
分析(https://my.oschina.net/xianggao/blog/520776)
7、tinylog
轻量级的java和Android框架
(https://tinylog.org/)
(https://github.com/pmwmedia/tinylog)
3.java常用日志框架之间的关系
Log4j2与Log4j1发生了很大的变化,log4j2不兼容log4j1。
Commons Logging和Slf4j是日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)。log4j和Logback则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用这只需要关注接口而无需关注具体的实现,做到解耦。
比较常用的组合使用方式是Slf4j与Logback组合使用,Commons Logging与Log4j组合使用。
Logback必须配合Slf4j使用。由于Logback和Slf4j是同一个作者,其兼容性不言而喻。
Commons Logging与Slf4j实现机制对比:
Commons logging实现机制
Commons logging是通过动态查找机制,在程序运行时,使用自己的ClassLoader寻找和载入本地具体的实现。详细策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。由于OSGi不同的插件使用独立的ClassLoader,OSGI的这种机制保证了插件互相独立, 其机制限制了commons logging在OSGi中的正常使用。
Slf4j实现机制
Slf4j在编译期间,静态绑定本地的LOG库,因此可以在OSGi中正常使用。它是通过查找类路径下org.slf4j.impl.StaticLoggerBinder,然后绑定工作都在这类里面进。
如果在项目中如果选择日志框架:
如果是在一个新的项目中建议使用Slf4j与Logback组合,这样有如下的几个优点。
Slf4j实现机制决定Slf4j限制较少,使用范围更广。由于Slf4j在编译期间,静态绑定本地的LOG库使得通用性要比Commons logging要好。
Logback拥有更好的性能。Logback声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在Logback中需要3纳秒,而在Log4J中则需要30纳秒。LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的。
Commons Logging开销更高 在使Commons Logging时为了减少构建日志信息的开销,通常的做法是:
if(log.isDebugEnabled()){
log.debug("User name: " +
user.getName() + " buy goods id :" + good.getId());
}
在Slf4j阵营,你只需这么做:
log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId());
也就是说,slf4j把构建日志的开销放在了它确认需要显示这条日志之后,减少内存和cup的开销,使用占位符号,代码也更为简洁
Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档。
#1 日志架构总结#
SLF4j框架包括:
1、门面接口(slf4j-api包),负责用户使用时统一调用接口,不负责日志的具体实现,当不存在桥接器时,将会报错,存在多个桥接器也会报错,但会选择一个进行使用,
使用,形如Logger logger=LoggerFactory.getLogger(Log4jSlf4JTest.class);
调用Logger实际上是先在slf4j-api中LoggerFactory类试图初始化ILoggerFactory并使用LoggerFactory生成Logger。
2、桥接器(slf4j-log4j12包等等)是连接门面接口和日志框架(slf4j连接底层日志框架时),每个日志框架都有具体实现LoggerFactory和Logger等,在Logger里调用具体的日志框架,统一在StaticLoggerBinder类中返回ILoggerFactory类型,Log4jLoggerAdapter就是实现了slf4j定义的Logger接口。
3、日志框架(jdk logging、log4j、log4j2、logback等实现了对应桥接器的日志框架),
每个日志框架的具体实现不同,效率也就不同,如今比较好的就是log4j2>logback>log4j>jdk logging.
4、逆向桥接器(jul-to-slf4j、log4j-over-slf4j、jcl-over-slf4j等)是其他框架连接底层的slf4j时使用。通常是多模块使用不同日志框架引起冲突时或统一代码时使用。
slf4j使用预编译方式连接桥接器和日志框架,降低运行时的工作量,提高速率,比Commons logging更优。
参考:
(https://my.oschina.net/xianggao/blog/517188)
#2 各种jar包总结#
log4j1:
log4j:log4j1的全部内容
log4j2:
log4j-api:log4j2定义的API
log4j-core:log4j2上述API的实现
logback:
logback-core:logback的核心包
logback-classic:logback实现了slf4j的API
commons-logging:
commons-logging:commons-logging的原生全部内容
log4j-jcl:commons-logging到log4j2的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
slf4j转向某个实际的日志框架:场景介绍:如 使用slf4j的API进行编程,底层想使用log4j1来进行实际的日志输出,这就是slf4j-log4j12干的事。
slf4j-jdk14:slf4j到jdk-logging的桥梁
slf4j-log4j12:slf4j到log4j1的桥梁
log4j-slf4j-impl:slf4j到log4j2的桥梁
logback-classic:slf4j到logback的桥梁
slf4j-jcl:slf4j到commons-logging的桥梁
某个实际的日志框架转向slf4j:场景介绍:如 使用log4j1的API进行编程,但是想最终通过logback来进行输出,所以就需要先将log4j1的日志输出转交给slf4j来输出,slf4j再交给logback来输出。将log4j1的输出转给slf4j,这就是log4j-over-slf4j做的事。这一部分主要用来进行实际的日志框架之间的切换(下文会详细讲解)。
jul-to-slf4j:jdk-logging到slf4j的桥梁
log4j-over-slf4j:log4j1到slf4j的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
#3 集成总结#
##3.1 commons-logging与其他日志框架集成##
1 commons-logging与jdk-logging集成,需要的jar包:
commons-logging
2 commons-logging与log4j1集成,需要的jar包:
commons-logging
log4j
3 commons-logging与log4j2集成,需要的jar包:
commons-logging
log4j-api
log4j-core
log4j-jcl(集成包)
4 commons-logging与logback集成,需要的jar包:
logback-core
logback-classic
slf4j-api、jcl-over-slf4j(2个集成包,可以不再需要commons-logging)
5 commons-logging与slf4j集成,需要的jar包:
jcl-over-slf4j(集成包,不再需要commons-logging)
slf4j-api
##3.2 slf4j与其他日志框架集成##
slf4j与jdk-logging集成,需要的jar包:
slf4j-api
slf4j-jdk14(集成包)
slf4j与log4j1集成,需要的jar包:
slf4j-api
log4j
slf4j-log4j12(集成包)
slf4j与log4j2集成,需要的jar包:
slf4j-api
log4j-api
log4j-core
log4j-slf4j-impl(集成包)
slf4j与logback集成,需要的jar包:
slf4j-api
logback-core
logback-classic(集成包)
slf4j与commons-logging集成,需要的jar包:
slf4j-api
commons-logging
slf4j-jcl(集成包)
#4 日志系统之间的切换#
##4.1 log4j无缝切换到logback##
###4.1.1 案例###
我们已经在代码中使用了log4j1的API来进行日志的输出,现在想不更改已有代码的前提下,使之通过logback来进行实际的日志输出。已使用的jar包:
log4j
使用案例:
private static final Logger logger=Logger.getLogger(Log4jTest.class);
public static void main(String[] args){
if(logger.isInfoEnabled()){
logger.info("log4j info message");
}
}
上述的Logger是log4j1自己的org.apache.log4j.Logger,在上述代码中,我们在使用log4j1的API进行编程。现在如何能让上述的日志输出通过logback来进行输出呢?只需要更换一下jar包就可以:
第一步:去掉log4j jar包
第二步:加入以下jar包
log4j-over-slf4j(实现log4j1切换到slf4j)
slf4j-api
logback-core
logback-classic
第三步:在类路径下加入logback的配置文件
###4.1.2 切换原理###
看下log4j-over-slf4j(简化更改版的log4j)就一目了然了:
输入图片说明
我们可以看到,这里面其实是简化更改版的log4j。去掉log4j1的原生jar包,换成该简化更改版的jar包(可以实现无缝迁移)。但是简化更改版中的Logger和原生版中的实现就不同了,简化版中的Logger实现如下(继承了Category):
public class Category {
private String name;
protected org.slf4j.Logger slf4jLogger;
private org.slf4j.spi.LocationAwareLogger locationAwareLogger;
Category(String name) {
this.name = name;
slf4jLogger = LoggerFactory.getLogger(name);
if (slf4jLogger instanceof LocationAwareLogger) {
locationAwareLogger = (LocationAwareLogger) slf4jLogger;
}
}
}
从上面可以看到简化版中的Logger内部是使用slf4j的API来生成的,所以我们使用的简化版的Logger会委托给slf4j来进行输出,由于当前类路径下有logback-classic,所以slf4j会选择logback进行输出。从而实现了log4j到logback的日志切换。
##4.2 jdk-logging无缝切换到logback##
###4.2.1 案例###
private static final Logger logger=Logger.getLogger(JulSlf4jLog4jTest.class.getName());
public static void main(String[] args){
logger.log(Level.INFO,"jul info a msg");
logger.log(Level.WARNING,"jul waring a msg");
}
可以看到上述是使用jdk-logging自带的API来进行编程的,现在我们想这些日志交给logback来输出,解决办法如下:
第一步:加入以下jar包:
jul-to-slf4j (实现jdk-logging切换到slf4j)
slf4j-api
logback-core
logback-classic
第二步:在类路径下加入logback的配置文件
第三步:在代码中加入如下代码:
static {
SLF4JBridgeHandler.install();
}
###4.2.2 切换原理###
先来看下jul-to-slf4j jar包中的内容:
输入图片说明
我们看到只有一个类:SLF4JBridgeHandler。
它继承了jdk-logging中定义的java.util.logging.Handler,Handler是jdk-logging处理日志过程中的一个处理器(具体我也没仔细研究过),在使用之前,必须要提前注册这个处理器,即上述的SLF4JBridgeHandler.install()操作,install后我们就可以通过这个handler实现日志的切换工作,如下:
protected Logger getSLF4JLogger(LogRecord record) {
String name = record.getLoggerName();
if (name == null) {
name = UNKNOWN_LOGGER_NAME;
}
return LoggerFactory.getLogger(name);
}
在处理日志的过程中,使用了slf4j的原生方式LoggerFactory来获取一个slf4j定义的Logger来进行日志的输出,而slf4j则又会选择logback来进行实际的日志输出。
##4.3 commons-logging切换到logback##
###4.3.1 使用案例###
使用的jar包
commons-logging
案例如下:
private static Log logger=LogFactory.getLog(JulJclTest.class);
public static void main(String[] args){
if(logger.isTraceEnabled()){
logger.trace("commons-logging-jcl trace message");
}
}
可以看到我们使用commons-logging的API来进行日志的编程操作,现在想切换成logback来进行日志的输出(这其实就是commons-logging与logback的集成),解决办法如下:
第一步:去掉commons-logging jar包(其实去不去都无所谓)
第二步:加入以下jar包:
jcl-over-slf4j(实现commons-logging切换到slf4j)
slf4j-api
logback-core
logback-classic
第三步:在类路径下加入logback的配置文件
###4.3.2 切换原理###
这个原理之前都已经说过了,可以看下commons-logging与logback的集成
就是commons-logging通过jcl-over-slf4j 来选择slf4j作为底层的日志输出对象,而slf4j又选择logback来作为底层的日志输出对象。
##4.4 常用的日志场景切换解释##
上面把日志的切换原理说清楚了,下面就针对具体的例子来进行应用,先来看下slf4j官方的一张图:
输入图片说明
下面分别详细说明这三个案例
###4.4.1###
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程
commons-logging
log4j1
jdk-logging
改进:现在想统一将日志的输出交给logback
第一步:将上述日志系统全部无缝先切换到slf4j
(1)去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
(2)去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j
(3)使用jul-to-slf4j,将jul的日志输出切换到slf4j
第二步:使slf4j选择logback来作为底层日志输出,加入以下jar包:
(1)slf4j-api
(2)logback-core
(3)logback-classic
###4.4.2###
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:
commons-logging
jdk-logging
改进:现在想统一将日志的输出交给log4j1
第一步:将上述日志系统全部无缝先切换到slf4j
(1)去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
(2)使用jul-to-slf4j,将jul的日志输出切换到slf4j
第二步:使slf4j选择log4j1来作为底层日志输出,加入以下jar包:
(1)slf4j-api
(2)log4j
(3)slf4j-log4j12(集成包)
###4.4.3###
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程
commons-logging
log4j
改进:现在想统一将日志的输出交给jdk-logging
第一步:将上述日志系统全部无缝先切换到slf4j
(1)去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
(2)去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j
第二步:使slf4j选择jdk-logging来作为底层日志输出,加入以下jar包:
(1)slf4j-api
(2)slf4j-jdk14(集成包)
#5 冲突说明# ##5.1 jcl-over-slf4j 与 slf4j-jcl 冲突##
jcl-over-slf4j: commons-logging切换到slf4j
slf4j-jcl : slf4j切换到commons-logging
如果这两者共存的话,必然造成相互委托,造成内存溢出。
##5.2 log4j-over-slf4j 与 slf4j-log4j12 冲突##
log4j-over-slf4j : log4j1切换到slf4j
slf4j-log4j12 : slf4j切换到log4j1
如果这两者共存的话,必然造成相互委托,造成内存溢出。但是log4j-over-slf4内部做了一个判断,可以防止造成内存溢出:即判断slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,如果存在则表示冲突了,抛出异常提示用户要去掉对应的jar包,代码在slf4j-log4j12 jar包的org.apache.log4j.Log4jLoggerFactory。
##5.3 jul-to-slf4j 与 slf4j-jdk14 冲突##
jul-to-slf4j : jdk-logging切换到slf4j
slf4j-jdk14 : slf4j切换到jdk-logging
如果这两者共存的话,必然造成相互委托,造成内存溢出。
参考文档链接:https://pan.baidu.com/s/1riGTe9e1kY6qSXsrGfkOAQ 提取码:h2l8
java日志学习(持续更新)的更多相关文章
- Java 日志学习
Java 日志学习,主要学习log4j,(为了查找方便,直接拷贝别人文章:原文:http://www.cnblogs.com/xt0810/p/3659045.html) [结构] java日志对调试 ...
- MongoDB学习——持续更新
參考MongoDB权威指南,学习阶段.大家多多交流问题.持续更新本文 MongoDB的长处 MongoDB具有丰富的数据模型,是面向文档的数据库. easy扩展.能够在多台server之间切割数据. ...
- Java名词术语---持续更新
在看技术文档的过程中,经常会出现新的java缩写术语,很多时候都不知道它们是什么,在这里记下,持续更新. ——————————————————————————————————————————————— ...
- java web开发中遇到的问题及解决方案(个人学习日志,持续更新)
转:http://blog.csdn.net/ducexu/article/details/7529613 2012.05.02 星期三 1.问题:导入的新工程,名字上出现感叹号. 原因:工程的j ...
- 2019春招面试高频题(Java版),持续更新(答案来自互联网)
第一模块--并发与多线程 Java多线程方法: 实现Runnable接口, 继承thread类, 使用线程池 操作系统层面的进程与线程(对JAVA多线程和高并发有了解吗?) 计算机资源=存储资源+计算 ...
- java日志学习笔记
一.日志家族 Log4j一开始就很强大,在jdk自带日志系统之前,apache就曾经尝试把log4j划为java的一部分,不知为何没能成功,sun还是用了自己很弱的日志系统.为了兼容各个日志系统,ap ...
- Java笔记(持续更新中)
Java语言的特点: 面向对象(封装,继承,多态) 平台无关性(JVM运行.class文件) 语言(泛型,Lambda) 类库(集合,并发,网络,IO/NIO) JRE(Java运行环境,JVM,类 ...
- 常见算法合集[java源码+持续更新中...]
一.引子 本文搜集从各种资源上搜集高频面试算法,慢慢填充...每个算法都亲测可运行,原理有注释.Talk is cheap,show me the code! 走你~ 二.常见算法 2.1 判断单向链 ...
- Java基础——集合(持续更新中)
集合框架 Java.util.Collection Collection接口中的共性功能 1,添加 booblean add(Object obj); 往该集合中添加元素,一次添加一个 boolea ...
随机推荐
- NEFU 118 n!后面有多少个0【数论】
http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=118 求n!后面有多少个0(1<=n<=1000000000) ...
- nodeJs学习-15 mysql中间件下载与使用、基本用法
下载mysql中间件(客户端):cnpm install mysql 链接数据库.查询示例: const mysql=require('mysql'); //1.连接 //createConnecti ...
- BasicAuth memo
string authInfo = userName + ":" + userPassword; authInfo = Convert.ToBase64String(Encodin ...
- UVa 495【大数加法】
UVa 495 求第n位斐波那契数列,n<=5000. 还是大数问题,这次是大数加法.仿照UVa 623的解法来做.623位数可以一位一位的增,但是这个需要预先给够位数,要是按六位存一个数组元素 ...
- C函数和宏中的可变参数
一:调用惯例 函数的调用方和被调用方对函数如何调用应该有统一的理解,否则函数就无法正确调用.比如foo(int n, int m),调用方如果认为压栈顺序是m,n,而foo认为压栈顺序是n, m,那么 ...
- @雅礼集训01/10 - T1@ matrix
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个矩阵.求它的所有子矩阵中本质不同的行的个数之和. inp ...
- 项目中容易出现的BUG预警
之前没有记录BUG的习惯导致在同一个坑里边栽了好几次,于是将最近几个项目中遇到的问题整理一下,在进行新项目时预警一遍: 使用携带有搜索功能的分页查询时,注意当用户更改了查询条件但没有点击查询按钮直接点 ...
- 重置Mysql自增列的开始序号
ALTER TABLE TableName AUTO_INCREMENT = 5; 代表重新从5开始(包括5)
- H3C V.35接口线缆
- 深入Java线程管理(三):线程同步
一. 引入同步: 有一个很经典的案例,即银行取款问题.我们可以先看下银行取款的基本流程: 1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. 2)用户输入取款金额. 3)系统判断账户金额是否大 ...