spring启动时如何自定义日志实现

news2024/7/6 19:12:34

一、现象

最近在编写传统的springmvc项目时,遇到了一个问题:虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener,且开启了日志写入文件,但是日志文件中只记录业务代码中我们声明了日志记录器的日志,无法记录spring上下文启动的日志。

测试方法如下:

package com.vat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * @author:whh
 * @date: 2024-02-01 21:02
 * <p></p>
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationcontext.xml"})
public class MvcTest {
    @Autowired
    ApplicationContext ctx;
    private static Logger LOGGER  = LoggerFactory.getLogger(MvcTest.class);
    @Test
    public void fun(){
        LOGGER.debug("123");
        System.out.println(ctx);
    }
}

观察下面的运行结果截图,我们发现红色字体是spring上下文的启动日志而灰色字体是我们打印的日志,而且在日志文件中也只记录了我们打印的日志和druid数据源的关闭日志。实时上spring上下文的启动日志也非常重要,通常用于我们观察我们的应用是否启动正常,有没有一些bean没有被spring初始化到。但是从上面看其启动日志并未记录到日志文件中。
在这里插入图片描述

二、思考

遇到这个问题后,便使用debug模式追踪spring上下文的启动。

首先这个测试类有一个注解@RunWith(SpringJUnit4ClassRunner.class)
即我们可以猜测是通过SpringJUnit4ClassRunner来启动spring上下文的,接着我们看下这个类的源码。

public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {

	private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class);

	private static final Method withRulesMethod;

	// Used by RunAfterTestClassCallbacks and RunAfterTestMethodCallbacks
	private static final String MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME = "org.junit.runners.model.MultipleFailureException";

	static {
		boolean junit4dot9Present = ClassUtils.isPresent(MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME,
			SpringJUnit4ClassRunner.class.getClassLoader());
		if (!junit4dot9Present) {
			throw new IllegalStateException(String.format(
				"Failed to find class [%s]: SpringJUnit4ClassRunner requires JUnit 4.9 or higher.",
				MULTIPLE_FAILURE_EXCEPTION_CLASS_NAME));
		}

注意看第一行是使用静态常量初始化了一个logger对象并且这个Log的类型是org.apache.commons.logging.Log类型,学过类加载的我们都知道,静态常量是在编译器就会进行初始化。

接着进入getFactory方法,首先会获取一个类加载器,我们可以看出是
lanucherAppClassLoader。
在这里插入图片描述

接着往下走会往缓存中获取LogFactory,第一次肯定是null的。
在这里插入图片描述

接下来加载commons-logging.properties的配置文件,但是咱们没有配置,所以是空的。
在这里插入图片描述
最终创建了一个默认的LogFactoryImpl。
在这里插入图片描述

调用的LogFactoryImpl来创建Log

在这里插入图片描述
后面到discoverLogImplementation方法,确定具体的日志实现。

private Log discoverLogImplementation(String logCategory)
        throws LogConfigurationException {
        if (isDiagnosticsEnabled()) {
            logDiagnostic("Discovering a Log implementation...");
        }

        initConfiguration();

        Log result = null;

        // See if the user specified the Log implementation to use
        String specifiedLogClassName = findUserSpecifiedLogClassName();

        if (specifiedLogClassName != null) {
            if (isDiagnosticsEnabled()) {
                logDiagnostic("Attempting to load user-specified log class '" +
                    specifiedLogClassName + "'...");
            }

            result = createLogFromClass(specifiedLogClassName,
                                        logCategory,
                                        true);
            if (result == null) {
                StringBuffer messageBuffer =  new StringBuffer("User-specified log class '");
                messageBuffer.append(specifiedLogClassName);
                messageBuffer.append("' cannot be found or is not useable.");

                // Mistyping or misspelling names is a common fault.
                // Construct a good error message, if we can
                informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
                informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
                informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
                informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
                throw new LogConfigurationException(messageBuffer.toString());
            }

            return result;
        }

最终确定为jdk的日志实现。

由于spring也是使用的org.apache.commons.logging.Log作为日志适配器,,所以会使用缓存的日志工厂LogFactoryImpl,即其日志实现也是jdk的日志,,所以和咱们自己配置log4j实现根本无关也就造成了spring上下文的日志未写入文件。

三、问题解决

怎么解决这个问题呢?这边有两个方案。
方案1:刚刚我们在调试模式模式的时,实例化LogFatory时会加载一个commons-logging.properties然后在该配置文件中加入属性
org.apache.commons.logging.LogFactory=XXX,其中XXX是自定义的LogFactory,然后在该LogFactory中指定日志的实现,但是这种方式还是有些复杂的。

方案2:使用spring的jcl方案,其实这个jcl方案是我参考了springboot启动时的日志实现原理,所以咱们只要引入下面的依赖,版本可以看实际情况,本项目的spring版本为4.2.4。

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jcl</artifactId>
      <version>5.3.5</version>
    </dependency>

其原理是基于spi机制,通过LogAdapter进行检测具体的日志实现。
在这里插入图片描述

LogAdapter的static静态代码块会确定具体的日志实现。

在这里插入图片描述

四、结果

集成spring jcl后,咱么在启动测试类,观察结果返现控制台即文件输出日志都换成了咱们指定的log4j日志实现。
在这里插入图片描述

而且咱们使用jetty的maven插件,启动咱们的项目,也是有相同效果的。
在这里插入图片描述

五、最后

完整项目,大家可以到我的码云自行下载:

https://gitee.com/whhdev/vat/tree/master

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

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

相关文章

CPU设计实战-协处理器访问指令的实现

目录 一 协处理器的作用与功能 1.计数寄存器和比较寄存器 2.Status寄存器 3.Cause寄存器(标号为13) 4.EPC寄存器(标号为14) 5.PRId寄存器(标号为15) 6.Config 寄存器(标号为16)-配置寄存器 二 协处理器的实现 三 协处理器访问指令说明 四 具体实现 1.译码阶段 2.执行…

3/12/24交换排序、插入排序、选择排序、归并排序

目录 交换排序 冒泡排序 快速排序 插入排序 直接插入排序 选择排序 简单选择排序 堆排序 归并排序 各种排序的时间复杂度、空间复杂度、稳定性和复杂度 快排真题2016 选排真题2022 排序算法分为交换类排序、插入类排序、选择类排序、归并类排序。 交换排序 交换排…

【智能算法】哈里斯鹰算法(HHO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2019年&#xff0c;Heidari 等人受到哈里斯鹰捕食行为启发&#xff0c;提出了哈里斯鹰算法(Harris Hawk Optimization, HHO)。 2.算法原理 2.1算法思想 根据哈里斯鹰特性&#xff0c;HHO分为探索-…

新智元 | Stable Diffusion 3技术报告流出,Sora构架再立大功!生图圈开源暴打Midjourney和DALL·E 3?

本文来源公众号“新智元”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Stable Diffusion 3技术报告流出&#xff0c;Sora构架再立大功&#xff01;生图圈开源暴打Midjourney和DALLE 3&#xff1f; 【新智元导读】Stability AI放…

chrome浏览器插件content.js和background.js还有popup都是什么,怎么通讯

popup 在用户点击扩展程序图标时&#xff08;下图中的下载图标&#xff09;&#xff0c;都可以设置弹出一个popup页面。而这个页面中自然是可以包含运行的js脚本的&#xff08;比如就叫popup.js&#xff09;。它会在每次点击插件图标——popup页面弹出时&#xff0c;重新载入。…

如何阅读“计算机界三大神书”之一 ——SICP

《计算机程序的构造和解释》&#xff08;Structure and Interpretation of Computer Programs&#xff0c;简记为SICP&#xff09;是MIT的基础课教材&#xff0c;出版后引起计算机教育界的广泛关注&#xff0c;对推动全世界大学计算机科学技术教育的发展和成熟产生了很大影响。…

plantUML使用指南之序列图

文章目录 前言一、序列图1.1 语法规则1.1.1 参与者1.1.2 生命线1.1.3 消息1.1.4 自动编号1.1.5 注释1.1.6 其它1.1.7 例子 1.2 如何画好 参考 前言 在软件开发、系统设计和架构文档编写过程中&#xff0c;图形化建模工具扮演着重要的角色。而 PlantUML 作为一种强大且简洁的开…

springboot265基于Spring Boot的库存管理系统

基于Spring Boot库存管理系统 Inventory Meanagement System based on Spring Boot 摘 要 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c;很容易受潮…

《vtk9 book》 官方web版 第3章 - 计算机图形基础 (3 / 5)

3.8 演员几何 我们已经看到了光照属性如何控制演员的外观&#xff0c;以及相机如何结合变换矩阵将演员投影到图像平面上。剩下的是定义演员的几何形状&#xff0c;以及如何将其定位在世界坐标系中。 建模 计算机图形学研究中的一个重要主题是建模或表示物体的几何形状。…

二分查找【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;二分查找的简单思路&#xff0c;为什么必须在有序的前提下才能使用二分查找&#xff0c;该怎么用C程序来实现二分查找&#xff0c;二分查找的局限性&#x1f440;。 文章目录 1. 题目2. 思路3. 前提条件4. 编写程序 1. 题目 在一个有…

Android Studio开发项目——记账簿应用

项目资源&#xff1a; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1zN9lrIypi1t_QpuoBcdBNQ?pwdxj5h 提取码&#xff1a;xj5h 项目设计内容 1.基本功能描述 电子记账本是一种在线财务管理工具&#xff0c;用于帮助用户记录和管理他们的收入与支出。以下是电…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的水果新鲜程度检测系统(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;开发水果新鲜程度检测系统对于提高农业产量和食品加工效率具有重大意义。本篇博客详细介绍了如何利用深度学习构建一个水果新鲜程度检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并结合了YOLOv7、YOLOv6、YOLOv5的对比…

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling ChatGPT结合实际业务&#xff0c;主要是研发多函数调用&#xff08;Function Calling&#xff09;功能模块&#xff0c;将自定义函数通过ChatGPT 问答结果&#xff0c;实现对应函数执行&#xff0c;再次将结果…

打卡学习kubernetes——了解kubernetes组成及架构

目录 1 什么是kubernetes 2 kubernetes组件 3 kubernetes架构 1 什么是kubernetes kubernetes是一个旨在自动部署、扩展和运行应用容器的开源平台。目标是构建一个生态系统&#xff0c;提供组件和工具以减轻在公共和私有云中运行应用程序的负担。 kubernetes是&#xff1a…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的跌倒检测系统详解(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;本研究介绍了一个基于深度学习和YOLOv8算法的跌倒检测系统&#xff0c;并对比分析了包括YOLOv7、YOLOv6、YOLOv5在内的早期版本性能。该系统可在多种媒介如图像、视频文件、实时视频流中准确识别跌倒事件。文内详解了YOLOv8的工作机制&#xff0c;并提供了相应…

一套plm系统大约多少钱?彩虹PLM系统

一套PLM&#xff08;产品生命周期管理&#xff09;系统的价格因多个因素而异&#xff0c;包括企业规模、需求复杂性、系统功能、技术支持和厂商选择等。一般来说&#xff0c;面向小型和微型企业的PLM产品&#xff0c;其价位在5万元~15万元左右&#xff1b;面向中型企业的中端PL…

STM32---通用定时器(二)相关实验

写在前面&#xff1a;前面我们学习了基本定时器、通用定时器的相关理论部分&#xff0c;了解到通用定时器的结构框图&#xff0c;总共包含六大模块&#xff1a;时钟源、控制器、时基单元、输入捕获、公共部分以及输出捕获。对相关模块的使用也做详细的讲解。本节我们主要是对上…

力扣刷题日记——L724. 寻找数组的中心下标

1. 前言 今天是力扣刷题日记的第二天&#xff0c;今天依旧是一道简单题啊&#xff0c;慢慢来&#xff0c;先看看题目是什么吧。 2. 题目描述 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和…

数据结构入门(3)顺序表和链表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直…

汤唯短发造型:保留经典和适合自己的风格,也许才是最重要的

汤唯短发造型&#xff1a;保留经典和适合自己的风格&#xff0c;也许才是最重要的 汤唯短发造型登上Vogue四月刊封面&#xff0c;引发网友热议。#李秘书讲写作#说说是怎么回事&#xff1f; 这次Vogue四月刊的封面大片&#xff0c;汤唯以一头短发亮相&#xff0c;身穿五颜六色的…