手把手带你理解Spring日志原理

news2025/1/10 3:06:01

文章目录

  • 1 楔子
  • 2 jcl原理分析
    • 2.1 依赖坐标
    • 2.2 API调用
    • 2.3 源码分析
  • 3 slf4j原理分析
    • 3.1 依赖坐标
    • 3.2 API调用
    • 3.3 源码分析
  • 4 spring是如何选择日志技术的?
    • 4.1 场景一:通过log4j2打印日志
      • 4.1.1 引入maven依赖
      • 4.1.2 编写配置文件
      • 4.1.3 执行测试方法
      • 4.1.4 测试结果
    • 4.2 场景二:通过logback打印日志
      • 4.2.1 引入maven依赖
      • 4.2.2 编写配置文件
      • 4.2.3 执行测试方法
      • 4.2.4 测试结果
    • 4.3 情景三:log4j2与logback共存,但不将log4j2绑定到slf4j
      • 4.3.1 引入maven依赖
      • 4.3.2 编写配置文件
      • 4.3.3 执行测试方法
      • 4.3.4 测试结果
      • 4.3.5 源码分析
  • 5 整合日志
    • 5.1 引入maven依赖
    • 5.2 编写配置文件
    • 5.3 执行测试方法
    • 5.4 测试结果
  • 6 思考
    • 6.1 springboot的日志体系是怎么实现的呢?

✨这里是第七人格的博客✨小七,欢迎您的到来~✨

🍅系列专栏:【Spring源码解析】🍅

✈️本篇内容: Spring日志原理✈️

🍱本篇收录完整代码地址:https://gitee.com/diqirenge/spring-book/tree/master/spring-log🍱

1 楔子

阅读spring源码,难免会在源码上进行改动,如添加日志,增加注释等,为了可以更好的学习spring源码,理解spring日志原理是非常有必要的。

目前市面上常见的日志框架有jul、log4j、log4j2以及logback,常用的日志整合技术有jcl以及slf4j。

2 jcl原理分析

2.1 依赖坐标

<!-- jcl -->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

2.2 API调用

org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory.getLog("jcl");
logger.info("jcl");

2.3 源码分析

跟进LogFactory.getLog源码,我们会发现,代码会调用LogFactoryImpl的newInstance方法,而newInstance方法内部会调用discoverLogImplementation方法,

if (isDiagnosticsEnabled()) {
    logDiagnostic(
        "No user-specified Log implementation; performing discovery" +
        " using the standard supported logging implementations...");
}
for(int i=0; i<classesToDiscover.length && result == null; ++i) {
    result = createLogFromClass(classesToDiscover[i], logCategory, true);
}

分析discoverLogImplementation方法我们可以知道,JCL内置了一个字符串数组classesToDiscover,这个数组当中保存了类名。

private static final String[] classesToDiscover = {
        LOGGING_IMPL_LOG4J_LOGGER,
        "org.apache.commons.logging.impl.Jdk14Logger",
        "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
        "org.apache.commons.logging.impl.SimpleLog"
};

也就是说jcl在调用getLogger方法去实例化一个logger对象的时候,会遍历一个保存了日志类名的数组,如果能够通过class.forName加载到,那么就使用该日志技术。

jcl作为日志整合技术,可以根据用户提供的具体日志技术去实例化具体的对象,但是由于它已经不更新了,所以它的内置数组,目前最多只有4个。

注:spring5.x有个spring-jcl的模块,就是为了解决jcl不更新了的问题。

3 slf4j原理分析

slf4j的核心思想和jcl是一致的,只是它是通过将具体的日志技术和绑定器绑在一起来工作的,假设有新的日志出现可以发布新的绑定器来实现扩展。官网地址:https://www.slf4j.org/

3.1 依赖坐标

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>

3.2 API调用

org.slf4j.Logger slf4j = LoggerFactory.getLogger("slf4j");
slf4j.info("slf4j");

3.3 源码分析

private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

public static String REQUESTED_API_VERSION = "1.6.99";

private static final String loggerFactoryClassStr =

Log4jLoggerFactory.class.getName();

private final ILoggerFactory loggerFactory = new Log4jLoggerFactory();

slf4j绑定器解决了,新日志技术的拓展适配问题,但是它还是没有解决以前日志技术的历史编码问题。

为了解决历史编码问题,slf4j引入了桥接器的概念,顾名思义,就是将某个日志技术,桥接到slf4j,再由slf4j的绑定器决定具体使用哪一个日志技术。

接下来,我们通过以下几个场景来分析一下spring是如何选择自己想用的日志技术的。

4 spring是如何选择日志技术的?

4.1 场景一:通过log4j2打印日志

4.1.1 引入maven依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--   log4j2依赖-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>

        <!-- log4j2-slf4j-impl 绑定器 绑定log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
        </dependency>

    </dependencies>

4.1.2 编写配置文件

<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="WARN">
   <Appenders>
      <Console name="Console" target="SYSTEM_OUT">
         <PatternLayout pattern="log4j2 ===> %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
      </Console>
   </Appenders>
   <Loggers>
      <Logger name="org.springframework.beans.factory" level="DEBUG"/>
      <Logger name="com.sheep.log.Log4j2Log" level="DEBUG"/>
      <Root level="debug">
         <AppenderRef ref="Console"/>
      </Root>
   </Loggers>
</Configuration>

4.1.3 执行测试方法

@Slf4j
public class Log4j2Log {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext
                context = new AnnotationConfigApplicationContext();
        context.refresh();

        log.info("我是log4j2在项目中打印的日志");
    }
}

4.1.4 测试结果

image-20230417202454549

4.2 场景二:通过logback打印日志

4.2.1 引入maven依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--  logback依赖 start-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <!--  logback依赖 end-->

    </dependencies>

4.2.2 编写配置文件

<configuration>
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>logback===> %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35}[%line] - %msg%n</pattern>
         <charset>UTF-8</charset>
      </encoder>
   </appender>

   <logger name="e" level="debug" additivity="false">
      <appender-ref ref="STDOUT"/>
   </logger>

   <root level="debug">
      <appender-ref ref="STDOUT"/>
   </root>
</configuration>

4.2.3 执行测试方法

@Slf4j
public class LogbackLog {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext
                context = new AnnotationConfigApplicationContext();
        context.refresh();

        log.info("我是logback在项目中打印的日志");
    }
}

4.2.4 测试结果

image-20230417203101158

4.3 情景三:log4j2与logback共存,但不将log4j2绑定到slf4j

4.3.1 引入maven依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--   log4j2依赖 start-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>

        <!--  logback依赖 start-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <!--  logback依赖 end-->
    </dependencies>

4.3.2 编写配置文件

配置文件不变,同情景一和二

4.3.3 执行测试方法

我们执行LogbackLog类的main方法

4.3.4 测试结果

image-20230417204626754

4.3.5 源码分析

项目日志是通过logback打印的,而spring框架日志是通过log4j2打印的,也就是说spring没有采用我们所配置的logback。

通过查询源码可知,logger对象是通过LogFactory生成的

image-20230417205420343

而LogFactory是通过调用LogAdapter.createLog(name);方法获取到logger的

image-20230417205900511

createLog方法是一个简单的分支语句,通过logApi对象判断,所以我们接下来需要看看这个logApi是怎么初始化的,所幸我们在LogAdapter的静态块中找到了logApi的初始化代码

image-20230417210103469

通过以上代码我们可以知道,Spring是优先使用log4j2的日志技术的,除非用户强行要求使用slf4j技术( 如果有log4j2到slf4j的桥接器,并且还有slf4j,那么就用slf4j)。

5 整合日志

基于前文的分析,我们可以通过使用slf4j的桥接器和绑定器,以及具体日志技术,完成日志技术的统一。思路如下:

将所用到的日志技术桥接到 slf4j,再将slf4j与具体的日志技术绑定,这里我们绑定到logback。

5.1 引入maven依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--   log4j2依赖 start-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>

        <!--  logback依赖 start-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <!--  logback依赖 end-->

        <!--    log4j2桥接器-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.9.1</version>
        </dependency>

        <!-- jul桥接器-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.7.26</version>
        </dependency>
        
    </dependencies>

5.2 编写配置文件

同情景三

5.3 执行测试方法

@Slf4j
public class AllLogToBackLog {

    public static void main(String[] args) {
        Logger logger = Logger.getLogger(String.valueOf(JucLog.class));
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
        logger.info("我是juc在项目中打印的日志");


        Logger log4j2 = Logger.getLogger ( AllLogToBackLog.class.getName () );
        log4j2.info("我是log4j2在项目中打印的日志");

        AnnotationConfigApplicationContext
                context = new AnnotationConfigApplicationContext();
        context.refresh();

        log.info("我是logback在项目中打印的日志");
    }
}

5.4 测试结果

logback===>2023-04-17 23:36:07.221 [main] INFO  class com.sheep.log.JucLog[23] - 我是juc在项目中打印的日志
logback===>2023-04-17 23:36:07.223 [main] INFO  com.sheep.log.AllLogToBackLog[27] - 我是log4j2在项目中打印的日志
logback===>2023-04-17 23:36:07.282 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext[596] - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@246ae04d
logback===>2023-04-17 23:36:07.288 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory[225] - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
logback===>2023-04-17 23:36:07.297 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory[225] - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
logback===>2023-04-17 23:36:07.298 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory[225] - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
logback===>2023-04-17 23:36:07.299 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory[225] - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
logback===>2023-04-17 23:36:07.300 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory[225] - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
logback===>2023-04-17 23:36:07.318 [main] INFO  com.sheep.log.AllLogToBackLog[33] - 我是logback在项目中打印的日志

6 思考

6.1 springboot的日志体系是怎么实现的呢?

提示

image-20230417234601687

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

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

相关文章

陆奇博士最新演讲分享:我的大模型世界观(附PPT下载链接)

省时查报告-专业、及时、全面的行研报告库 省时查方案-专业、及时、全面的营销策划方案库 【免费下载】2023年3月份热门报告合集 【限时免费】ChatGPT4体验&#xff0c;无需翻墙直接用 ChatGPT调研报告&#xff08;仅供内部参考&#xff09; ChatGPT的发展历程、原理、技术架构…

使用WireShark抓包分析TCP_IP协议

文章目录 前言一、TCP/IP协议1.1 OSI分层1.2 TCP/IP 分层 二、抓包2.1 Socket代码2.2 过滤包 三、分析3.1 TCP首部3.2 实战分析3.3 三次握手3.4 四次挥手 参考 前言 TCP/IP 协议 是一组用于互联网通信的协议。它由两个主要协议组成&#xff1a;传输控制协议&#xff08;TCP&am…

【视频课程】算法工程师需要的ChatGPT大模型算法理论与实践课程!非粗浅科普...

前言 自从2022年11月ChatGPT发布之后&#xff0c;迅速火遍全球。其对话的交互方式&#xff0c;能够回答问题&#xff0c;承认错误&#xff0c;拒绝不适当的请求&#xff0c;高质量的回答&#xff0c;极度贴近人的思维的交流方式&#xff0c;让大家直呼上瘾&#xff0c;更是带火…

【Java】面试常问知识点(计算机网络方面)

计算机网络 OSI七层模型 应用层 (Application): 网络服务与最终用户的一个接口。 协议有:HTTP FTP TFTP SMTP SNMP DNS 表示层(Presentation Layer): 数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层) 格式有&#xff0c;JPEG、ASCll、DECOIC、加密格式等 会…

# VGA协议实践

VGA协议实践 文章目录 VGA协议实践1.VGA介绍2. ALTPLL3. 字模与图像生成4. ROM5. 代码5.1 vga驱动模块5.2 显示数据生成模块5.3 按键消抖模块5.4 顶层模块5.5 TCL绑定引脚代码 6. 效果7.总结8.参考文章 1.VGA介绍 VGA:Video Graphics Array视频图形阵列是IBM于1987年提出的一个…

【react全家桶学习】react中函数组件和类式组件(超详/必看)

函数式组件定义及特点 定义&#xff08;核心就是一个函数&#xff0c;返回虚拟dom&#xff09;&#xff1a; import React from reactexport default function index() {return <div>index</div> }特点&#xff1a; 1、适用于【简单组件】的定义2、是一个函数&a…

macOS与Ubuntu困惑解答

homebrew&#xff08;报管理器&#xff09;、yaml、apt-get、apt是包管理工具&#xff1b; zsh、bash都是解释器&#xff0c;是shell语言的解释器&#xff0c;都是服务于shell语言的&#xff0c;它们之间的区别是&#xff0c;zsh能够很好的兼容bash&#xff0c;zsh更优雅&…

web端导航菜单系列

导航菜单属于导航中最常规的一种导航模式&#xff0c;它有2个显而易见的用途&#xff1a;帮助我们找到想要的任何东西和告诉我们现在身在何处。帮助用户在不同页面之间跳转找到目标功能。 导航作为网站或者平台的骨架&#xff0c;是产品设计中不容忽视的一环。结合自身对于导航…

java第一课

常用dos命令 第一个e&#xff1a;加上回车&#xff0c;直接切换到e盘目录 看e盘文件的文件夹 dir加回车 进入文件夹 cd 文件夹名称加回车 进入文件夹就是 cd加文件夹名称 cd 加一个文件夹的名称就是进入这个文件夹 回退就是cd.. (这样子是单级目录的回退) 进入很多个就是进入…

必备装机软件,软件推荐

https://www.den4b.com/download/renamer/installer?key9d97aa7096681c8342442f75e34f7d5a8b13551ee3283956323516c81b1fe91b 官网https://www.den4b.com/ 从不同的文件夹中选择文件并将它们添加到工作区域。 a、 更改添加文件夹按钮的默认行为(可选步骤) b、添加单独选择的文…

数据库基础篇 《14.视图》

数据库基础篇 《14.视图》 1. 常见的数据库对象 对象描述表(TABLE)表是存储数据的逻辑单元&#xff0c;以行和列的形式存在&#xff0c;列就是字段&#xff0c;行就是记录数据字典就是系统表&#xff0c;存放数据库相关信息的表。系统表的数据通常由数据库系统维护&#xff0…

Ubuntu运行.sh文件

一、运行.sh文件 &#xff08;1&#xff09;使用sh testsh执行 &#xff08;2&#xff09;使用bash testsh 执行 &#xff08;3&#xff09;使用点 执行 &#xff08;4&#xff09;使用source执行./sh 文件开头***的含义&#xff1a; #!/bin/sh     以下的代码由/…

Redis 的 Protected Mode 解读

官方配置文件自带的注释&#xff1a; Protected mode is a layer of security protection, in order to avoid that Redis instances left open on the internet are accessed and exploited.When protected mode is on and if:1) The server is not binding explicitly to a …

服务(第十四篇)lvs的高可用负载均衡

Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题。 在一个LVS服务集群中通常有主服务器&#xff08;MASTER&#xff09;和备份服务器&#xff08;BACKUP&#xff09;两种角色的服务器&#xff0c;但是对外表现为一个虚…

Qt连接MySql数据库(本地和远程数据库均可)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 三种方法方法一 略方法二 使用ODBC设置mysql为数据源库1. 添加ODBC数据源&#xff0c;在控制面板中找到管理工具&#xff0c;其中有ODBC数据源 64位的&#xff0c;打…

美颜sdk与人脸识别技术的结合:为智能化时代注入美感

在当今的智能化时代&#xff0c;人脸识别技术已经成为了很多应用的核心。而在这些应用中&#xff0c;美颜功能也逐渐成为了用户所追求的重要特性之一。因此&#xff0c;美颜sdk的出现和发展&#xff0c;为人脸识别技术注入了更多的美感。 一、定义和作用 美颜sdk可以对人脸进…

Django框架之模板基本使用

本篇文章主要讲解Django 3.0框架配置模板路径&#xff0c;并使用视图和模板结合实现一些小功能。 概述 模板是html页面&#xff0c;可以根据视图中传递过来的数据进行填充。 在project中创建templates目录和应用模板目录 如templates/myapp 配置模板路径 修改settings.py文…

四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < n nums1[i] nums2[j] nums3[k] nums4[l] 0 来源&#xff1a;力扣&#xff08;LeetCode&#xff0…

Java 网络IO编程总结 nio netty原理 bio nio aio io多路复用 事件驱动 信号驱动 汇总总结

目录 ​编辑 io 多路复用 NIO 多线程 和 io多路复用区别 &#xff1a; Netty 操作流程 看了众多精简总结 Netty Bio Nio Aio Io多路复用 事件驱动 信号驱动 io 多路复用 I/O 多路复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力&#xff…

【备忘录设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

简介 备忘录模式&#xff08;Memento Pattern&#xff09;是一种结构型设计模式。这种模式就是在不破坏封装的条件下&#xff0c;将一个对象的状态捕捉(Capture)住&#xff0c;并放在外部存储起来&#xff0c;从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录…