日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

前言

随着互联网和大数据的迅猛发展,分布式日志系统和日志分析系统已广泛应用,几乎所有应用程序都使用各种日志框架记录程序运行信息。因此,作为工程师,了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影响,但没有日志的应用程序是不完整的,甚至可以说是有缺陷的。优秀的日志系统可以记录操作轨迹监控系统运行状态解决系统故障


Java 日志框架进化史

早期 Java 日志框架没有制定统一的标准,使得很多应用程序会同时使用多种日志框架。Java 日志框架的发展历程大致可分为以下几个阶段:

1.**Log4j:**Apache Log4j是一种基于Java的日志记录工具。该项目由Ceki Gülcü于1999年创建,并几乎成为了Java日志框架的实际标准。

2.**JUL:**Apache 希望将 Log4j 引入 jdk,不过被 sun 公司拒绝了。随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL(java.util.logging)。

3.**Commons Logging:**为了解耦日志接口与实现,Apache在2002年推出了JCL(Jakarta Commons Logging)。JCL定义了一套日志接口,具体的实现由Log4j或JUL完成。Commons Logging使用动态绑定来实现日志记录,编码时只需要使用它定义的接口即可,程序运行时会使用ClassLoader来查找和加载底层的日志库,因此可以灵活选择Log4j或JUL来实现日志功能。

4.**Slf4j&Logback:**Ceki Gülcü与Apache基金会在Commons-Logging标准上存在分歧。后来,Ceki Gülcü离开了Apache,并创建了Slf4j和Logback两个项目。Slf4j是一个日志门面,仅提供接口,可以支持Logback、JUL、log4j等日志实现。而Logback则提供了具体的实现。相比于log4j,Logback具有更快的执行速度和更完善的功能。

5.**Log4j 2:**为了保持在Java日志领域的地位,防止JCL和Log4j被Slf4j和Logback取代,Apache在2014年推出了Log4j 2。Log4j 2与log4j不兼容,经过大量深度优化,其性能得到显著提升。


日志框架介绍

在上文中已经提及,目前常用的日志框架有 Log4j,Log4j 2,Commons Logging,Slf4j,Logback,JUL。这些日志框架可以分为两种类型:门面日志和日志系统。

日志门面

**日志门面(Logging Facade)**是一种设计模式,用于在应用程序中实现日志记录的抽象层。它提供了一组统一的接口和方法,即相应的 API,而不提供具体的接口实现。日志门面在使用时,可以动态或者静态地指定具体的日志框架实现,解除了接口和实现的耦合,使用户可以灵活地选择日志的具体实现框架。

日志系统

**日志系统(Logging System)**是指用于记录和管理应用程序运行时产生的日志信息的软件工具或框架。与日志门面相对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的功能,如日志级别管理、日志格式化、日志输出目标设置等。常见的日志系统包括Log4j、Logback、Java Util Logging等。

通过使用日志门面,我们可以在应用程序中使用统一的API进行日志记录,而具体的日志实现可以根据需要选择和配置。这样,我们可以根据项目需求和团队喜好来灵活选择、切换和配置日志系统,而不会对应用程序代码造成太大影响。

避免环形依赖

Slf4j 的作者 Ceki Gülcü 当年因为觉得 Commons-Logging 的 API 设计的不好,性能也不够高,因而设计了 Slf4j。而他为了 Slf4j 能够兼容各种类型的日志系统实现,还设计了相当多的 adapter 和 bridge 来连接,如下图所示:

鉴于此,在引入日志框架依赖的时候要尽力避免,比如以下组合就不能同时出现:

•jcl-over-slf4j 和 slf4j-jcl

•log4j-over-slf4j 和 slf4j-log4j12

•jul-to-slf4j 和 slf4j-jdk14

日志框架的使用选择

常用的组合使用方式是 Slf4j & Logback 组合使用,Commons Logging & Log4j 组合使用。

推荐

Slf4j & Logback

原因

1. Slf4j 实现机制决定 Slf4j 限制较少,使用范围更广。相较于 Commons-Logging,Slf4j 在编译期间便静态绑定本地的 Log 库,其通用性要好得多;

2. Logback 拥有更好的性能。Logback 声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高,这个操作在 Logback 中只需 3 ns,而在 Log4j 则需要 30 ns;

3. Slf4j 支持参数化,使用占位符号,代码更为简洁,如下例子:

// 在使用 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());

4. Logback 的所有文档是免费提供的,Log4j 只提供部分免费文档而需要用户去购买付费文档;

5. MDC (Mapped Diagnostic Contexts) 用 Filter,将当前用户名等业务信息放入 MDC 中,在日志 format 定义中即可使用该变量。具体而言,在诊断问题时,通常需要打出日志。如果使用 Log4j,则只能降低日志级别,但是这样会打出大量的日志,影响应用性能;如果使用 Logback,保持原定日志级别而过滤某种特殊情况,如 Alice 这个用户登录,日志将打在 DEBUG 级别而其它用户可以继续打在 WARN 级别。实现这个功能只需加 4 行 XML 配置;

6. 自动压缩日志。RollingFileAppender 在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩过程是异步的,因此在压缩过程中应用几乎不会受影响。


Slf4j+Logback入门实践

maven依赖

pom.xml

<!--日志框架接口-->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId>
</dependency>
<!--日志框架接口实现-->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId>
</dependency>
<!--日志框架核心组件-->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId>
</dependency><!--自动化注解工具-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version>
</dependency>

配置文件

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!--默认日志配置--><include resource="org/springframework/boot/logging/logback/defaults.xml"/><!-- 控制台日志 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder charset="UTF-8"><pattern>${CONSOLE_LOG_PATTERN}</pattern></encoder></appender><!-- Info日志 --><appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_FILE}-info.log</file><append>true</append><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>NEUTRAL</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_FILE}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 --><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 --></timeBasedFileNamingAndTriggeringPolicy><maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 --><totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 --><cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 --></rollingPolicy><encoder charset="UTF-8"><pattern>${FILE_LOG_PATTERN}</pattern></encoder></appender><!-- Warn日志 --><appender name="FILE-WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_FILE}-warn.log</file><append>true</append><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>WARN</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_FILE}-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件的路径和名称 --><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>200MB</maxFileSize> <!-- 单个日志文件的最大大小 --></timeBasedFileNamingAndTriggeringPolicy><maxHistory>15</maxHistory> <!-- 保留的历史日志文件数量 --><totalSizeCap>2GB</totalSizeCap> <!-- 所有日志文件的总大小上限 --><cleanHistoryOnStart>true</cleanHistoryOnStart> <!-- 在启动时清除历史日志文件 --></rollingPolicy><encoder charset="UTF-8"><pattern>${FILE_LOG_PATTERN}</pattern></encoder></appender><!-- Error日志 --><appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${LOG_PATH}/${LOG_FILE}-error.log</file><append>true</append><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_FILE}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>200MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><maxHistory>15</maxHistory><totalSizeCap>2GB</totalSizeCap><cleanHistoryOnStart>true</cleanHistoryOnStart></rollingPolicy><encoder charset="UTF-8"><pattern>${FILE_LOG_PATTERN}</pattern></encoder></appender><!-- 异步输出 --><appender name="info-asyn" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="FILE-INFO"/><queueSize>512</queueSize> <!-- 异步队列的大小 --></appender><appender name="warn-asyn" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="FILE-WARN"/><queueSize>512</queueSize> <!-- 异步队列的大小 --></appender><appender name="error-asyn" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="FILE-ERROR"/><queueSize>512</queueSize></appender><!-- 应用日志 --><logger name="com.improve.fuqige.bronze" additivity="false"><appender-ref ref="CONSOLE"/><appender-ref ref="FILE-INFO"/><appender-ref ref="FILE-WARN"/><appender-ref ref="FILE-ERROR"/></logger><!-- 总日志出口 --><root level="${logging.level.root}"><appender-ref ref="CONSOLE"/><appender-ref ref="info-asyn"/><appender-ref ref="warn-asyn"/><appender-ref ref="error-asyn"/></root>
</configuration>

applicantion.properties

logging.file=fuqige-bronze
logging.path=XXXXXX/Logs/XXXXXX
logging.level.root=info
logging.level.com.improve.fuqige.bronze=info
logging.pattern.console=%cyan(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow([%thread]) %highlight(%-5level) %boldGreen(%logger{80}[LineNumber:%L]): %highlight(%msg%n)
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] %-5level --- [%thread] %logger{80}[LineNumber:%L]: %msg%n

测试用例

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {@GetMapping("/hello")public String hello() {log.info("进来了!");log.warn("进来了!");log.error("进来了!");return "hello, world! requestId=" + MDC.get("requestId");}
}

参考资料

Java 日志框架: https://zhuanlan.zhihu.com/p/365154773

SLF4J框架常见的用法和最佳实践: https://juejin.cn/post/7215569601161166906

作者:京东零售 张洪

来源:京东云开发者社区 转载请注明来源

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

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

相关文章

OpenEular23.09(欧拉)操作系统为企业搭建独立的K8S集群环境,详细流程+截图

1.环境&#xff1b; win10&#xff0c;vmware16 pro&#xff0c;openeular23.09 集群模式&#xff1a;一主二从 主机硬件配置 主机名IP角色CPU内存硬盘k8s-master01192.168.91.100master4C4G40Gk8s-worker02192.168.91.101worker(node)4C4G40Gk8s-worker03192.168.91.102work…

12月27日,每日信息差

以下是2023年12月27日的8条信息差 第一、小米公司&#xff1a;小米汽车正式加入小米“人车家全生态”&#xff0c;随着小米汽车的即将发布&#xff0c;小米“人车家全生态”也实现了真正闭环 第二、吉利将于2024年初发射11颗卫星&#xff0c;吉利银河E8率先搭载卫星通信技术。…

详解全志R128 RTOS安全方案功能

介绍 R128 下安全方案的功能。安全完整的方案基于标准方案扩展&#xff0c;覆盖硬件安全、硬件加解密引擎、安全启动、安全系统、安全存储等方面。 配置文件相关 本文涉及到一些配置文件&#xff0c;在此进行说明。 env*.cfg配置文件路径&#xff1a; board/<chip>/&…

字符串转成时间的SQL,一个多种数据库通用的函数

select date 2010-10-06 from dual; date 函数&#xff0c;此函数适用于&#xff1a; 1.MySQL数据库 2.Oracle数据库 3.达梦数据库 4.人大金仓数据库

TensorFlow 的基本概念和使用场景。

TensorFlow是一个开源的机器学习框架&#xff0c;由Google开发并于2015年发布。它旨在提供一个灵活的、高效的框架来构建和部署各种机器学习和深度学习模型。 TensorFlow的核心概念是使用数据流图来表示计算任务。数据流图是由节点&#xff08;操作&#xff09;和边&#xff0…

linux用户态与内核态通过字符设备交互

linux用户态与内核态通过字符设备交互 简述 Linux设备分为三类&#xff0c;字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取&#xff0c;常见外设基本都是字符设备。块设备一般用于存储设备&#xff0c;一块一块的读取。网络设备&#xff0c;Linux将对网络…

JAVA进化史: JDK7特性及说明

JDK 7&#xff08;Java Development Kit 7&#xff09;是Java平台的一个重要版本&#xff0c;于2011年7月发布。这个版本引入了一系列的语言、库和虚拟机的改进&#xff0c;提升了Java的开发体验和性能。以下是JDK 7的一些主要特性&#xff0c;以及带有示例说明 字符串在switc…

数的分解(100%用例)C卷 (JavaPythonNode.jsC++)

给定一个正整数n,如果能够分解为m(m >1)个连续正整数之和,请输出所有分解中,m最小的分解。 如果给定整数无法分解为连续正整数,则输出字符串"N" 输入描述 输入数据为一整数,范围为 (1,2^30] 输出描述 比如输入为: 21 输出: 21=10+11 示例1 输入输出示例…

20231228在Firefly的AIO-3399J开发板的Android11使用Firefly的DTS配置单前后摄像头ov13850

20231228在Firefly的AIO-3399J开发板的Android11使用Firefly的DTS配置单前后摄像头ov13850 2023/12/28 19:20 缘起&#xff0c;突然发现只能打开前置的ov13850&#xff0c;或者后置的ov13850。 但是不能切换&#xff01; 【SDK&#xff1a;rk3399-android-11-r20211216.tar.xz】…

c++学习:运算符重载编写字符串类实战

目录 先定义一个类 定义构造和析构函数 用out<< 用s1.clear();清空数组 用s1.size();返回字符个数 加入扩容数组函数 用s2.append("world");和s1 "nihao";追加数组数据 用if(s1 s2)比较两个对象的数组 用if(s1 "123456")比较对…

Windows搭建RTSP视频流服务(EasyDarWin服务器版)

文章目录 引言1、安装FFmpeg2、安装EasyDarWin3、实现本地\虚拟摄像头推流服务4、使用VLC或PotPlayer可视化播放器播放视频5、RTSP / RTMP系列文章 引言 RTSP和RTMP视频流的区别 RTSP &#xff08;Real-Time Streaming Protocol&#xff09;实时流媒体协议。 RTSP定义流格式&am…

[BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务

1.问题描述 使用yarn调度任务时&#xff0c;在CapacityScheduler页面上单击叶队列&#xff08;或子队列&#xff09;时&#xff0c;不会显示应用程序任务信息&#xff0c;root队列可以显示任务。此外&#xff0c;FairScheduler页面是正常的。 No matching records found2.原…

字符串序列判定(100%用例)C卷 (JavaPythonNode.jsC语言C++)

输入两个字符串 S 和 L ,都只包含英文小写字母。 S 长度 <=100 , L 长度 <=500,000 。判定 S 是否是 L 的有效字串。 判定规则: S 中的每个字符在 L 中都能找到(可以不连续),且 S 在L中字符的前后顺序与 S 中顺序要保持一致。(例如, S="ace" 是 L=&q…

Unity之地形的构建

PS&#xff1a;公司没活干&#xff0c;好无聊偷偷摸鱼学Unity&#xff0c;害怕自己学完之后忘记&#xff0c;写下这一篇博客 先来看一下效果图&#xff1a;有山有水有树有草地 创建一个新的Unity3D项目 这里要用到Unity官方的免费资源包&#xff08;现在好像已经下架了百度网盘…

elementui+vue2 input输入框限制只能输入数字

方法1 自定义表单校验 <el-form :model"Formdata" ref"formRef" :rules"nodeFormRules" label-width"100px"><el-form-itemlabel"年龄"prop"age"><el-input v-model.number"Formdata.age&q…

excel 函数技巧

1&#xff1a;模糊查询 LOOKUP(1,0/FIND(F1062,Sheet1!C$2:Sheet1!C$9135),Sheet1!B$2:Sheet1!B$9135) 函数含义&#xff1a;寻找F列1062行和sheet1中的C2行到C9135行进行模糊查询&#xff0c;返回该行对应的B2行到B9135行的结果。未查到返回结果0 函数公式&#xff1a; LO…

SQL高级:递归查询

如果在单表或两表中存储了树形结构数据,那么在查询这些数据时,就有可能要用到递归查询。 在实际的业务场景中,树形结构的数据很常见。比如组织架构、产品材料清单、产品大类和小类等等。 递归查询也是一个很有趣的知识点。我们来学习一下它。 辅助表 为了学习这个知识点…

thinkphp命令执行漏洞(CVE-2018-1002015)

漏洞描述&#xff1a; ThinkPHP 5.0.x版本和5.1.x版本中存在远程代码执行漏洞&#xff0c;该漏洞源于ThinkPHP在获取控制器名时未对用户提交的参数进行严格的过滤。远程攻击者可通过输入‘&#xff3c;’字符的方式调用任意方法利用该漏洞执行代码。 复现过程&#xff1a; 1…

学习Java第80天,基于IDEA 进行Maven依赖管理

1. 依赖管理概念 Maven 依赖管理是 Maven 软件中最重要的功能之一。Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题&#xff0c;使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中&#xff0c;避免出现版本冲突和依赖缺失等…

Excel formulas 使用总结(更新中)

最近在写task assigment的时候学习到的&#xff0c;记录下。 首先它所有需要写赋值formuls都要用 开头 相等赋值 a1 这个就代表这格的数据和a1是一样的。如果希望其他格和它相同的逻辑&#xff0c;可以直接复制该cell或者直接拖动该cell右下角&#xff0c;他会自动进行匹配…