当前位置: 首页 > news >正文

Log4j-tag丢失

一、引言

        最近有个线上日志丢失tag的问题,是组内封装了后置请求的拦截器把请求的响应结果存到ClickHouse里面去,但是日志总有一些tag丢失。

        作者提出父级线程的threadlocal被清空,同事认为可能是threadlocal的弱引用在gc的时候被回收。两种想法其实都是有可能的,那就要一个个验证。这里要感谢ld和cbc的共同排查、讨论,最后锁定原因是父级线程的MDC被ar组件清空。

二、问题

1、架构背景

        这里先讲讲问题后面架构的背景,由于需要按照特定的格式存一些日志数据到ck,组内封装的后置拦截器会使用slf4j的MDC.getCopyOfContextMap()获取请求被存储在ThreadLocal里面的map,里面放了一些订单、人员信息的key-value,拿到这些数据再和一些响应参数存入ck。

        集团的日志框架里面实现了servlet的Filter,在servlet容器初始化的时候会运行这个拦截器,把一部分埋点运行,但是会清空,保证进入业务处理的时候是干净的。真正用到的买点是在业务线程里面实现的,所以在业务处理完成到后置拦截器的过程中可能存在clear的操作。需要把框架整体结构研究下。

2、问题背景

        在落数据的监控,看到有几列偶现是空的。

        看代码确认这些空列都是从MDC取出来的。

三、分析

        快速的讨论锁定了出问题的代码。

1、代码

        这个代码是从父线程取出来日志信息,作为入参返回给子runnable,等到子runnable启动的时候,它就变成了子线程里面的threadlocal。

@Overridepublic void apply(PostResponseFilterArgs args) {try {Runnable logTask = () -> doLog(args);if (CommonLogConfig.logAsync()&& StringUtilsExt.isNotBlank(CommonLogConfig.logExecutor())) {TaskRunner.create(LogTagsWrap.wrap(logTask)).start(CommonLogConfig.logExecutor());} else {logTask.run();}} catch (Throwable e) {LOGGER.info(TITLE, e);}}
public static Runnable wrap(Runnable runnable) {Map<String, String> logTags = log.getCommonTags();return () -> {log.setCommonTags(logTags);runnable.run();};}private static void doLog(PostResponseFilterArgs args) {// 构建业务信息Map<String, String> map = LogUtil.buildService(args);// 设置MDC信息LogUtil.setMdc(map);// 记录CK日志Throwable throwable = args.executionResult().throwable();if (Objects.nonNull(throwable)) {LogUtil.error(map, throwable, CommonLogConfig.logScenario());} else {LogUtil.info(map, null, CommonLogConfig.logScenario());}}

  2、浅拷贝✖️

        作者一开始以为这里父级线程取出来的可能是浅拷贝,然后在子线程启动之前被其他请求使用了,导致并发清空。但是追溯下去,MDC使用的是深拷贝拿到日志信息。

@Overridepublic Map<String, String> getCopy() {final StringMap map = localMap.get();return map == null ? new HashMap<>() : map.toMap();}@Overridepublic Map<String, String> getCopy() {final Map<String, String> map = localMap.get();return map == null ? new HashMap<>() : new HashMap<>(map);}
public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);}else if (s > threshold)resize();for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {K key = e.getKey();V value = e.getValue();putVal(hash(key), key, value, false, evict);}}}

3、子级threadlocal在gc时回收✖️  

        不过子线程里面一定是在set mdc信息的时候是空的,也就引出来开头作者和同事的两个猜想。threallocal作者感觉是不太可能的,因为线程执行完才算是强引用链结束,但是还是写个测试方法试一试。

        使用system.gc强制回收,看看会不会把threadlocal给回收,结果并没有。这里要设置启动参数,不然system.gc不一定会执行。

public static void main(String[] args) {List<Task> list = new ArrayList<>();for (int i = 0; i < 10000;i++) {
System.out.println(i + "start");Map<String, String> md = new HashMap<>();md.put("orderid",i + "666");// 设置MDC信息MDC.setContextMap(md);int finalI = i;Runnable logTask = () -> {
// 构建SOA信息Map<String, String> map = new HashMap<>();map.put("soa", finalI + "666");System.gc();// 设置MDC信息LogUtil.setMdc(map);System.out.println(map);if (map.size() == 1) {
System.out.println(finalI + "soaerror");}
};Task t = TaskRunner.create(LogTagsWrap.wrap(logTask));list.add(t);System.out.println(i + "end");}
for (Task t : list) {
t.start(CommonLogConfig.logExecutor());}
}

4、父级MDC信息清空☑️

        那么接下来就是看会不会清空父级线程的threadlocal了,作者把框架整体看了一遍。感觉出问题的地方还不少,就看这些拦截器是不是会异步走清理。

         cbc使用过arthas进行线上诊断,作者也学习了一波,使用watch对全限定名的类方法进行字节码动态生成,打印方法的出入参便于排查。

        但是作者这里的情况比较特殊,由于需要确定父级丢失还是子级的gc回收,需要在两个方法里面取出MDC的log信息,在为空的时候打印出来。作者和cbc折腾了许久,比较复杂的监控arthas还是不好用的。就在这时候ld给出了新的方向。

5、ar组件清空父级MDC信息☑️

        他发现出问题的机器ip固定在某两台机器上面,于是我们开始分析这两天机器与其他机器有什么不同。cbc之前接触过测开组做了一个ar组件,使用java-agengt方法在服务发布时注入,这是我们观察不到但是有可能进行拦截增强的代码。

        于是我们开始研究他的源码,果然这个组件使用了 SerivceInstrumentation extends TypeInstrumentation类进行字节码增强,并且是在业务处理的方法外层进行增强,比后置拦截器早。

@Overrideprotected ElementMatcher<TypeDescription> typeMatcher() {return named("server.OperationHandler");}private static void recordMocker(Mocker mocker) {// 业务处理MDC.clear();}

        他的过程可以画成这样。

        关掉ar组件之后,可以看到没有再发生有问题的ck日志记录,了解到ar组件的执行频率是一小时10次,这也说明了为什么我们发现的监控是部份发生,其实这个组件导致了必然的结果。

        一切的偶然总有必然的原因!

 四、总结

        再次抒感:一切的偶然总有必然的原因!这次的问题告诉了我们,问题不一定出现在看得见的代码,java-ageng之类看不见的代码很多时候是另外一个方向。

        这里再次感谢ld和cbc的协作分析,合作无间!

相关文章:

Log4j-tag丢失

一、引言 最近有个线上日志丢失tag的问题&#xff0c;是组内封装了后置请求的拦截器把请求的响应结果存到ClickHouse里面去&#xff0c;但是日志总有一些tag丢失。 作者提出父级线程的threadlocal被清空&#xff0c;同事认为可能是threadlocal的弱引用在gc的时候被回收。两种想…...

代码随想录算法训练营第五十六天|1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和 动态规划

1143. 最长公共子序列 int longestCommonSubsequence(char * text1, char * text2){int len1 strlen(text1);int len2 strlen(text2);int dp[len11][len21];for (int i 0; i < len1; i){for (int j 0; j < len2; j){dp[i][j] 0;}}for (int i 1; i < len1; i){f…...

虚拟机和Windows的文件传输

拖拽/复制粘贴 直接将虚拟机linux系统的文件拖曳到windows桌面&#xff0c;或者直接将windows的文件拖曳到虚拟机linux系统当中&#xff0c;可以实现文件传输。当然复制粘贴方式也可以&#xff0c;但是前提是需要下载安装好VMware tools。 共享文件夹 概念&#xff1a;在Win…...

leetcode分类刷题:二叉树(八、二叉搜索树特有的自顶向下遍历)

二叉搜索树是一个有序树&#xff1a;每个二叉树都满足左子树上所有节点的值均小于它的根节点的值&#xff0c;右子树上所有节点的值均大于它的根节点的值&#xff1b;利用该性质&#xff0c;可以实现二叉搜索树特有的自顶向下遍历 700. 二叉搜索树中的搜索 思路1、自顶向下的遍…...

Vue 插槽 组件插入不固定内容

定义好一个组件&#xff0c;如果想插入图片或视频这非常不好的控制应该显示什么&#xff0c;这个时候可以使用插槽插入自定义内容 默认插槽 <Login><template><h1>我是插入的内容</h1></template></Login >组件 <slot></slot>…...

webpack打包时配置环境变量

webpack打包时配置环境变量 一、常规环境变量配置1. 使用webpack.DefinePlugin定义全局常量2. 在Vue静态页面中使用该环境变量 二、纯静态文件配置环境变量1. 使用npm或yarn安装html-webpack-plugin2. 在Webpack配置中引入并使用插件3. 使用htmlwebpackplugin.options方式配置环…...

【c++|opencv】一、基础操作---3.访问图像元素

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 访问图像元素 1. 访问图像像素 1.1 访问某像素 //灰度图像&#xff1a; image.at<uchar>(j, i) //j为行数&#xff0c;i为列数 //BGR彩色图像 i…...

机器视觉3D项目评估的基本要素及测量案例分析

目录 一. 检测需求确认 1、产品名称&#xff1a;【了解是什么产品上的零件&#xff0c;功能是什么】 2、*产品尺寸&#xff1a;【最大兼容尺寸】 3、*测量项目&#xff1a;【确认清楚测量点位】 4、*精度要求&#xff1a;【若客户提出的精度值过大或者过小&#xff0c;可以和客…...

力扣日记10.31-【栈与队列篇】前 K 个高频元素

力扣日记&#xff1a;【栈与队列篇】前 K 个高频元素 日期&#xff1a;2023.10.31 参考&#xff1a;代码随想录、力扣 347. 前 K 个高频元素 题目描述 难度&#xff1a;中等 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意…...

TensorFlow案例学习:简单的音频识别

前言 以下内容均来源于官方教程&#xff1a;简单的音频识别&#xff1a;识别关键字 音频识别 下载数据集 下载地址&#xff1a;http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip 可以直接浏览器访问下载。 下载完成后将其解压到项目…...

css小程序踩坑记录

写标签设置距离 一直设置不动 写个双层 设置动了 神奇 好玩...

Android sqlite分页上传离线订单后删除

1、判断订单表的的总数是否大于0&#xff0c;如果大于0开始上传订单 public int getOrderCount() {String query "SELECT COUNT(*) FROM " TABLE_NAME;Cursor cursor db.rawQuery(query, null);int count 0;if (cursor.moveToFirst()) {count cursor.getInt(0);…...

Flask基本教程以及Jinjia2模板引擎简介

flask基本使用 直接看代码吧&#xff0c;非常容易上手&#xff1a; # 创建flask应用 app Flask(__name__)# 路由 app.route("/index", methods[GET]) def index():return "FLASK&#xff1a;欢迎访问主页&#xff01;"if __name__ "__main__"…...

Django实战项目-学习任务系统-兑换物品管理

接着上期代码框架&#xff0c;开发第5个功能&#xff0c;兑换物品管理&#xff0c;再增加一个学习兑换物品表&#xff0c;主要用来维护兑换物品&#xff0c;所需积分&#xff0c;物品状态等信息&#xff0c;还有一个积分流水表&#xff0c;完成任务奖励积分&#xff0c;兑换物品…...

jmeter和postman你选哪个做接口测试?

软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大部分人在做的接口测试&#xff0c;小白变高手…...

mac版本 Adobe总是弹窗提示验证问题如何解决

来自&#xff1a; mac软件下载macsc站 mac电脑使用过程中总是弹出Adobe 的弹窗提示&#xff0c;尤其是打开Adobe的软件&#xff0c;更是频繁的弹出提示&#xff1a; Your Adobe app is not genuine. Adobe reserves the right to disable this software after a 0 grace period…...

钡铼技术ARM工控机在机器人控制领域的应用

ARM工控机是一种基于ARM架构的工业控制计算机&#xff0c;用于在工业自动化领域中进行数据采集、监控、控制和通信等应用。ARM&#xff08;Advanced RISC Machine&#xff09;架构是一种低功耗、高性能的处理器架构&#xff0c;广泛应用于移动设备、嵌入式系统和物联网等领域。…...

HTML+CSS+JS实现计算器

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…...

Git工作原理和常见问题处理方案

博客定位Git工作区域工作区域划分暂存区设计目的 Git基本操作核心操作初始化和配置指令 HEAD指针Git版本回滚指令介绍reset模式reset hard使用场景reset soft使用场景reset mixed使用场景reset使用注意事项checkout使用场景 Git分支管理什么是分支分支应用场景分支相关指令被合…...

C++-实现一个简单的菜单程序

C-实现一个简单的菜单程序 1&#xff0c;if-else语句实现1.1&#xff0c;代码实现1.2&#xff0c;功能检测 2&#xff0c;switch语句实现2.1&#xff0c;代码实现2.2&#xff0c;功能检测 1&#xff0c;if-else语句实现 实现一个简单的菜单程序&#xff0c;运行时显示"Men…...

出海营销决战指南:从“流量过客”到“私域常客”的全局地图

2026 全球出海营销日历&#xff1a;如何在关键节点实现社媒私域流量的指数级增长&#xff1f;2026年&#xff0c;出海战场规则已变。粗放投放的红利耗尽&#xff0c;碎片化的渠道、敏感的风控与难以逾越的文化沟壑&#xff0c;正让每一分营销预算的效能急剧衰减。节点依旧汹涌&…...

零基础一键配置黑苹果:OpCore-Simplify智能工具让复杂变简单

零基础一键配置黑苹果&#xff1a;OpCore-Simplify智能工具让复杂变简单 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为黑苹果配置时面对满屏代…...

Linux核心转储文件生成与调试全指南

1. Linux核心转储文件调试方法详解1.1 核心转储文件概述在Linux系统下&#xff0c;当程序发生崩溃时&#xff0c;系统会生成一个包含程序崩溃时内存映像的文件&#xff0c;称为core文件。这个文件记录了程序崩溃时的内存状态和调试信息&#xff0c;是定位程序崩溃原因的重要工具…...

ubuntu系统检测内核配置是否支持Docker核心模块

有一些内核缺少 Docker 所需的核心模块&#xff08;overlayfs、bridge、iptables 相关等&#xff09;所以在安装docker之前可以先检查一下。 脚本&#xff0c;可以检测Kernel配置是否符合Docker的运行要求 源地址&#xff1a;https://github.com/moby/moby/blob/master/contr…...

从‘水变油’到‘大师一问三不知’:求实学风如何塑造科学巨匠与避免历史弯路

1. 科学史上的两副面孔&#xff1a;浮夸与求实 1993年&#xff0c;一场名为"水变油"的闹剧在国内掀起轩然大波。某"发明家"声称发明了能将水转化为燃料的"神奇添加剂"&#xff0c;甚至获得了部分政府部门的支持。这个明显违背能量守恒定律的&quo…...

解锁AMD锐龙隐藏性能:SMUDebugTool深度调校实战指南

解锁AMD锐龙隐藏性能&#xff1a;SMUDebugTool深度调校实战指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitc…...

SpringBoot3 + JetCache实战:如何用两级缓存把接口性能提升10倍?

SpringBoot3 JetCache实战&#xff1a;高并发场景下的缓存架构设计与性能优化 在电商秒杀、实时数据查询等高并发场景中&#xff0c;传统数据库直接承受流量冲击往往会导致系统崩溃。去年双十一期间&#xff0c;某头部电商平台通过多级缓存架构成功扛住了每秒百万级的查询请求…...

【PAT甲级真题】- Is It a Binary Search Tree (25)

题目来源 Is It a Binary Search Tree (25) 题目描述点击链接自行查看 注意点&#xff1a; 这里的二叉搜索树大于等于插到右边 思路简介 一道二叉树模板题&#xff08;6202年了应该不会还有人不会写二叉树吧bushi &#xff09; 一开始想到前序遍历不可能确定一棵树还以为题目…...

开源风扇控制工具FanControl全攻略:从问题诊断到散热方案优化

开源风扇控制工具FanControl全攻略&#xff1a;从问题诊断到散热方案优化 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Tren…...

智能变电站实战:如何用SCL配置文件搞定IED设备联调(附避坑案例)

智能变电站实战&#xff1a;SCL配置文件在IED设备联调中的关键作用与避坑指南 在智能变电站的工程实施中&#xff0c;设备联调环节往往成为项目进度的"拦路虎"。据统计&#xff0c;超过60%的现场调试时间消耗在IED设备间的通信配置问题上。SCL&#xff08;Substation…...