SpringBoot集成Log2j4指定外部配置文件源码解读

一、背景

程序读取外部log4j2.xml配置文件方式为启动命令添加了--logging.config=/path/log4j2.xml,因系统安全整改,将/var/log/目录改为了700,程序使用非root启动时log4j2报错无法在/var/log目录下创建日志文件。经排查发现jar包的classpath目录下指定的日志文件位置在/var/log下,但是外部配置文件指定的日志文件在该系统用户下具有创建文件权限。按之前的理解通过logging.config指定了外部配置文件后jar包内的配置文件会失效,那为什么程序还是去读取了jar包内的配置文件导致在没有权限的目录下尝试创建日志文件呢。

二、问题排查

修改jar包内的配置文件,将日志输出路径改为该系统用户可以创建文件的/opt/log目录,修改外部配置文件,将日志输出路径改为该系统用户可以创建文件的/opt/log2目录。启动程序后发现,在/opt/log以及/opt/log2目录下都生成了日志文件,但是/opt/log目录下的日志文件并不会产生日志,只有/opt/log2目录下的日志文件(也就是外部配置文件指定的目录)会产生日志,且外部配置文件可以控制程序的日志级别。带着这个问题查看springboot加载log4j2并读取log4j2.xml过程的源码。

三、SpringBoot下Log4j2加载配置文件优先级。

1.初次加载

springboot程序启动的类初始化阶段会触发log4j2初次加载配置文件,即不受springboot配置的影响,一定会加载的,其加载顺序如下。(这里只说明在不同目录下加载log4j2.xml配置文件的顺序,不说明其他后缀的配置文件加载顺序)

-Dlog4j.configurationFile=/path/log4j2.xml  >  jar包内classpath下log4j2.xml

参数说明:

-D:添加在启动命令的虚拟机参数位置,代表设置虚拟机环境变量。必须使用该方式。

特别说明一下在背景中提到的--logging.config参数,它是配置在java启动命令的程序参数位置,也就是会通过main方法传递过来的,一般springboot的一些配置是通过这种形式传递的,例如:--spring.config.location配置指定springboot的配置文件位置,那么为什么不能通过--log4j.configuration的方式配置log4j2的配置文件位置呢,因为他的加载是在类的初始化阶段进行的,此时spring容器还尚未加载完成,且log4j源码中也体现了是从jvm环境变量中读取的该配置项。

详细的java启动参数的说明可以参考一下文章:

https://www.cnblogs.com/haycheng/p/12781261.html

2.配置重加载

log4j2的配置文件重加载发生在springboot的监听器中,它触发log4j2重加载配置文件,是加载logging.config配置的。

3.结论

通过以上两点就解释了问题排查中讲到的程序即加载了jar包内的log4j2.xml也加载了--logging.config指定的log4j2.xml,那么我们在springboot中使用log4j2且想指定外部配置文件时最好使用-Dlog4j.configurationFile的这种方式,让程序在初次加载时就指定外部配置文件,这样程序就不会加载jar包内的配置文件并创建对应的日志文件了(虽然也不往其中输出日志,但目录和文件毕竟会创建,当程序权限不足时就会报错)。

四、SpringBoot启动加载log4j2.xml过程源码解读

1.首次加载

SpringApplication类中定义了静态的Log对象,因此在加载SpringApplication类时会触发Log类的加载及初始化操作,而不是等到执行run方法,这也就是为什么说log4j2首次加载配置文件时spring容器还未加载完成。

springboot使用jcl(Jakarta Commons Logging)日志门面,其中LogFactory为jcl的包下的类,其通过spring-jcl引入。

进入LogAdapter#createLog,createLog为Function类型,查看何时给其赋值。

 在LogAdapter的静态代码块中通过几个Present判断为createLog复制,可以看出这里为选择一种具体的日志实现的适配器。

 查看这几个Present如何来的。通过查看不同日志类型的类是否可以被加载来判断程序使用哪种日志框架,该案例中选择的是log4j 2.x + slf4j的实现。

进入createLog定义的Function,即Log4jAdapter::createLog,Log4jAdapter为LogAdapter的内部类。

调用Log4jLog类的构造方法,Log4jLog为LogAdapter的内部类。其中LogManager为log4j-api包内的类,其通过spi机制找到org.apache.logging.log4j.core.impl.Log4jProvider

 

 

2.springboot重加载

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

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

相关文章

我的创作纪念日—128天的坚持|分享|成长

☘️博主介绍☘️: ✨又是一天没白过,我是奈斯,DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&am…

C++进阶-----继承

1、继承的概念和定义 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象 程序设计的层次结构&#xf…

Docker-Compose概述与简单编排部署

目录 前言 一、Docker-Compose 概述 1、Docker-Compose 概念 2、Docker-Compose 优缺点 2.1 Docker-Compose 优点 2.2 Docker-Compose 缺点 3、Docker-Compose与Docker-Swarm的区别 二、两大文件格式 1、YAML 文件格式 2、JOSON 文件格式 3、YAML 与 JOSON 格式的区…

全志ARM-蜂鸣器

sh操作准备: 1.使Tab键的缩进和批量对齐为4格 在/etc/vim/vimrc 中添加一项配置 set tabstop 4; 也可以再加一行 set nu显示代码的行数 vim的设置,修改/etc/vim/vimrc文件,需要用超级用户权限 /etc/vim/vimrc set shiftwidth4 设置批量…

【刷题篇】动态规划-二维费用的背包问题(十二)

文章目录 1、一和零2、盈利计划3、组合总和 Ⅳ4、不同的二叉搜索树(卡特兰数) 1、一和零 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素&#x…

vue3 + ts 快速入门(全)

文章目录 学习链接1. Vue3简介1.1. 性能的提升1.2.源码的升级1.3. 拥抱TypeScript1.4. 新的特性 2. 创建Vue3工程2.1. 基于 vue-cli 创建2.2. 基于 vite 创建(推荐)vite介绍创建步骤项目结构安装插件项目结构总结 2.3. 一个简单的效果Person.vueApp.vue …

数字电路-5路呼叫显示电路和8路抢答器电路

本内容涉及两个电路,分别为5路呼叫显示电路和8路抢答器电路,包含Multisim仿真原文件,为掌握FPGA做个铺垫。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易! 目录: 一、5路呼叫显…

Android BINDER是干嘛的?

1.系统架构 2.binder 源码位置: 与LINUX传统IPC对比

题目:吃奶酪

问题描述: 解题思路: 枚举每种吃奶酪顺序,并计算其距离,选择最小的距离即答案。v数组:记录顺序。 注意点:1. 每次用于min的s需要重置为0。 2. 实数包括小数,所以结构体内x,y为double类型。 3. 第…

Python | Leetcode Python题解之第65题有效数字

题目: 题解: from enum import Enumclass Solution:def isNumber(self, s: str) -> bool:State Enum("State", ["STATE_INITIAL","STATE_INT_SIGN","STATE_INTEGER","STATE_POINT","STATE_…

Vue基础:为什么要学Vue3,Vue3相较于Vue2有那些优势?

为什么要学Vue3? 1.框架层面 1.响应式底层API的变化 Proxy 数组下标的修改 对象动态添加属性 解释说明:1.vue2采用的是Object.definePrototype,它每次只能对单个对象中的单个数据进行劫持,所以在Vue2中data()中的数据一多就要进行…

基础IO认识

回顾文件 我们之前认识文件只是在语言程度上理解&#xff0c;但是我们理解的不够彻底&#xff0c;要想真正理解文件要在os上理解。 简单代码认识 1 #include<stdio.h>2 int main(){3 FILE* fpfopen("log.txt","w");4 if(fpNULL){5 p…

leetcode_43.字符串相乘

43. 字符串相乘 题目描述&#xff1a;给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 &q…

蓝桥杯练习系统(算法训练)ALGO-951 预备爷的悲剧

资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 英语预备爷gzp是个逗(tu)比(hao)&#xff0c;为了在即将到来的英语的quiz中不挂科&#xff0c;gzp废寝忘食复习英语附录单词…

SpringBoot 基础简介

目录 1. SpringBoot 概述 1.1. 为什么会有springboot 1.1.1. 传统Spring 的两个缺点 1.1.2. Springboot 功能 2. SpringBoot 快速搭建 2.1. 创建Maven项目​编辑​编辑​编辑 2.2. 导入SpringBoot起步依赖 2.3. 定义controller 2.4. 添加引导类 2.5. 启动访问 3. Sprin…

使用node调用chrome(基于selenium-webdriver包)

下载测试版chrome和chromedriver https://googlechromelabs.github.io/chrome-for-testing/ 把chromedriver复制到chrome的文件里 设置环境变量 编写代码 const { Builder, Browser, By, Key, until } require(selenium-webdriver) const puppeteer require(puppeteer)//查…

2024/5/2 英语每日一段

Enablers’ fate is inherently linked to adopters, which are their ultimate customers, after all. That dynamic won’t flip overnight, but adopters able to demonstrate progress on AI initiatives will increasingly get credit from investors in the form of high…

基于SSM的“一汽租车辆共享平台”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“一汽租车辆共享平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 租车界面 订单管理界面 财务报表界面 理赔界面 …

I/O体系结构和设备驱动程序

I/O体系结构 为了确保计算机能够正常工作&#xff0c;必须提供数据通路&#xff0c;让信息在连接到个人计算机的CPU、RAM和I/O设备之间流动。这些数据通路总称为总线&#xff0c;担当计算机内部主通信通道的作用。 所有计算机都拥有一条系统总线&#xff0c;它连接大部分内部…

Java | Leetcode Java题解之第63题不同路径II

题目&#xff1a; 题解&#xff1a; class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int n obstacleGrid.length, m obstacleGrid[0].length;int[] f new int[m];f[0] obstacleGrid[0][0] 0 ? 1 : 0;for (int i 0; i < n; i) {for (i…