【深入日志打印】log.error(“你好{}“, “世界“, e);只有一个占位符是否会打印后面多出的参数呢?(详细跟进源码讲解调试分析)

news2024/11/17 18:24:34

文章目录

  • 【深入日志打印】log.error(“你好{}“, “世界“, e);只有一个占位符是否会打印后面多出的参数呢?(详细跟进源码讲解调试分析)
    • 测试代码
    • 执行结果
    • 调试分析
    • 其他样例探讨

【深入日志打印】log.error(“你好{}“, “世界“, e);只有一个占位符是否会打印后面多出的参数呢?(详细跟进源码讲解调试分析)

测试代码

直接在try里弄一个除以0的报错,然后打印日志,日志字符串里只设置了一个占位符,传2个参数过去,是否会打印两个参数呢?

@Slf4j
public class Demo {
    public static void main(String[] args) {
        try {
            int a = 1 / 0;
        } catch (Exception e) {
            log.error("你好{}", "世界", e);
        }
    }
}

执行结果

先直接公布结果,打印了异常堆栈
在这里插入图片描述

调试分析

我们点开源代码里看看,我们可以看到这个log.error(“你好{}”, “世界”, e);匹配到的方法是下线这个方法,第一个参数为格式字符串,第二个参数为任意参数对象1,第三个参数为任意参数对象2。看着像这后面2个参数是为了提供给格式字符串格式化替换占位符用的,如果格式字符串里只有一个占位符的话,那后面那个参数就不会被格式化进去,也就不会打印,这样的思考是有道理的,那为啥又打印了呢?我们继续往下看。
在这里插入图片描述
我们点开方法实现,发现有四个实现,直接每个实现里代码第一行都加个断点,看看到底进了哪个
(断点是走debug模式启动的,点左侧代码行那添加断点,不懂就百度一下,你就知道。)
在这里插入图片描述
断点进去看到进了
ch.qos.logback.classic.Logger#error(java.lang.String, java.lang.Object, java.lang.Object)
在这里插入图片描述
继续往下走看这个filterAndLog_2方法,你会发现这个Throwable参数是null,那这个时候你会不会就在想,既然异常对象的参数为空,为啥最后又打印了异常堆栈呢?对,我也很好奇,我们继续往下调试,看看堆栈啥时候打印出来的,哪个代码打印的,代码的堆栈数据从哪里来的
在这里插入图片描述

继续往下走我们看到新建了一个LoggingEvent,把这个空的Throwable也传进去了,而我们那个异常对象的e是在params里传进去了
在这里插入图片描述
在这里插入图片描述
继续往下走看这个callAppenders(le)
在这里插入图片描述
遍历Appender对象,执行doAppend方法,传入的参数是LoggingEvent
在这里插入图片描述
这个doAppend有两个实现,都加断点看去了哪个,最后去了ch.qos.logback.core.UnsynchronizedAppenderBase

在这里插入图片描述

在这里插入图片描述
这个UnsynchronizedAppenderBase是个抽象类,append方法又有三个实现,异步Appender、数据库Appender、输出流Appender,最后进了OutputStreamAppender

在这里插入图片描述
又有个subAppend,又有两个实现,输出流的Appender、滚动文件Appender,继续往下追
在这里插入图片描述

在这里插入图片描述
可以看到,由encoder.encode把event编码为字节数组了,然后执行写入字节数组。

在这里插入图片描述

encode又是两个实现,继续往下追断点
在这里插入图片描述

到了layout.doLayout,又是五个实现,
在这里插入图片描述
最后进了TTLLayout里
在这里插入图片描述
我们看下这个格式字符串最终格式化出来的结果,只有你好世界,没有那个e,证明格式字符串做格式化的时候确实是按照我们写的占位符一个,只把第一个参数格式化了,第二个是没有格式化进去的,那为啥还是打印了堆栈呢,我们一步步调试发现是打印这个event.getThrowableProxy()时打印的堆栈信息
在这里插入图片描述

IThrowableProxy tp = event.getThrowableProxy();
        if (tp != null) {
            String stackTrace = tpc.convert(event);
            sb.append(stackTrace);
        }

在这里插入图片描述
有个private ThrowableProxy throwableProxy;可是我就没有赋值过这玩意呀,那我们看下这个玩意哪里赋值的

在这里插入图片描述
我们看下这个LoggingEvent构造方法里,这不就出来了,如果throwable为空,则执行某个导出throwable的方法,我们当时的throwable是null的,重新debug,让代码再次断点到这个构造方法来,进去看看

在这里插入图片描述

断点确实进到了throwable==null的分支里
在这里插入图片描述

这里用了个EventArgUtil.extractThrowable(argArray);从参数里去导出了一个Throwable
在这里插入图片描述
继续看源码,你可以看到,如果参数为空,那就返回null,然后去获取了参数数组里最后一个参数,判定是否为Throwable的实例,如果是就返回这个,否则返回null,到这你就明白了,为啥格式化出来的字符串没有异常堆栈信息,最后却打印了,这是因为在构造LoggingEvent的时候会去看参数数组最后一个参数是不是个异常,是的话就会作为异常存下来给后面打印异常堆栈使用了。
在这里插入图片描述

其他样例探讨

@Slf4j
public class Demo {
    public static void main(String[] args) {
        try {
            int a = 1 / 0;
        } catch (Exception e) {
            log.error("你好{}", e, "世界");
        }
    }
}

打印结果,多出的那个参数是没打印的
在这里插入图片描述

@Slf4j
public class Demo {
    public static void main(String[] args) {
        try {
            int a = 1 / 0;
        } catch (Exception e) {
            log.error("你好{}", "啊", e, "世界");
        }
    }
}

打印结果,异常对象e放到中间也是不会打印的。刚看过源码你是明白的,不是占位符要的参数的话,只判断了最后一个参数。
在这里插入图片描述

@Slf4j
public class Demo {
    public static void main(String[] args) {
        try {
            int a = 1 / 0;
        } catch (Exception e) {
            log.error("你好{}", e);
        }
    }
}

你觉得会不会打印两次e?结果是不会。
在这里插入图片描述
你可以看到源码里写的第二个参数是Throwable,作为异常对象来传过去的,然后调其他方法的时候params参数传的null,这就是为啥格式字符串里占位符没转化为e了。
在这里插入图片描述

最后我补充一点看完了你要学的是分析跟踪的思维,不要死记会不会打印,因为你项目里用的包、版本、实现类、代码可能不同,最后的结果按最后执行的代码来定。

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

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

相关文章

【电能管理】电力物联网仪表/多功能电表/无线计量/多回路计量/分项计量/终端感知设备/全电量参数测量/正反向有功无功测量

什么是物联网电表!!! 安科瑞薛瑶瑶18701709087 物联网电表是智能电表的一种,可以用无线通信方式来操控,除了拥有电度表的有点以外,还可以把硬件和软件联合起来发挥更大的作用。 物联网电表主要用于计量低…

UOS、Linux下的redis的详细部署流程(适用于内网)

提示:适用于Linux以及UOS等内外网系统服务器部署。 文章目录 一.上传离线包二.部署基本环境三.解压并安装redis四.后台运行redis五.uos系统可能遇到的问题六.总结 一.上传离线包 1.自己去Redis官网下载适配自己部署系统的redis安装包。 2.通过文件传输工具&#xf…

微信平台会员卡应用源码系统 带完整的安装代码包以及搭建教程

在移动互联网时代,消费者对于便捷、个性化的服务需求日益增长。微信会员卡作为一种创新的营销方式,不仅能为消费者提供便捷的会员服务,还能帮助商家更好地管理会员信息,提升营销效果。然而,许多商家由于缺乏技术支持&a…

钡铼技术R40工业4G路由器为户外广告牌智能控制系统提供无线网络

钡铼技术R40工业4G路由器在户外广告牌智能控制系统中的应用,为广告行业带来了革命性的变革。作为一种先进的无线通信设备,R40工业4G路由器通过其稳定的信号传输和强大的网络连接能力,为户外广告牌的智能控制系统提供了可靠的无线网络支持&…

蓝桥杯day14刷题日记

P8707 [蓝桥杯 2020 省 AB1] 走方格 思路&#xff1a;很典型的动态规划问题&#xff0c;对于偶数格特判&#xff0c;其他的正常遍历一遍&#xff0c;现在所处的格子的方案数等于左边的格子的方案数加上上面格子的方案数之和 #include <iostream> using namespace std; …

北京朝阳办理广播电视节目制作经营许可证材料和要求

北京经典世纪集团有限公司-资 质代办 尊敬的客户&#xff0c;您对于办理广播电视节目制作经营许可证的需求我们深感关切。作为专 业的资 质代办机构&#xff0c;我们的目标是为您提供一站式服务&#xff0c;帮助您高效顺利地完成所有办理程序。&#xff08;游览器搜经典世纪胡云…

【竞技宝】国足4比1大胜新加坡,武磊独造三球记首功

国足在本轮世预赛主场跟新加坡狭路相逢,这场比赛对于主帅伊万科维奇来说不容有失。因为,国足之前未能在客场击败新加坡,让球队出线前景变得非常严峻。如果,国足还想从36强赛杀出重围,就必须主场战胜新加坡。如果,国足主场都赢不了新加坡,伊万科维奇将面临下课危机。重压之下的伊…

IPv6-基础概念

IPv6基础概念 IPv6技术特点&#xff1a;精简报文格式、实现自动配置和重新编制、支持层次化网络编制、支持端对端安全、更好的支持Qos、支持移动特性。 五元组&#xff1a;源地址&#xff0c;目的地址&#xff0c;源端口&#xff0c;目的端口&#xff0c;协议。 IPv6报头优势…

Abaqus周期性边界代表体单元Random Sphere RVE 3D (Mesh)插件

插件介绍 Random Sphere RVE 3D (Mesh) - AbyssFish 插件可在Abaqus生成三维具备周期性边界条件(Periodic Boundary Conditions, PBC)的随机球体骨料及骨料-水泥界面过渡区(Interfacial Transition Zone, ITZ)模型。即采用周期性代表性体积单元法(Periodic Representative Vol…

【Linux实践室】Linux用户管理实战指南:用户密码管理操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;用户密码存放地及方式2.2 &#x1f514;使用…

批量删除 rabbitmq中随机队列

批量删除 amq.gen–* 随机队列 操作错误产生了无效随机队列&#xff0c;需要批量删除 过滤列出指定amq.gen–队列 # 列出 指定 vhost/qq 以amq.gen开头的所有队列 rabbitmqctl list_queues --vhost / | grep ^amq.gen-# 批量删除队列 #由于list_queues会列出队列名称以及对应…

python实现图片压缩

首先 pip install Pillow compression_level参数&#xff0c;该参数的范围从0到100&#xff0c;其中0表示最小尺寸&#xff08;最高压缩&#xff09;&#xff0c;100表示最大质量&#xff08;最小压缩&#xff09;。这个脚本将尝试在保持图片可识别性的同时&#xff0c;尽可能…

解锁TikTok直播专线,提高使用体验

TikTok&#xff0c;作为当今全球最受欢迎的社交媒体平台之一&#xff0c;给商家带来了无限的商机与市场。然而&#xff0c;商家在TikTok的网络体验也面临诸多挑战&#xff0c;例如网络卡顿、直播断线以及账号易被封锁等问题。为解决这些难题&#xff0c;我们推出了TikTok直播专…

系统安装(kuntaiR522 kvm安装)

(1)通过PC1 web连接Server2,给Server2安装rocky-arm64 CLI系统(语言为英文)。 首先是访问server2的IPMI口,访问192.168.2.10, 用户为Admin,密码为Admin@123 登录进去 以HTML5 集成控制台方式打开 插入U盘修改启动项安装系统

逆向案例9--小鹅通视频m3u8内容解密--含简单webpack

视频网址&#xff1a;https://app4nseessp8638.h5.xiaoeknow.com/v2/course/alive/l_65b9e8dfe4b064a83b90e102?type2&app_idapp4nseessp8638&channel_id&res_type4&pro_id&res_idl_65b9e8dfe4b064a83b90e102 按照惯例&#xff0c;刷新网站&#xff0c;搜…

MFC(二)集成基础控件

目录 OnCreateCStatic【标签&#xff0c;图片】CEdit【文本框&#xff0c;密码框&#xff0c;数值框&#xff0c;文本区】CButton【按钮&#xff0c;单选按钮&#xff0c;多选按钮】CComboBox【下拉列表&#xff0c;列表】CSliderCtrl【滑动条】CListCtrl【表格】CAnimateCtrl【…

LangChain核心概念与组件

Chains Chains可以让你按照一定的顺序和逻辑来执行不同的任务。Chains有以下四种类型&#xff1a; 类型作用LLMChain用于在语言模型周围添加一些功能的简单Chain&#xff0c;它由一个PromptTemplate和一个语言模型&#xff08;LLM或chat model&#xff09;组成&#xff0c;它…

FlorisBoard:Android开源键盘的现代化选择

FlorisBoard&#xff1a;Android开源键盘的现代化选择 简介 FlorisBoard是一款免费且开源的安卓键盘&#xff0c;适用于Android 7.0及以上版本的设备。它的现代化设计和用户友好的界面使其在众多键盘应用中脱颖而出。FlorisBoard的独特之处在于它注重用户体验的同时&#xff0…

ArcGIS二次开发(一)——搭建开发环境以及第一个简单的ArcGIS Engine 程序

Arcgis10.2、Arcgis Engine10.2与Microsoft Visual Studio 2012的版本进行安装 1、推荐教程与安装包2、安装顺序3、安装成功测试VS新建项目可以创建ArcGIS项目&#xff0c;并且在VS中拖拽ArcGIS工具 4、搭建第一个简单的ArcGIS Engine 程序 ArcEngine和VS版本是有对应的&#x…

如何高效系统地自学 Python?

导言&#xff1a; Python作为一门流行的编程语言&#xff0c;被广泛运用于数据分析、人工智能、网络应用等领域。想要系统地自学Python&#xff0c;并掌握其核心概念和编程技能&#xff0c;需要一定的方法和步骤。本文将介绍如何高效系统地自学Python&#xff0c;让你能够快速…