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

一、现象

最近在编写传统的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 {@AutowiredApplicationContext ctx;private static Logger LOGGER  = LoggerFactory.getLogger(MvcTest.class);@Testpublic 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 RunAfterTestMethodCallbacksprivate 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 useString 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 caninformUponSimilarName(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.mzph.cn/news/728841.shtml

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

相关文章

leetcode69---x 的平方根

大家好&#xff0c;我是大唐&#xff0c;刚刷完了几道经典的leetcode题&#xff0c;今天给大家分享一道leetcode上面的二分查找经典题型---x 的平方根&#xff0c;我们往下看。 题目描述 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&a…

《招聘 ERP 系统架构师指南:微服务、云计算与高负载下的挑战》

在服装行业&#xff0c;ERP系统的顺畅运行对企业至关重要。为了确保您招聘到一位能够应对微服务架构、公有云环境以及高负载挑战的卓越架构师&#xff0c;以下是一份详细的面试指南。 技术深度与系统架构 微服务架构&#xff1a; 问题&#xff1a;请详细解释微服务架构在您设计…

2021片段代码记录-智能感知

【if else美化】 // 前 if(newValue.length 0){this.btn_disable true } else{this.btn_disable false } // 后 this.btn_disable newValue.length 0【五层数据联动】冗余待改善 <template><!-- 数据展示页面 --><div class"container"><…

Ant Design Vue 修改Model弹框 样式不生效

今天在使用 Ant Design Vue 组件库中又踩了一个坑 其他的样式都可以更改&#xff0c;唯独更改 Model 弹框组件的样式一直不生效 于是研究了好久才找到样式不生效的原因 最后又折腾了好久&#xff0c;参考了不少资料才得出的解决方案&#xff1a;

Gin 获取请求参数

POST 请求参数 Gin 获取Post请求URL参数有三种方式 func (c *Context) PostForm(key string) string func (c *Context) DefaultPostForm(key, defaultValue string) string func (c *Context) GetPostForm(key string) (string, bool)大多数情况下使用的是application/x-www…

力扣刷题

文章目录 1. 双指针1.1 两数之和1.2 三数之和1.3 盛最多水的容器1.4 接雨水 2. 字串2.1 滑动窗口最大值 3. 动态规划4. 多维动态规划4.1 最长回文字串 1. 双指针 1.1 两数之和 思路&#xff1a;因为是有序数组&#xff0c; 1.2 三数之和 题目要求不能重复 思路&#xff1a;三…

力扣刷题Days12第二题--100相同的树(js)

目录 1,题目 2&#xff0c;代码 2.1深度优先遍历 2.2广度优先遍历 3&#xff0c;学习与总结 1,题目 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是…

vue element plus Form 表单

表单包含 输入框, 单选框, 下拉选择, 多选框 等用户输入的组件。 使用表单&#xff0c;您可以收集、验证和提交数据。 TIP Form 组件已经从 2. x 的 Float 布局升级为 Flex 布局。 典型表单# 最基础的表单包括各种输入表单项&#xff0c;比如input、select、radio、checkbo…

Java毕业设计 基于SpringBoot vue 疫苗咨询与预约系统

Java毕业设计 基于SpringBoot vue 疫苗咨询与预约系统 SpringBoot vue 疫苗咨询与预约系统 功能介绍 用户前端&#xff1a;首页 图片轮播 疫苗信息 条件查询 疫苗详情 点我收藏 评论 接种疫苗 疫情资讯 资讯详情 资讯评论 论坛交流 发布帖子 公告信息 公告详情 留言反馈 登录…

基于AM62X+FPGA/MCU的B码对时定制化整机解决方案

什么是IRIG-B码对时 IRIG-B(inter-range instrumentationgroup-B)码是一种时间同步标准&#xff0c;通常用于精确的时间测量和数据同步&#xff0c;广泛应用于电力、通信、航空等领域。 IRIG-B码为每秒一帧的时间串码&#xff0c;一帧串码中包含100个码元&#xff0c;频率为1K…

C++ 特殊的类设计

目录 1.请设计一个类&#xff0c;不能被拷贝 2. 请设计一个类&#xff0c;只能在堆上创建对象 3. 请设计一个类&#xff0c;只能在栈上创建对象 4. 请设计一个类&#xff0c;不能被继承 5. 请设计一个类&#xff0c;只能创建一个对象(单例模式) 1.请设计一个类&#xff0c;…

用readproc函数读取进程的状态

概要&#xff1a; 本篇演示用readproc函数读取进程的状态 libprocps库的安装参考笔者的文章readproc.h-CSDN博客 演示所用的系统是Ubuntu22.04 一、代码 #include<stdio.h> #include<stdlib.h> #include<proc/readproc.h> int main() {struct PROCTAB *…

CentOS下安装RabbitMQ

准备工作&#xff0c;更新yum源 正式环境慎用 yum update -y # 进入目录 cd /etc/yum.repos.d/ # 创建目录 mkdir backup # 默认源配备份 mv C* backup/ # 下载阿里云yum源 wget -O /etc/yum.repos.d/CenOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo # 清除旧…

大唐国际务实迎战两会保电,智能巡检机器人助力电力保障

全国两会召开在即。近年来&#xff0c;我国两会期间电力供应稳定性备受关注。作为国家重要的政治盛会&#xff0c;两会的顺利召开需要可靠的电力保障&#xff0c;以确保会议期间各项活动的正常进行。大唐国际作为国内领先的电力企业&#xff0c;面临着如何保障两会期间电力供应…

金融行业专题|基金超融合架构转型与场景探索合集(2023版)

更新内容 更新 SmartX 超融合在基金行业的覆盖范围、部署规模与应用场景。更新信创云资源池、关键业务系统性能优化等场景实践。更多超融合金融核心生产业务场景实践&#xff0c;欢迎下载阅读电子书《金融核心生产业务场景探索文章合集》。 随着数字化经济的蓬勃发展&#xf…

【Linux-tar/gzip/zip】

Linux-tar/gzip/zip ■ tar■ gzip■ zip■ unzip解压缩 ■ tar 基本语法&#xff1a; tar [选项] 压缩后的压缩包 要压缩的文件 选项说明描述-cf :对文件或文件夹进行打包-v :显示压缩的进度- z :使用gzip压缩工具把打包后的文件进行压缩为.gz-j :使用bzip2压缩工具把打包后…

如何使用WinSCP结合Cpolar实现公网远程访问内网Linux服务器

文章目录 1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件&#xff0c;它的主要功能是在本地与远程计…

基本工具学习--宝藏“课程”

Contents Linux概念&提问Vim命令正则表达式工具 Git自我修养 Linux Linux入门教程鸟哥的Linux私房菜Harley Hahn’s Guide to Unix and Linux计算机教育缺失的一课&#xff1a;https://missing.csail.mit.edu/Linux C编程一站式学习&#xff1a;https://akaedu.github.io/…

HCIP---IS-IS协议

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.IS-IS协议概述 IS-IS是一种基于链路状态的内部网关协议&#xff08;IGP&#xff09;&#xff0c;它使用最短路径优先算法&#xff08;SPF或Dijkstra&#xff09;进行路由计算。这种协议在自治…

javascript:void(0)用法及常见问题解析

文章目录 一、javascript:void(0)用法1. 阻止链接的默认行为2. 结合事件处理器3. 为什么使用 javascript:void(0) 而不是 #4. 现代替代方案 二、javascript:void(0)常见问题解析常见问题解析1. 为何使用 javascript:void(0) 而不是简单的 #&#xff1f;2. javascript:void(0) 是…