探索java世界中的日志奥秘

                                  java日志简单介绍

 

 

 

对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。JAVA领域存在多种日志框架,目前常用的日志框架包括Log4jLog4j 2Commons LoggingSlf4jLogbackJul

 

 

一、java日志发展史

 

二、java 常用日志框架类别介绍

 

三、门面、实现、桥接

 

四、commons logging vs Slf4j

 

五、 log4j vs logback

六、 slf4j 源码解读

 

七、logback

 

八、MDC

 

 

 

 

 

 

 

 

 

 

 

 

 

一、java日志发展史

 

1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。

 

期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4jjava的标准库中,但Sun拒绝了。

2002Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。

接着,Apache推出了Jakarta Commons LoggingJCL只是定义了一套日志接口(其内部也提供一个Simple Log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用Commons Logging的接口,底层实现可以是log4j,也可以是Java Util Logging

后来(2006)Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于Commons Logging)Logback(Slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述Logback的:The GenericReliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)

现今,Java日志领域被划分为两大阵营:Commons Logging阵营和SLF4J阵营。
Commons LoggingApache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub30000个项目,统计出了最流行的100Libraries,可以看出slf4j的发展趋势更好:

 

 

Apache眼看有被Logback反超的势头,于2012-07重写了log4j 1.x,成立了新的项目Log4j 2Log4j 2具有logback的所有特性。

 

 

二、java常用日志框架类别介绍

 

         • Log4j Apache Log4j是一个基于Java的日志记录工具。现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。 Log4j应该说是Java领域资格最老,应用最广的日志工具。从诞生之日到现在一直广受业界欢迎。Log4j是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX系统日志等。

Log4j 2 Apache Log4j 2apache开发的一款Log4j的升级产品。

 

Logback 一套日志组件的实现(slf4j阵营)

        Jul (Java Util Logging),Java1.4以来的官方日志实现。JDK1.4开始,通过java.util.logging提供日志功能。它能满足基本的日志需要,但是功能没有Log4j强大,而且使用范围也没有Log4j广泛。

 

Commons Logging Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging

Slf4j 类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。

 

                   这个些所有的日志们都要归功于一个人       Ceki Gülcü !!!

 

 

 

 

 

 

 

三、门面、实现、桥接

 

经历了上述的发展,现在使用日志框架时往往会涉及三个层面的东西。 

                

 

· 门面

· Slf4j: The simple logging facade for java.

·    JCL: Jakarta Commons Logging.

· 实现类

· log4j-1.2

· log4j-2.x

· logback

· jul: java.util.logging

·

· 桥接包

· SLF4J LOG4J 12 Binding

· JUL To SLF4J Bridge

· JCL 1.1.1 Implemented Over SLF4J ??

· SLF4J JDK14 Binding

· Apache Log4j Commons Logging Bridge

·

 

门面主要只负责定义接口,实现类才负责具体的编码工作。

 

为什么要定义门面呢? 依赖接口而不依赖实现

 

桥接包顾名思义就是桥接门面和实现类。比如SLF4J LOG4J 12 Binding这个桥接包可以使Slf4j和log4j1.2结合起来正常工作。一个已经成型的系统如果使用了这个模式,底层又想将log4j1.2换成log4j2.0实现,则只需要替换实现包为log4j2.x以及桥接包为 Log4j 2 SLF4J Binding。

 

 

                    

 

 

 

 

 

 

 

 

 

 

 

四、commons logging vs Slf4j

 

我们先看一下java日志框架之间的关系

 

Commons LoggingSlf4j是日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)log4jLogback则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用这只需要关注接口而无需关注具体的实现,做到解耦。

比较常用的组合使用方式是Slf4jLogback组合使用,Commons LoggingLog4j组合使用。

Logback必须配合Slf4j使用。由于LogbackSlf4j是同一个作者,其兼容性不言而喻。(https://stackoverflow.com/questions/10117788/how-to-setup-commons-logging-to-use-logback)

 

Commons logging实现机制

Commons logging是通过动态查找机制,在程序运行时,使用自己的ClassLoader寻找和载入本地具体的实现。详细策略可以查看commons-logging-*.jar包中的org.apache.commons.logging.impl.LogFactoryImpl.java文件。

 

Slf4j实现机制

Slf4j在编译期间,静态绑定本地的LOG库。它是通过查找类路径下org.slf4j.impl.StaticLoggerBinder,然后绑定工作都在这类里面进行。

 

 

静态绑定 & 动态绑定

静态绑定又称编译时绑定,动态绑定又称运行时绑定。

JCL作为第一个log接口框架,使用了基于反射的动态绑定的方法,原理很简单,预先定义好支持的log实现的工厂类的全路径到一个数组中,遍历这个数组,调用Class.forName依次尝试寻找各个log实现,如果当前class loader没找到,就去父class loader去找,直到找到任意一个实现为止。

这种方法有致命的缺陷,这也正是SLF4J诞生的原因。Java EE的web容器,为了实现servlet规范中同一个容器中不同web app之间、web app和web容器之间的隔离,都使用的自己实现的class loader,逻辑和标准的class loader不同,导致一系列的无法正常发现log实现库的问题。

Taxonomy of class loader problems encountered when using Jakarta Commons Logging

这篇文章做了非常详尽的分析解释,文章的作者正是log4j和SLF4J的作者Ceki Gülcü,有兴趣的同学可以阅读。

 

另外一个小改进:

JCL 输出一个 debug 级别的 log:

logger.debug("start process request, url:" + url);

这个有什么问题呢?一般生产环境 log 级别都会设到 info 或者以上,那这条 log 是不会被输出的。然而不管会不会输出,这其中都会做一个字符串连接操作,然后生产一个新的字符串。如果这条语句在循环或者被调用很多次的函数中,就会多做很多无用的字符串连接,影响性能。

所以 JCL 的最佳实践推荐这么写:

if (logger.isDebugEnabled()) {

    logger.debug("start process request, url:" + url);

}

然而开发者常常忽略这个问题或是觉得麻烦而不愿意这么写。

 

 

所以SLF4J提供了新的API,方便开发者使用:

logger.debug("start process request, url:{}", url);

这样的话,在不输出 log 的时候避免了字符串拼接的开销;在输出的时候需要做一个字符串format,代价比手工拼接字符串大一些,但是可以接受。

 

 

五、 log4j vs logback

logback算是log4j的升级版本 ,基本实现了所有log4j的功能。

logback比log4j有更多的优点

更快的实现 

Logback的内核重写了,在一些关键执行路径上性能提升10倍以上。而且logback不仅性能提升了,初始化内存加载也更小了。 

 

非常充分的测试 

Logback经过了几年,数不清小时的测试。Logback的测试完全不同级别的。在作者的观点,这是简单重要的原因选择logback而不是log4j。 

 

Logback-classic非常自然实现了SLF4j 

Logback-classic实现了SLF4j。在使用SLF4j中,你都感觉不到logback-classic。而且因为logback-classic非常自然地实现了SLF4J,所以切换到log4j或者其他,非常容易,只需要提供成另一个jar包就OK,根本不需要去动那些通过SLF4JAPI实现的代码。 

 

非常充分的文档 

Logback文档免费。Logback的所有文档是全面免费提供的,不象Log4J那样只提供部分免费文档而需要用户去购买付费文档 

 

 

 

Filters(过滤器) 

有些时候,需要诊断一个问题,需要打出日志。在log4j,只有降低日志级别,不过这样会打出大量的日志,会影响应用性能。在Logback,你可以继续保持那个日志级别而除掉某种特殊情况,如alice这个用户登录,她的日志将打在DEBUG级别而其他用户可以继续打在WARN级别。要实现这个功能只需加4行XML配置。

 

SiftingAppender(一个非常多功能的Appender) 

它可以用来分割日志文件根据任何一个给定的运行参数。如,SiftingAppender能够区别日志事件跟进用户的Session,然后每个用户会有一个日志文件。 

 

自动压缩已经打出来的log 

RollingFileAppender在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩是个异步过程,所以甚至对于大的日志文件,在压缩过程中应用不会受任何影响。 

 

堆栈树带有包版本 

Logback在打出堆栈树日志时,会带上包的数据。 

 

自动去除旧的日志文件 

通过设置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory属性,你可以控制已经产生日志文件的最大数量。如果设置maxHistory为12,那那些log文件超过12个月的都会被自动移除。 

 

 

六、 slf4j源码解读

 

我们写代码的时候是怎么打日志的呢?

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

LoggerFactory.getLogger(getClass());

 

这里看不到任何跟实现类有关联的代码,然而我们已经可以使用get到的logger打日志了。那么slf4j到底是怎么找到实现类的了?

 

根据Path = “org/slf4j/impl/StaticLoggerBinder.class”去加载相应的实现类

 

 

为什么要获取类的名字,而根据名字来获取对象呢?

因为每个类使用的日志处理实现可能不同,iLoggerFactory中也是根据名字来判断一个类的实现方式的。

 

 

 

 

 

 

那么这里会有一个问题,如果找到多个实现类,最终会绑定哪一个呢?

 

The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. As of version 1.6.6, SLF4J will name the framework/implementation class it is actually bound to.

Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J’s purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways.

如果发现有多个实现类,那么slf4j会打印出warning信息。但是仅仅是warning而已。即使有多个实现类,slf4j也只会挑选其中一个,这个选择取决于JVM和所有其他实际因素,基本算是随机性的。同时,slf4j建议删除多余的实现类,仅仅保留一个。

 

 

 

 

 

七、logback

 

 

1StaticLoggerBinder 初始化并创建logFactory ()

 

 

 

StaticLoggerBinder.init();

 

初始化 new ContextInitializer(defaultLoggerContext).autoConfig();

 

getLoggerFactory()

 

 

总结一下这个过程: 

1StaticLoggerBinder在加载的时候,会去读取配置文件,并根据配置文件对LoggerContext进行初始化 

2、然后初始化ContextSelectorStaticBinder,在这个类内部new一个DefaultContextSelector,并把第一步中配置完毕的LoggerContext传给DefaultContextSelector 

3、调用getLoggerFactory()方法,直接返回第一步中配置的LoggerContext,或者委托DefaultContextSelector类返回LoggerContext

 

 

2loggerContext工厂类产出logger对象 

 

 

 

Logger getLogger(final String name);

 

com.darcytech.controller.LoginController

 

Logger[com]、Logger[com.darcytec]、Logger[com.darcytech.controller]、Logger[com.darcytech.controller.LoginController] 

 

 

总结一下创建Logger的完整流程: 

1、如果请求ROOT logger,则直接返回root 

2、如果请求的Logger已经存在,则直接返回 

3、如果请求的Logger尚未创建,则从ROOT开始,级联创建所有Logger 

4、每创建一个Logger,都要设置父子关系,继承生效级别 

5、每创建一个Logger,都将其放入loggerCache,并将size++ 

 

 

3、Logger

 

transient private AppenderAttachableImpl<ILoggingEvent> aai;

Logger是委托这个类实现AppenderAttachable接口,也是委托这个类来调用Appender组件来实际记录日志,所以这个字段是最关键的。

 

主要方法

getChildByName

setLevel

createChildByName每创建一个Logger,都要设置父子关系,继承生效级别

info

 

 

 

 

callAppenders

如果子Logger和父Logger都关联了同样的Appender,则日志信息会重复记录

 

 

 

总结一下Logger类中定义的字段和方法,是出于以下目的: 

 

1、定义parentchildList,用于实现父子Logger的树形结构 

2、定义createChildByName()getChildByName()方法,是供LoggerContext创建Logger 

3、定义leveleffectiveLevelInt,是为了判定日志级别是否足够 

4、最后,filterAndLog()buildLoggingEventAndAppend()callAppenders()appendLoopOnAppenders()方法,是Logger类的核心方法,一步步地委托AppenderAttachableImpl类来实际记录日志 

 

 

 

4Appender

 

 

实现类就是最常见的ConsoleAppender和FileAppender

 

doAppend()

 

最终writeOut()方法委托配置给它的Encoder组件来记录

 

 

 

5、简单了解一下RollingFileAppender,rollingPolicy

常用的RollingFileAppender, TimeBasedRollingPolicy

 

 

 

 

 

 

 

 

八、什么是MDC

 

 

 

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对,当需要记录日志时,只需要从 MDC 中获取所需的信息即可。

 

 

 

此外,对于一些线程池使用的应用场景,可能我们在最后使用结束时,需要调用clear方法来清洗将要丢弃的数据。

 

LogbackMDCAdapter

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/wazqy/p/8315757.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ细说之开篇

前言关于消息中间件的应用场景&#xff0c;小伙伴们应该都耳熟能详了吧&#xff0c;比如经常提到的削峰填谷、分布式事务、异步业务处理、大数据分析等等&#xff0c;分布式消息队列成为其中比较关键的桥梁&#xff0c;也就意味着小伙伴们得掌握相关技能&#xff1b;当下相对比…

【Java】五种常见排序之-----------冒泡排序

冒泡排序&#xff1a; 原理: 将关键字较小的值不断地上浮&#xff0c;将关键字值较大的不断下沉&#xff1b;时间复杂度&#xff1a;O(n^2)空间复杂度&#xff1a;最优&#xff08;即已经排好序&#xff09;为0&#xff0c;平均空间复杂度为O(1);核心代码&#xff1a;for(int i…

混战的低代码江湖,如何区分「李逵」和「李鬼」?

作者&#xff1a;APICloud 创始人刘鑫 这两年&#xff0c;无论是资本层面&#xff0c;还是企业IT部门的关注&#xff0c;“低代码”都是绝对的热点。互联网圈也似在一夜之间冒出了各种各样的低代码公司。 到底什么是低代码&#xff1f;低代码是新技术么&#xff1f;低代码开发能…

关于捕获键盘信息的processDialogkey方法2--具体应用

自定义控件里的keydown方法无法捕获所有的按键消息的处理方法1&#xff08;自定义控件里的keydown方法无法获取的键值如上下左右键等&#xff09; 处理办法具体如下&#xff1a; 1、首先在自定义控件UserControl1中重写ProcessDialogKey方法 自定义控件UserControl1中重写Proce…

指针

指针 题目一&#xff1a; 计算两数的和与差 本题要求实现一个计算输入的两数的和与差的简单函数。 函数接口定义&#xff1a; void sum_diff( float op1, float op2, float psum, float pdiff ); 其中op1和op2是输入的两个实数&#xff0c;psum和pdiff是计算得出的和与差。 裁判…

【MapGIS精品教程】006:MapGIS根据经纬度计算各比例尺图幅编号

己知某点的经纬度或图幅西南图廓点的经纬度,计算该点所在图幅号。 例题一:某点的经度为11433′45″,纬度为3922′30″,计算所在1:250000图幅的编号。 文章目录 1. 公式计算编号的方法2. 软件计算编号的方法1. 公式计算编号的方法 求解过程: 第一步,利用下列公式计算其所…

分析完百年飞机空难数据,我发现了这几条“保命”小秘诀

来 源&#xff5c;Giao数据 数 据 | YaJie 文 章 | 张子豪,YaJie 本文爬取了飞机失事网1908-2020年空难相关数据&#xff0c;包括空难发生次数、机组和乘客的死亡人数与死亡率、不同季节的空难发生次数、空难相关文本的关键词、空难高发地、空难高发航空公司、空难高发机型以…

用 QuestPDF操作生成PDF更快更高效!

QuestPDFQuestPDF是一个开源的工具库&#xff0c;可以在.NET或者.Net Core中生成pdf文档它提供了一个布局引擎&#xff0c;设计时考虑到了完整的分页支持以及灵活性要求&#xff01;比市面上常见的Aspose和iTextSharp好用太多了&#xff01;GitHub地址安装Install-Package Ques…

ASP.NET vs MVC vs WebForms

许多ASP.NET开发人员开始接触MVC认为MVC与ASP.NET完全没有关系&#xff0c;是一个全新的Web开发&#xff0c;事实上ASP.NET是创建WEB应用的框架而MVC是能够用更好的方法来组织并管理代码的一种更高级架构体系&#xff0c;所以可以称之为ASP.NET MVC。 我们可将原来的ASP.NET称为…

在Blazor 中自定义权限验证

Blazor是什么Blazor 是微软在 .NET 里推出的一个 WEB 客户端 UI 交互的框架&#xff0c;使用 Blazor 你可以代替 JavaScript 来实现自己的页面交互逻辑&#xff0c;可以很大程度上进行 C# 代码的复用&#xff0c;Blazor 对于 .NET 开发人员来说是一个不错的选择。需求背景其实我…

【ArcGIS微课1000例】0035:地图面状符号设计教程

地图符号是表示地图内容的基本手段,它由形状不同、大小不一、色彩有别的图形和文字组成。 地图符号是地图的语言,是一种图形语言。它与文字语言相比较,最大的特点是形象直观,一目了然。 本文讲解ArcGIS中面状符号设计方法。 文章目录 一、新建符号样式二、面状符号设计1. 斜…

MySQL夺命15问,你能坚持到第几问?

前言 MySQL在面试中经常被问到&#xff0c;本文总结了面试中的经典问题。 1. 数据库三大范式是什么&#xff1f; 第一范式&#xff1a;每个列都不可以再拆分。 第二范式&#xff1a;在第一范式的基础上&#xff0c;非主键列完全依赖于主键&#xff0c;而不能是依赖于主键的一部…

ios元素定位

原文地址http://www.cnblogs.com/meitian/p/7373460.html 第一种&#xff1a;通过Appium1.6的Inspector来查看 具体安装方式前面的随笔已经介绍了&#xff1a;http://www.cnblogs.com/meitian/p/7360017.html可以通过定位找到元素xpath或name个人不推荐用这个方法&#xff0c;实…

使用 LSM-Tree 思想基于.NET 6.0 C# 写个 KV 数据库(案例版)

文章有点长&#xff0c;耐心看完应该可以懂实际原理到底是啥子。这是一个KV数据库的C#实现&#xff0c;目前用.NET 6.0实现的&#xff0c;目前算是属于雏形&#xff0c;骨架都已经完备&#xff0c;毕竟刚完工不到一星期。当然&#xff0c;这个其实也算是NoSQL的雏形&#xff0c…

35.使用拦截器实现权限验证

转自&#xff1a;https://wenku.baidu.com/view/84fa86ae360cba1aa911da02.html 为了说明此问题&#xff0c;我们建立struts2auth项目&#xff0c;流程图如下&#xff1a; 简短说明&#xff1a;当我们访问main.jsp页面&#xff0c;并试图通过此页面中的链接地址&#xff1a;not…

如何保证缓存和数据库的一致性?

1. 问题分析 2. Cache-Aside 2.1 读缓存 2.2 写缓存 2.3 延迟双删 2.4 如何确保原子性 3. Read-Through/Write-Through 3.1 Read-Through 3.2 Write-Through 4. Write Behind 很多小伙伴在面试的时候&#xff0c;应该都遇到过类似的问题&#xff0c;如何确保缓存和数据库…

Pressed状态和clickable,duplicateParentState的关系

做Android开发的人都用过Selector,可以方便的实现View在不同状态下的背景。不过&#xff0c;相信大部分开发者遇到过和我一样的问题&#xff0c;本文会从源码角度&#xff0c;解释这些问题。 首先&#xff0c;这里简单描述一下&#xff0c;我遇到的问题&#xff1a; 界面上有个…

【招聘(南京)】 慧咨环球南京研发中心 .NET和Blazor 前端

主要的亮点快速增长的、产品导向型的全球性科技公司设计和开发市场领先的软件解决方案WLB — 工作生活相平衡澳洲排名前五的软件公司混合办公 — 3天在家办公&#xff0c;2天在办公室办公在C#和.NET开发&#xff0c;企业级系统研发&#xff0c;软件工程方面有长期的优秀实践和技…

用Python+Django在Eclipse环境下开发web网站【转】

一、创建一个项目如果这是你第一次使用Django&#xff0c;那么你必须进行一些初始设置。也就是通过自动生成代码来建立一个Django项目--一个Django项目的设置集&#xff0c;包含了数据库配置、Django详细选项设置和应用 特性配置&#xff0c;具体操作步骤如下所示。 1.新建Djan…

[转]数据结构KMP算法配图详解(超详细)

KMP算法配图详解 前言 KMP算法是我们数据结构串中最难也是最重要的算法。难是因为KMP算法的代码很优美简洁干练&#xff0c;但里面包含着非常深的思维。真正理解代码的人可以说对KMP算法的了解已经相当深入了。而且这个算法的不少东西的确不容易讲懂&#xff0c;很多正规的书本…