一次日志配置未生效问题排查记录

news2024/11/26 0:31:58

某天排查业务问题时,在我司的日志收集平台上,未能发现相关业务服务接口访问日志。经过和相关同事确定,发现业务服务未能将接口访问日志吐到日志收集平台,由此开启一段有点漫长的排查之旅。

业务服务是典型的SpringBoot web应用,日志记录采用slf4j+log4j2组合。

通过application.propertis文件中logging.config配置项指定了日志配置文件log4j2.xml具体位置。

通过实现org.apache.logging.log4j.core.Appender接口实现自定义的HttpAppender,将接口访问记录发送到日志收集平台。在log4j2.xml配置文件里如下图

具体管理与日志收集平台服务连接,发送请求的是类HttpManager,HttpManager的属性url决定了接收日志信息的日志收集平台地址(来源是配置文件中HttpAppender的url属性)。

所有HttpAppender对象共享同一个HttpManager,按照单例模式创建,合理的做法应是每个HttpAppender有自己单独的HttpManager。

这会导致在有配置多个HttpAppender的情况下,只有在实例化第一个HttpAppender时,其配置的日志平台收集服务的地址会生效。其他的通通无效.

合理的办法是改动HttpAppender源代码,但是我没权限。只能从日志配置文件加载方面想办法,想办法让我们指定的日志配置文件生效。

基本情况介绍完毕,下面开始排查之旅。

在和同事确定之后没有讲日志信息发送到日志收集平台,凭直觉猜测日志收集平台地址是否正确。检查日志配置文件,发现地址配置正确。难道就这样结束了?不,你已经是一个成熟的程序员了,要学会自己排查问题了。

遇到这种情况时,我看了一下其他服务,相同的配置,却稳定正常的向日志收集平台在发送日志。那我就有理由猜测线上服务实际生效的日志收集平台地址不是我们想要的,但是如何验证了。

我们如何方便的可以查看线上服务某个类对象的字段值了,那就要亮出大杀器了,阿里巴巴开源的Arthas。使用arthas的vmtool命令查看jvm进程中的实例对象数据

看到的结果让人始料未及,线上服务使用的是日志收集平台的测试地址。现在问题变成了,是什么原因导致了线上加载了错误的配置。

在idea中全局搜索发现,在resources目录下的log4j2.xml中,含有相同的HtppAppender,url属性配置成了日志收集平台的测试地址,maven打包之后,业务jar中也会含有log4j2.xml文件。

那接下来要验证的是jar包里的log4j2.xml是否被加载,如果被加载,这样算下来就会有两份位置不同log42.xml文件被加载。

接下来就是如何进行验证了。

思路是如果加载了两份不同的位置的log4j2.xml,那么创建HttpAppender的方法就会被调用两次,如果能追踪到创建HttpAppender方法的被调用路径,那么排查起来就会轻松些了.

这次还是借助Arthas,通过其stack命令,可以查看指定方法被调用的调用路径。

因为SpringBoot应用一起动,便会在SpringApplication的prepareEnvironment方法里触发日志配置文件加载,等我通过Arthas attach到指定的jvm进程,执行stack命令的时候,日志配置加载已经结束了,为时已晚。

解决办法是通过在启动类的main方法里加上一行代码TimeUnit.SECONDS.sleep(30) 延迟应用的启动,以便有足够的时候通过Arthas attach到jvm进程,执行stack命令。结果如下两张图

通过上面这两张图得知,在LoggingApplicationListener正常加载日志配置文件之前,BootStrapApplicationListener触发LoggingApplicationListener做了一次日志配置文件加载。

接下来需要分析的是由BootStrapApplicationListener触发加载的是哪个日志配置文件。

通过分析BootStrapApplicationListener的bootstrapServiceContext方法,其新建的SpringApplication实例对象,读取的默认配置文件为bootstrap.yml。因为项目并没有配置bootstrap.yml,导致ConfigFileApplicationListener对Environment进行初始化时,没有设置logging.config。

没有设置logging.config 那ConfigFileApplicationListener是从哪里加载日志配置文件了。通过分析代码,默认的日志配置加载文件位置由org.springframework.boot.logging.AbstractLoggingSystem#getSelfInitializationConfig方法负责,如下图

通过上图可得知主要检测classpath里是否存在log4j2.xml,logj4j2.json这样的配置文件。不巧的是服务的jar里存在log4j2.xml,且配置的url是日志收集平台的测试地址。那要如何解决了这个问题了。

通过分析BootStrapApplicationListener源代码,通过在启动脚本里启动命令中添加-Dspring.cloud.bootstrap.enabled=false来禁止BootstrapApplicationListener运行.

修改完毕,再次执行vmtool命令检查日志收集平台url配置是否正确。结果显示的还是日志收集平台测试地址。那说明还有其他地方触发加载了jar包内的log4j2.xml文件。

此时瞥了一眼我们的启动类,那么大一个Logger在那杵着,之前愣是没注意。

启动类xxxApplication被加载的时候,此时会触发LOGGER的创建,虽然使用的是SLF4J,实际上创建的是log4j2的Logger,由此会触发针对日志配置文件的加载,经过google搜索,和针对log4j的源码分析,如果没有指定配置文件,默认是会加载classpath下的log4j2.xml文件。

 此时想到的简单办法是把LOGGER属性注释掉,那么就会避免发生记在jar包内的log4j2.xml文件,从而只加载服务目录下的log4j2.xml。

修改完毕,进行第二次验证。不巧的是,显示的还是日志收集平台测试地址,那说明还是有其他地方触发加载了jar包内的log4j2.xml文件。

此时我们的办法还是和第一次内似,这次我们用的是watch命令,关注的是org.slf4j.LoggerFactory getLogger方法的被调用的时候入参。

通过上图,发现工程依赖的公司封装的Apollo配置服务的jar包内的ApolloApplicationContextInitializer触发了Logger的创建,进而触发加载了jar包内的log4j2.xml。

那这个ApolloApplicationContextInitializer是因为什么原因被触发了class加载,导致了Logger的创建。通过查看源码,ApolloApplicationContextInitializer实现了ApplicationListener接口。SpringApplication在构造函数中会收集当前classpath的spring.factories文件中org.springframework.context.ApplicationListener配置项中的Listener,并实例化。因此导致了ApolloApplicationContextInitializer类的初始化。

到此我们可以做一下总结,业务程序的启动过程中,按先后顺序总共做了三次日志配置文件log4j2.xml的加载。

第一次:由程序启动类XXXApplication的静态属性LOGGER触发,加载的是业务jar包内log4j2.xml。

第二次:  由org.springframework.cloud.bootstrap.BootstrapApplicationListener触发,加载的是业务jar包内log4j2.xml

第三次:  由LoggingApplicationListener负载加载application.properties指定的服务目录下的logj42.xml。


至此,问题的分析与排查可暂时告一段落,接下来的就是如何修复问题,然后进行验证。办法就两条,

1.减少日志配置文件的加载的次数,

2.让log4j2框架第一次加载日志配置文件时,便加载到正确的日志配置文件。

办法1. 通过在服务启动脚本中加入-Dspring.cloud.bootstrap.enable=false,可以避免上面提到的第二次日志文件加载。

办法2. 通过分析lo4j2源代码,在org.apache.logging.log4j.core.config.ConfigurationFactory类中可以发现通过设置名为log4j.configurationFile的sytem property指定要加载的日志配置文件。


 方法都已找到,解决方法如下图所示,综合了前面提到的两条办法。

经过验证,问题得到解决,接口访问日志顺利吐到日志收集平台。一个问题的解决之旅也就此结束。

其实事后总结,还是基础不够扎实,对日志框架的了解掌握不够深入,排查分析问题不够仔细,对启动类存在的Logger熟视无睹,由此忽略了带来的日志配置加载问题。

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

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

相关文章

《BlazePose: On-device Real-time Body Pose tracking》在移动设备端实时人体姿态追踪(2006)

BlazePose: On-device Real-time Body Pose tracking 论文地址:https://arxiv.org/pdf/2006.10204.pdf GoogleBolg:https://ai.googleblog.com/2020/08/on-device-real-time-body-pose-tracking.html 模型下载地址:https://github.com/PIN…

clop勒索软件攻击活动频发,西门子能源中招

自6月初被通报利用MOVEit Transfer服务器中的零日漏洞窃取加密组织数据后,clop勒索软件攻击活动频繁,全球陆续发生了多起clop软件攻击事件。本周,Clop团伙在其数据泄露网站上列出了西门子能源公司的信息,表示该公司的数据被泄露。…

must declare a single constructor

1、异常详细描述 Test ignored.org.junit.platform.commons.PreconditionViolationException: Class [org.bc.offer.datastructure.Stack] must declare a single constructorProcess finished with exit code -12、源码 package org.bc.offer.datastructure;import org.jun…

SUI Token释放计划时间表和几个相关问题

Sui主网于2023年5月3日正式上线,网络原生token SUI用于链上交易、支付gas费用、保护网络以及提供链上流动性。SUI的长期流通总供应量为100亿。在主网上线时,大约有5%的token在流通,其余的将按照下面的图表所示的计划释放,以保持网…

特异性抑制剂:FAPI-4 NH2 ,FAPI4Amine,成纤维细胞活化蛋白 (FAP) 抑制剂连接氨基

编辑来源||陕西新研博美生物科技有限公司小编MISSwu FAPI-4-NH2中FAPI是FAP的特异性抑制剂。在这些FAPI中,含有DOTA配体(FAPI-04)的[68Ga]Ga-DOTA-FAPI-04 PET/CT能显示出良好的体内药代动力学,导致快速kidney清除和注射后10分钟至3小时的低背景活性。 …

JAVA C++的权限区别

访问级别的名称和个数:在C中,有三个访问级别关键字:public、protected和private。而在Java中,有四个访问级别关键字:public、protected、private和默认(没有关键字修饰)。 默认访问级别不同&…

【三层交换机】网络杂谈(16)之三层交换机技术

涉及知识点 什么是三层交换机,三层交换技术的由来,三层交换机,三层交换的应用范例。深入了解三层交换机技术。 原创于:CSDN博主-《拄杖盲学轻声码》,更多内容可去其主页关注下哈,不胜感激 文章目录 涉及知…

SAP-QM质量管理视图字段解析

QM物料授权:如果在物料主记录中输入物料授权组,则系统将检查(在物料基础上)用户是否具有对以上名称对象的授权。如果未输入授权组,则不进行授权检查。要通过授权检查,用户需要 物料授权,其中包含有字段值所表示的相关授权组。 收货处理时间:收货后的加盐时间(天数)如…

深度学习(五)—— 卷积神经网络(CNN)

卷积神经网络(CNN) 1 CNN的组成2 卷积层2.1 卷积的计算2.2 多通道卷积2.3 多卷积核卷积2.4 特征图大小2.5 卷积层 api 实现 3 池化层3.1 最大池化3.2 平均池化 4 全连接层5 CNN的构建5.1 数据加载5.2 数据处理5.3 模型搭建5.4 模型编译5.5 模型训练5.6 模…

Go语言使用net/http实现简单登录验证和文件上传功能

最近再看Go语言web编程,使用net/http模块编写了一个简单的登录验证和文件上传的功能,在此做个简单记录。 目录 1.文件目录结构 2.编译运行 3.用户登录 4.文件上传 5.mime/multipart模拟form表单上传文件 代码如下: package mainimport …

【C语言】递归实战,通过几个例子带你深入走进递归算法

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,这里是君兮_,今天给大家带来一篇递归的实战教学文章,由于递归算法不仅对于初学者十分不易理解并且在我们以后的数据结构中也非常重要。我们今天就通过几个应用递归的实际例子来给…

Apache Doris 在头部票务平台的应用实践:报表开发提速数十倍、毫秒级查询响应

作者|国内某头部票务平台 大数据开发工程师 刘振伟 本文导读: 随着在线平台的发展,票务行业逐渐实现了数字化经营,企业可以通过在线销售、数字营销和数据分析等方式提升运营效率与用户体验。基于此,国内某头部票务平…

【Java】Java核心 81:Git 教程(4)差异比较 版本回退

文章目录 06.GIT本地操作-差异比较目标内容小结 07.GIT本地操作-版本回退目标内容小结 在Git中,可以使用差异比较命令和版本回退命令来查看文件之间的差异并回退到早期的版本。 以下是对这些操作的简要解释: 差异比较:你可以使用git diff命…

本地Linux 部署 Dashy 并远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 转载自cpolar极点云文章:本地Linux 部署 Dashy 并远程访问 简介 Dashy 是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你…

video-04-videojs配置及使用

videojs是一种轻框架,可以帮我们快速开发一个video视频组件 目录 一、参考资料 二、引入videojs 三、简单了解使用 四、配置项和事件 4.1 常用配置项 4.2 常用事件 4.3 常用方法 4.4 网络状态 4.5 播放状态 4.6 视频控制 五、实例(可直接复制…

升级iOS 17测试版后如何降级?iOS17降级教程

对于已经升级到 iOS 17 测试版的用户,如果在体验过程中,感觉到并不是那么稳定,例如出现应用程序不适配、电池续航下降、功能无法正常启用等问题,想要进行降级操作,可以参考本教程。 降级前注意事项: 1.由于…

Android 自定义手写签字板,签署姓名,签名

各位大佬好又来记笔记了~ 今天要做的是签字板,实现客户签名功能,直接看效果: 逐个进行签字,可以避免连笔导致识别不清问题。就是想要客户一个一个写,认真写~~。 下面方框显示的“王某才” 其实是三张图片,…

【算法题】动态规划中级阶段之不同路径、最小路径和

动态规划中级阶段 前言一、不同路径1.1、思路1.2、代码实现 二、不同路径 II2.1、思路2.2、代码实现 三、最小路径和3.1、思路3.3、代码实现 总结 前言 动态规划(Dynamic Programming,简称 DP)是一种解决多阶段决策过程最优化问题的方法。它…

卸载及安装docker的教程-ubuntu

一、前言 万地高楼平地起~ 二、环境 OS:Ubuntu 20.04 64 bit 显卡:NVidia GTX 2080 Ti CUDA:11.2 三、卸载docker 1、删除docker及安装时自动安装的所有包 apt-get autoremove docker docker-ce docker-engine docker-ce-*for pkg in …

linux -信号量semphore分析

linux -信号量分析 1 struct semaphore和sema_init1.1 struct semaphore1.2 sema_init 2 down3 up4 down_interruptible5 down_killable6 down_timeout7 down_trylock 基于linux-5.15分析,信号量在使用是是基于spin lock封装实现的。 1 struct semaphore和sema_ini…