当前位置: 首页 > news >正文

日志框架简介-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

作者:京东零售 张洪

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

相关文章:

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

前言 随着互联网和大数据的迅猛发展&#xff0c;分布式日志系统和日志分析系统已广泛应用&#xff0c;几乎所有应用程序都使用各种日志框架记录程序运行信息。因此&#xff0c;作为工程师&#xff0c;了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影…...

c 语言, 随机数,一个不像随机数的随机数

c 语言&#xff0c; 随机数&#xff0c;一个不像随机数的随机数 使用两种方式获取随机数&#xff0c;总感觉使用比例的那个不太像随机数。 方法一&#xff1a; rand() 获取一个随机数&#xff0c;计算这个随机数跟最大可能值 RAND_MAX&#xff08;定义在 stdlib.h 中&#xf…...

Git三种方法从远程仓库拉取指定分支

克隆指定分支 git clone -b dev开发分支 https://github.com/521/springboot-rabbitmq.git切换到远程分支 git checkout -b dev开发分支 origin/dev开发分支参考 Git三种方法从远程仓库拉取指定的某一个分支...

7.6分割回文串(LC131-M)

算法&#xff1a; 有很多分割结果&#xff0c;按照for循环去做肯定做不来 这个时候就要想到回溯&#xff01;那就要画树&#xff01; 画树 分割的画树过程其实和组合很像。 例如对于字符串aab&#xff1a; 组合问题&#xff1a;选取一个a之后&#xff0c;在ab中再去选取第…...

stata回归结果输出中,R方和F值到底是用来干嘛的?

先直接回答问题&#xff0c;R方表示可决系数&#xff0c;反映模型的拟合优度&#xff0c;也就是模型的解释能力如何&#xff0c;也可以理解为模型中的各个解释变量联合起来能够在多大程度上解释被解释变量&#xff1b;F值用于模型整体的统计显著性&#xff0c;对应的P值越小&am…...

Windows搭建RTMP视频流服务(Nginx服务器版)

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

IP地址SSL证书

IP地址SSL证书是一种专门针对公网IP地址颁发的数字证书。与常规的域名SSL证书类似&#xff0c;其主要目标是提供数据加密和身份验证。以下几点概述了IP地址SSL证书的重要特性及其申请过程&#xff1a; 1. 保护直接IP访问&#xff1a; 当用户直接通过IP地址访问服务时&#xff…...

关于“Python”的核心知识点整理大全49

目录 16.2.10 加亮颜色主题 16.3 小结 第&#xff11;7 章 使用API 17.1 使用 Web API 17.1.1 Git 和 GitHub 17.1.2 使用 API 调用请求数据 17.1.3 安装 requests 17.1.4 处理 API 响应 python_repos.py 注意 17.1.5 处理响应字典 python_repos.py import json i…...

爬虫学习(1)--requests模块的使用

前言 什么是爬虫 爬虫是一种自动化工具&#xff0c;用于从互联网或其他计算机网络上获取数据。它可以模拟人的行为&#xff0c;自动访问网页&#xff0c;提取感兴趣的数据&#xff0c;并将其存储到本地计算机或数据库中。爬虫通常用于搜索引擎、数据分析、信息聚合等领域&…...

【Vue2 + ElementUI】el-table中校验表单

一. 案例 校验金额 阐述&#xff1a;校验输入的金额是否正确。如下所示&#xff0c;点击【编辑图标】会变为input输入框当&#xff0c;输入金额。当输入框失去焦点时&#xff0c;若正确则调用接口更新金额且变为不可输入状态&#xff0c;否则返回不合法金额提示 <templat…...

PgSQL技术内幕 - ereport ERROR跳转机制

PgSQL技术内幕 - ereport ERROR跳转机制 使用客户端执行SQL的时候经常遇到报ERROR错误&#xff0c;然后SQL语句就退出了。当然&#xff0c;事务也会回滚掉。本文我们看下它是如何做到退出SQL语句并回滚事务的。 1、以insert一个numeric类型值为例 表一个字段为numeric(10,2)类型…...

【验证概括 SV的数据类型_2023.12.18】

验证概括 验证的过程是保证芯片实现符合规格说明书&#xff08;Specification&#xff0c;spec&#xff09;的过程 验证的两项任务&#xff1a; RTL sim&#xff1a;前仿真&#xff0c;验证功能 GLS-Gate (Level Simulation)&#xff1a;后仿真&#xff0c;验证功能和时序 验…...

如何在无公网IP环境下远程访问Serv-U FTP服务器共享文件

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…...

电子工程师如何接私活赚外快?

对电子工程师来说&#xff0c;利用业余时间接私活是个很常见的技术&#xff0c;不仅可以赚取额外收入&#xff0c;也能提升巩固技术&#xff0c;可以说国内十个工程师&#xff0c;必有五个在接私活养家糊口&#xff0c;如果第一次接私活&#xff0c;该如何做&#xff1f; 很多工…...

数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)

目录 1、概述 2、环境准备 3、读写分离实验 3.1、安装jdk 3.2、安装Mycat 3.3、配置Mycat 3.3.1、配置schema.xml ​​​​3.3.2、配置server.xml 3.4、修改主从机远程登陆权限 3.4.1、主机 3.4.2、从机 3.5、启动Mycat 3.6、登录Mycat 3.7、验证 1、概述 读写分…...

MidJourney笔记(9)-daily_theme-docs-describe

/daily_theme 切换 #daily-theme 频道更新的通知。 但我发现在对话框那里,是没有这个命令的: 但官网是有介绍,不知道是不是版本问题还是这个命令已经无效。 但后来,我发现这个命令是要在Midjourney服务对话框那里才有,在我们后面添加的Mid...

鸿蒙 - arkTs:网络请求封装和使用

1. module.json5文件配置网络请求 {"module": {"requestPermissions": [{"name": "ohos.permission.INTERNET"}]} } 2. 在pages同级创建一个文件夹&#xff0c;起名为api 3. api文件夹下创建index.ts文件&#xff0c;文件内容&…...

多功能演示工具ProVideoPlayer2 mac特色介绍

ProVideoPlayer2 mac是用于大多数任何生产的首选多功能演示工具。ProVideoPlayer 2是一种动态视频播放和处理媒体服务器&#xff0c;可将视频映射&#xff08;包括播放和实时视频输入&#xff09;实时控制到一个或多个输出。包括实时效果&#xff0c;调度&#xff0c;网络同步和…...

java设计模式学习之【责任链模式】

文章目录 引言责任链模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用日志示例代码地址 引言 在现实生活中&#xff0c;常常会遇到这样的场景&#xff1a;一个请求或命令需要经过多个层级的处理。例如&#xff0c;一个行政审批流程可能需要通过多个部门的审…...

docker 安装可视化工具 Protainer 以及 汉化

一、创建保存数据的卷 安装网址&#xff1a;Install Portainer BE with Docker on Linux - Portainer Documentation docker pull portainer/portainer二、根据portainer镜像创建容器 docker run -d -p 8000:8000 -p 9000:9000\ --name portainer --restartalways \ -v /var/r…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域&#xff0c;专业机构的核心价值不仅在于减轻债务数字&#xff0c;更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明&#xff0c;合法债务优化需同步实现三重平衡&#xff1a; 法律刚性&#xff08;债…...