聊聊LogbackMDCAdapter
序
本文主要研究一下LogbackMDCAdapter
MDCAdapter
org/slf4j/spi/MDCAdapter.java
public interface MDCAdapter {/*** Put a context value (the <code>val</code> parameter) as identified with* the <code>key</code> parameter into the current thread's context map. * The <code>key</code> parameter cannot be null. The <code>val</code> parameter* can be null only if the underlying implementation supports it.* * <p>If the current thread does not have a context map it is created as a side* effect of this call.*/public void put(String key, String val);/*** Get the context identified by the <code>key</code> parameter.* The <code>key</code> parameter cannot be null.* * @return the string value identified by the <code>key</code> parameter.*/public String get(String key);/*** Remove the context identified by the <code>key</code> parameter.* The <code>key</code> parameter cannot be null. * * <p>* This method does nothing if there is no previous value * associated with <code>key</code>.*/public void remove(String key);/*** Clear all entries in the MDC.*/public void clear();/*** Return a copy of the current thread's context map, with keys and * values of type String. Returned value may be null.* * @return A copy of the current thread's context map. May be null.* @since 1.5.1*/public Map<String, String> getCopyOfContextMap();/*** Set the current thread's context map by first clearing any existing * map and then copying the map passed as parameter. The context map * parameter must only contain keys and values of type String.* * Implementations must support null valued map passed as parameter.* * @param contextMap must contain only keys and values of type String* * @since 1.5.1*/public void setContextMap(Map<String, String> contextMap);/*** Push a value into the deque(stack) referenced by 'key'.* * @param key identifies the appropriate stack* @param value the value to push into the stack* @since 2.0.0*/public void pushByKey(String key, String value);/*** Pop the stack referenced by 'key' and return the value possibly null.* * @param key identifies the deque(stack)* @return the value just popped. May be null/* @since 2.0.0*/public String popByKey(String key);/*** Returns a copy of the deque(stack) referenced by 'key'. May be null.* * @param key identifies the stack* @return copy of stack referenced by 'key'. May be null.* * @since 2.0.0*/public Deque<String> getCopyOfDequeByKey(String key);/*** Clear the deque(stack) referenced by 'key'. * * @param key identifies the stack* * @since 2.0.0*/public void clearDequeByKey(String key);}
slf4j定义了MDCAdapter接口,该接口定义了put、get、remove、clear、getCopyOfContextMap、setContextMap、pushByKey、popByKey、getCopyOfDequeByKey、clearDequeByKey方法
LogbackMDCAdapter
ch/qos/logback/classic/util/LogbackMDCAdapter.java
public class LogbackMDCAdapter implements MDCAdapter {// BEWARE: Keys or values placed in a ThreadLocal should not be of a type/class// not included in the JDK. See also https://jira.qos.ch/browse/LOGBACK-450final ThreadLocal<Map<String, String>> readWriteThreadLocalMap = new ThreadLocal<Map<String, String>>();final ThreadLocal<Map<String, String>> readOnlyThreadLocalMap = new ThreadLocal<Map<String, String>>();private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();//......}
LogbackMDCAdapter实现了MDCAdapter接口,它基于readWriteThreadLocalMap、readOnlyThreadLocalMap、threadLocalMapOfDeques来实现
readWriteThreadLocalMap
public void put(String key, String val) throws IllegalArgumentException {if (key == null) {throw new IllegalArgumentException("key cannot be null");}Map<String, String> current = readWriteThreadLocalMap.get();if (current == null) {current = new HashMap<String, String>();readWriteThreadLocalMap.set(current);}current.put(key, val);nullifyReadOnlyThreadLocalMap();}@Overridepublic String get(String key) {Map<String, String> hashMap = readWriteThreadLocalMap.get();if ((hashMap != null) && (key != null)) {return hashMap.get(key);} else {return null;}}@Overridepublic void remove(String key) {if (key == null) {return;}Map<String, String> current = readWriteThreadLocalMap.get();if (current != null) {current.remove(key);nullifyReadOnlyThreadLocalMap();}}@Overridepublic void clear() {readWriteThreadLocalMap.set(null);nullifyReadOnlyThreadLocalMap();}private void nullifyReadOnlyThreadLocalMap() {readOnlyThreadLocalMap.set(null);} public void setContextMap(Map contextMap) {if (contextMap != null) {readWriteThreadLocalMap.set(new HashMap<String, String>(contextMap));} else {readWriteThreadLocalMap.set(null);}nullifyReadOnlyThreadLocalMap();}
put、get、remove、clear、setContextMap都是基于readWriteThreadLocalMap,同时修改操作会同时调用nullifyReadOnlyThreadLocalMap,将readOnlyThreadLocalMap设置为null
getCopyOfContextMap
public Map getCopyOfContextMap() {Map<String, String> readOnlyMap = getPropertyMap();if (readOnlyMap == null) {return null;} else {return new HashMap<String, String>(readOnlyMap);}}public Map<String, String> getPropertyMap() {Map<String, String> readOnlyMap = readOnlyThreadLocalMap.get();if (readOnlyMap == null) {Map<String, String> current = readWriteThreadLocalMap.get();if (current != null) {final Map<String, String> tempMap = new HashMap<String, String>(current);readOnlyMap = Collections.unmodifiableMap(tempMap);readOnlyThreadLocalMap.set(readOnlyMap);}}return readOnlyMap;}
getCopyOfContextMap方法通过getPropertyMap获取,如果不为null则新创建HashMap返回;getPropertyMap先从readOnlyThreadLocalMap读取,如果readOnlyThreadLocalMap为null则从readWriteThreadLocalMap拷贝一份unmodifiableMap,并设置到readOnlyThreadLocalMap
threadLocalMapOfDeques
@Overridepublic void pushByKey(String key, String value) {threadLocalMapOfDeques.pushByKey(key, value);}@Overridepublic String popByKey(String key) {return threadLocalMapOfDeques.popByKey(key);}@Overridepublic Deque<String> getCopyOfDequeByKey(String key) {return threadLocalMapOfDeques.getCopyOfDequeByKey(key);}@Overridepublic void clearDequeByKey(String key) {threadLocalMapOfDeques.clearDequeByKey(key);}
pushByKey、popByKey、getCopyOfDequeByKey、clearDequeByKey均是基于threadLocalMapOfDeques,它是ThreadLocalMapOfStacks类型
ThreadLocalMapOfStacks
org/slf4j/helpers/ThreadLocalMapOfStacks.java
public class ThreadLocalMapOfStacks {// BEWARE: Keys or values placed in a ThreadLocal should not be of a type/class// not included in the JDK. See also https://jira.qos.ch/browse/LOGBACK-450final ThreadLocal<Map<String, Deque<String>>> tlMapOfStacks = new ThreadLocal<>();public void pushByKey(String key, String value) {if (key == null)return;Map<String, Deque<String>> map = tlMapOfStacks.get();if (map == null) {map = new HashMap<>();tlMapOfStacks.set(map);}Deque<String> deque = map.get(key);if (deque == null) {deque = new ArrayDeque<>();}deque.push(value);map.put(key, deque);}public String popByKey(String key) {if (key == null)return null;Map<String, Deque<String>> map = tlMapOfStacks.get();if (map == null)return null;Deque<String> deque = map.get(key);if (deque == null)return null;return deque.pop();}public Deque<String> getCopyOfDequeByKey(String key) {if (key == null)return null;Map<String, Deque<String>> map = tlMapOfStacks.get();if (map == null)return null;Deque<String> deque = map.get(key);if (deque == null)return null;return new ArrayDeque<String>(deque);}/*** Clear the deque(stack) referenced by 'key'. * * @param key identifies the stack* * @since 2.0.0*/public void clearDequeByKey(String key) {if (key == null)return;Map<String, Deque<String>> map = tlMapOfStacks.get();if (map == null)return;Deque<String> deque = map.get(key);if (deque == null)return;deque.clear();}}
ThreadLocalMapOfStacks是slf4j定义的,基于
ThreadLocal<Map<String, Deque<String>>>实现的
小结
slf4j定义了MDCAdapter接口,该接口定义了put、get、remove、clear、getCopyOfContextMap、setContextMap、pushByKey、popByKey、getCopyOfDequeByKey、clearDequeByKey方法;LogbackMDCAdapter实现了MDCAdapter接口,它基于readWriteThreadLocalMap、readOnlyThreadLocalMap、threadLocalMapOfDeques来实现,其中put、get、remove、clear、setContextMap都是基于readWriteThreadLocalMap,pushByKey、popByKey、getCopyOfDequeByKey、clearDequeByKey均是基于threadLocalMapOfDeques。
相关文章:
聊聊LogbackMDCAdapter
序 本文主要研究一下LogbackMDCAdapter MDCAdapter org/slf4j/spi/MDCAdapter.java public interface MDCAdapter {/*** Put a context value (the <code>val</code> parameter) as identified with* the <code>key</code> parameter into the cur…...
spring命名空间注入和XML自动装配、引入外部配置文件
Spring p命名空间注入util命名空间注入基于XML的自动装配根据名称自动装配 Spring引入外部属性配置文件 p命名空间注入 作用:简化配置。 使用p命名空间注入的前提条件包括两个: ● 第一:在XML头部信息中添加p命名空间的配置信息:…...
【2024年11月份--2024精灵云校招C++笔试题】
考试形式 笔试考了三道算法题,笔试形式为阅读题目,然后用中文给出算法思路,最后给出算法例程,分数各占一半,简单,中等,复杂各一道题。我看9月份有人也是考这3道题,一模一样。 第…...
Visual Studio 2019下编译OpenCV 4.7 与OpenCV 4.7 contrib
一、环境 使用的环境是Win10,Visual Studio 2019,Cmake3.28,cdua 11.7,cudnn 8.5,如果只是在CPU环境下使用,则不用安装CUDA。要使用GPU处理,安装好CUDA之后,要测试安装的CUDA是否能用。不能正常使用的话,添加一下系统…...
【Linux网络】系统调优之聚合链路bonding,可以实现高可用和负载均衡
一、什么是多网卡绑定 二、聚合链路的工作模式 三、实操创建bonding设备(mode1) 1、实验 2、配置文件解读 3、查看bonding状态,验证bonding的高可用效果 三、nmcli实现bonding 一、什么是多网卡绑定 将多块网卡绑定同一IP地址对外提供服务…...
k8s持久化存储PV、PVC
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次&a…...
CocosCreator3.8原生引擎源码研究
1. Cocos Creator引擎架构图 2. 原始引擎源码流程图 下图中包含Android native层引擎到js适配层的启动和主循环的启用流程和必要说明,本猿比较懒,暂时不细述了,各位看官直接看图吧,还在细化扩充,后续逐渐更新。。。 版…...
高二英语上
unit 1 1.yarn三种意思 1.码; 2.庭院,天井; 3.花园;down**down 在这里是介词,也可以作副词,与 down 相对的是 up。请比较下列两句: 1.Look! Hes driving down the street . 2.Look! Hes driving up the street .这两例…...
JavaWeb Day10 案例 准备工作
目录 一、需求说明 二、环境搭建 (一)数据库 (二)后端 ①controller层 1.DeptController.java 2.EmpController.java ②mapper层 1.DeptMapper.java 2.EmpMapper.java ③pojo层 1.Dept.java 2.Emp.jav…...
Nginx:不同域名访问同一台机器的不同项目
Nginx很简单就可以解决同一台机器同时跑两个或者多个项目,而且都通过域名从80端口走。 以Windows环境下nginx服务为例,配置文件nginx.conf中,http中加上 include /setup/nginx-1.20.1/conf/conf.d/*.conf;删除server部分,完整如…...
C++(20):new数组时元素个数自动推到
C20在new数组时可以根据初始化列表,自动推到元素个数: #include <iostream> using namespace std;int main() {int *pd new int[]{1,2,3,4};for(auto i 0; i < 4; i){cout<<pd[i]<<endl;}return 0; }运行程序输出: 1…...
使用visualStudio发布可执行文件
编译成功后会在程序项目的路径下创建一个debug文件夹和一个release文件夹 文件夹中的具体文件入下所示 生成32位的可执行文件 32位的可执行文件可以在64位的计算机中执行,而64位的操作系统程序只能在64位的计算机中执行安装运行库的安装包根据电脑的版本选择合适的…...
yolo系列报错(持续补充ing)
文章目录 export GIT_PYTHON_REFRESHquiet解决 没有pt权重文件解决 python文件路径报错解决 读取文件列名报错解决 导入不同文件夹出错解决 megengine没有安装解决然后你发现它竟然还没有用 export GIT_PYTHON_REFRESHquiet 设置环境变量 GIT_PYTHON_REFRESH ,这个…...
Technology Strategy Patterns 学习笔记9 - bringing it all together
1 Patterns Map 2 Creating the Strategy 2.1 Ansoff Growth Matrix 和owth-share Matrix 区别参见https://fourweekmba.com/bcg-matrix-vs-ansoff-matrix/ 3 Communicating...
Redis(12)| 过期删除策略和内存淘汰策略
Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。 如何设置过期时间 先说一下对 key 设置过期时间的命令。 设置 key 过期时间的命令一共有 4 个: expire key n&#x…...
Go-服务注册和发现,负载均衡,配置中心
文章目录 什么是服务注册和发现技术选型 Consul 的安装和配置1. 安装2. 访问3. 访问dns Consul 的api接口go操作consulgrpc下的健康检查grpc的健康检查规范动态获取可用端口号 负载均衡策略1. 什么是负载均衡2. 负载均衡策略1. 集中式load balance2. 进程内load balance3. 独立…...
k8s-实验部署 1
1、k8s集群部署 更改所有主机名称和解析 开启四台实验主机,k8s1 仓库;k8s2 集群控制节点; k8s3 和k8s4集群工作节点; 集群环境初始化 使用k8s1作为仓库,将所有的镜像都保存在本地,不要将集群从外部走 仓库…...
Git的原理与使用(一)
目录 Git初始 Git安装 Git基本操作 创建git本地仓库 配置git 工作区,暂存区,版本库 添加文件,提交文件 查看.git文件 修改文件 版本回退 小结 Git初始 git是一个非常强大的版本控制工具.可以快速的将我们的文档和代码等进行版本管理. 下面这个实例看理解下为什么需…...
1204. 错误票据
题目: 1204. 错误票据 - AcWing题库 思路: 将输入的数据存入数组,从小到大排序后遍历,若 (a[i] a[i - 1])res1 a[i]--->重号;若(a[i] - a[i - 1] > 2)res2 a[i] - 1--->断号。 难点:题目只告诉我们输入…...
uniapp中在组件中使用被遮挡或层级显示问题
uniapp中在组件中使用或croll-view标签内使用uni-popup在真机环境下会被scroll-view兄弟元素遮挡,在开发环境下和安卓系统中可以正常显示,但在ios中出现了问题 看了许多文章都没有找到问题的原因,最后看到这一个文章http://t.csdnimg.cn/pvQ…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
