可用于追踪线上 Java服务 运行时信息的神器 BTrace,你们经常用吗 ?

概述

生产环境中的服务可能会出现各种问题,但总不能让服务下线来专门排查错误,这时候最好有一些手段来获取程序运行时信息,比如 接口方法参数/返回值、外部调用情况 以及 函数执行时间等信息以便定位问题。传统的日志记录方式的确可以,但有时非常麻烦,甚至可能需要重启服务,因此代价太大,这时可以借助一个牛批的工具:BTrace

BTrace 可用于动态跟踪正在运行的 Java程序,其原理是通过动态地检测目标应用程序的类并注入跟踪代码 ( “字节码跟踪” ),因此可以直接用于监控和追踪线上问题而无需修改业务代码并重启应用程序。

BTrace 的使用方式是用户自己编写符合 BTrace使用语法的脚本,并结合btrace命令,来获取应用的一切调用信息,就像下面这样:

<btrace>/bin/btrace <PID> <trace_script>
  • 其中 <PID>为被监控 Java应用的 进程ID
  • <trace_script> 为 根据需要监控的信息 而自行编写的 Java脚本

本文就来实操一波 BTrace工具的使用,实验环境如下:

  • OS:CentOS 7.4 64bit
  • BTrace版本:1.3.11.3
  • 被追踪的 Java应用:Spring Boot 2.1.1 应用,这里使用我的文章《Spring Boot应用缓存实践之:Ehcache加持》一文中的 Spring Boot工程

BTrace 安装部署

  • 下载 二进制文件并解压

这里我解压到目录:/home/btrace

  • 配置系统环境变量
vim /etc/profileBTRACE_HOME=/home/btrace
export BTRACE_HOME
export PATH=$PATH:$BTRACE_HOME/bin
  • 验证 BTrace安装情况
btrace --version

编译 BTrace源码

  • 克隆源码
git clone git@github.com:btraceio/btrace.git
  • 编译源码
./gradlew build

编译源码

  • 构建完成的生成物路径位于build/libs目录下

[

我们取出构建生成的 jar包供下文使用。


利用btrace追踪 Spring Boot应用例析

首先我们得构造一个 Spring Boot的模拟业务 用于下文被追踪和分析,这里我就使用文章 《Spring Boot应用缓存实践之:Ehcache加持》中的实验工程。

我们在此工程里再添加一个 scripts包,用于放置 btrace 脚本文件:

[

由于 btrace脚本中需要用到 btrace相关的组件和函数库,因此我们还需要在工程的 pom.xml中引入 btrace的依赖,所使用的 jar包就是上文编译生成的 btrace-1.3.11.3.jar

<dependency><groupId>com.sun.btrace</groupId><artifactId>btrace</artifactId><version>1.3.11.3</version>
</dependency>

Talk is cheap ,Show you the code !接下来就用四五个实验来说明一切吧:


0x01 监控方法耗时情况

btrace 脚本:

@BTrace
public class BtraceTest2 {@OnMethod(clazz = "cn.codesheep.springbt_brace.controller.UserController", method = "getUsersByName", location = @Location(Kind.RETURN))public static void getFuncRunTime( @ProbeMethodName String pmn, @Duration long duration) {println( "接口 " + pmn + strcat("的执行时间(ms)为: ", str(duration / 1000000)) ); //单位是纳秒,要转为毫秒}
}

接下来开始运行 btrace脚本来拦截方法的参数,首先我们用 jps命令取到需要被监控的 Spring Boot应用的进程 Id为 27887,然后执行:

/home/btrace/bin/btrace 27887 BtraceTest2.java

这里我总共对 /getusersbyname接口发出了 12次 POST请求,情况如下:

12次请求情况

接下来我们再看看利用btrace脚本监控到的 /getuserbyname接口的执行时间:

12次请求所对应的接口调用时间

这样一对比很明显,从数据库取数据还是需要 花费十几毫秒的,但从缓存读取数据 几乎没有耗时,这就是为什么要让缓存加持于应用的原因!!!


0x02 拦截方法的 参数/返回值

btrace 脚本:

@OnMethod(clazz = "cn.codesheep.springbt_brace.controller.UserController",method = "getUsersByName",location = @Location(Kind.ENTRY)
)
public static void getFuncEntry(@ProbeClassName String pcn, @ProbeMethodName String pmn, User user ) {println("类名: " + pcn);println("方法名: " + pmn);// 先打印入参实体整体信息BTraceUtils.print("入参实体为: ");BTraceUtils.printFields(user);// 再打印入参实体每个属性的信息Field oneFiled = BTraceUtils.field("cn.codesheep.springbt_brace.entity.User", "userName");println("userName字段为: " + BTraceUtils.get(oneFiled, user));oneFiled = BTraceUtils.field("cn.codesheep.springbt_brace.entity.User", "userAge");println("userAge字段为: " + BTraceUtils.get(oneFiled, user));}

接下来开始运行 btrace脚本来拦截方法的参数,首先我们用 jps命令取到需要被监控的java应用的进程 Id为 27887,然后执行:

/home/btrace/bin/btrace -cp springbt_brace/target/classes 27887 BtraceTest4.java

此时正常带参数 {"userName":"codesheep.cn"} 去请求业务接口:POST /getusersbyname,会得到如下输出:

成功拦截到了接口入参

很明显请求参数已经被 btrace给拦截到了

同理,如果想拦截方法的返回值,可以使用如下 btrace脚本:

@OnMethod(clazz = "cn.codesheep.springbt_brace.controller.UserController",method = "getUsersByName",location = @Location(Kind.RETURN)  //函数返回的时候执行,如果不填,则在函数开始的时候执行
)
public static void getFuncReturn( @Return List<User> users ) {println("返回值为: ");println(str(users));
}

运行 btrace命令后,继续请求想要被监控的业务接口,则可以得到类似如下的输出:

成功拦截到了接口返回值


0x03 监控代码是否到达了某类的某一行

btrace 脚本如下:

@BTrace
public class BtraceTest3 {@OnMethod(clazz="cn.codesheep.springbt_brace.service.UserService",method="getUsersByName",location=@Location(value= Kind.LINE, line=28)  // 比如拦截第28行, 28行是从数据库取数据操作)public static void lineTest( @ProbeClassName String pcn, @ProbeMethodName String pmn, int line ) {BTraceUtils.println("ClassName: " + pcn);BTraceUtils.println("MethodName: " + pmn);BTraceUtils.println("执行到的line行数: " + line);}
}

执行 btrace追踪命令

/home/btrace/bin/btrace 28927 BtraceTest3.java

接着用 POSTMAN工具连续发出了对 /getuserbyname接口的 十几次POST请求,由于只有第一次请求没有缓存时才会从数据库读,因此也才会执行到 UserService类的第 28行 !


0x04 监控指定函数中所有外部调用的耗时情况

btrace脚本如下:

@BTrace
public class BtraceTest5 {@OnMethod (clazz = "cn.codesheep.springbt_brace.service.UserService",method = "getUsersByName",location=@Location(value= Kind.CALL, clazz="/.*/", method="/.*/", where = Where.AFTER) )public static void printMethodRunTime(@Self Object self,@TargetInstance Object instance,@TargetMethodOrField String method, @Duration long duration) {if( duration > 5000000 ){  //如果外部调用耗时大于 5ms 则打印出来println( "self: " + self );println( "instance: " + instance );println( method + ",cost:" + duration/1000000 + " ms" );}}}

执行监控命令:

/home/btrace/bin/btrace 28927 BtraceTest5.java

然后再对接口 /getuserbyname发出POST请求,观察监控结果如下:

[](https://raw.githubusercontent.com/hansonwang99/pic/master/springbt-btrace/发现最耗时的外部调用来源于 MyBatis调用.png)发现最耗时的外部调用来源于 MyBatis调用

我们发现最耗时的外部调用来源于 MyBatis调用。


0x05 其他追踪与监控

除了上面四种典型的追踪场景之外,其他的 btrace追踪与监控场景还比如 查看谁调用了System.gc(),调用栈如何,则可以使用如下 btrace脚本进行监控

@BTrace
public class BtraceTest {@OnMethod(clazz = "java.lang.System", method = "gc")public static void onSystemGC() {println("entered System.gc()");jstack();}
}

很明显,因为btrace 内置了一系列诸如 jstack等十分有用的监控命令。

当然最后需要说明的是 btrace内置了很多语法和命令,可以应对很多线上 Java应用监控场景,大家可以去研究一下官方文档


后记

由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!

本文转载自https://www.codesheep.cn/2019/01/17/springbt-btrace/
如有侵权,请联系作者删除

利用神器BTrace 追踪线上 Spring Boot应用运行时信息相关推荐

  1. 企业如何利用网络趋势做好线上营销?

    随着互联网的不断发展,线上营销越来越成为企业营销的重要组成部分.如何利用网络趋势做好线上营销,已经成为各大企业关注的焦点.本文将为大家介绍如何利用网络趋势做好线上营销的方法和技巧. 一.了解网络趋势 ...

  2. Spring Boot————应用启动时的监听机制测试

    引言 本文承接前面的<Spring Boot----Spring Boot启动流程分析>,主要测试一下ApplicationContextInitializer.SpringApplica ...

  3. java报错空指针异常_分析使用Spring Boot进行单元测试时,报出空指针异常

    使用Spring Boot进行单元测试时,发现使用@Autowired注解的类无法自动注入,当使用这个类的实例的时候,报出NullPointerException,即空指针异常. Spring Boo ...

  4. spring boot指定运行环境

    文章目录 spring boot指定运行环境 1. 命令行指定的方式 springboot根据启动命令不同,执行不同配置文件 2. application.properties中添加spring.pr ...

  5. Spring Boot整合Nacos时遇到 java.lang.IllegalStateException: Context has been already given a name 的解决办法

    问题概述 在进行项目开发过程中,通过Spring Boot整合Alibaba Nacos的服务注册与发现和配置中心时,遇到个梗,启动时打印出一串错误日志信息 " Failed to rena ...

  6. spring boot 集成Mybatis时 Invalid bound statement (not found)

    spring boot 集成Mybatis时,运行提示 org.apache.ibatis.binding.BindingException: Invalid bound statement (not ...

  7. 创建Spring boot项目运行出现报错: java: 无效的源发行版: 14

    触发场景:创建Spring boot项目运行出现报错\ 首先在左边的项目中选择pom.xml文件 找到这行,把中间的版本改成自己下载的java版本 我的是1.8,改好后再点maven刷新即可

  8. Spring Boot jar 启动时设置环境参数

    文章目录 1 摘要 2 核心代码 2.1 spring Boot 多环境配置 2.2 spring Boot 项目启动命令 3 Spring boot 简易启动与停止 shell 脚本 3.1 启动脚 ...

  9. 通过btrace排查线上频繁Full GC的case

    概述 又是一次因为线上报警机制开启的排查问题之旅.某日,钉钉机器人疯狂报警: 接着就是申请机器权限去排查问题,既然是频繁Full GC,那我们排查问题的思路就应该是找到引起Full GC的原因.引起频 ...

最新文章

  1. [翻译]MongoDb 架构(MongoDb Architecture)
  2. 【转】Android应用开发allowBackup敏感信息泄露的一点反思
  3. 取某个日期所在周的任意一天日期
  4. Qt工作笔记-树图结构的2种方式,实现右键菜单
  5. 用websocket技术开发的web聊天系统
  6. cpp mqtt paho 使用_MQTT--Paho C Client 的实现和详解
  7. python安装方法_Python如何批量更新已安装的库,为你介绍六种方法!
  8. 多核cpu的特殊中断
  9. 如何更高效地使用 OkHttp
  10. 手写bind_这些手写代码会了吗?少年
  11. 软件评测师教程简介(第二篇-测试技术)
  12. 想要与北上争雄,深圳还有哪些课要补?| DT城数
  13. 大数据对于企业的价值,主要体现在哪几个方面?
  14. 企业邮箱为何不能当作邮件群发工具
  15. 面试官都叫好的Synchronized底层实现,这工资开多少一个月?
  16. zb服务器连接不稳定,绝对惊人!全球服务器处理9.57ZB数据
  17. 水果之王之猕猴桃-系列三(猕猴桃的功能和禁忌)
  18. 计算机和遥感哪个专业好啊,遥感科学与技术专业毕业是干什么的
  19. Enow ICQ上的自白
  20. 双CPU与单CPU有什么区别,有什么优势?

热门文章

  1. 【学术】60%学者反映实验无法复现?Nature给出五招!
  2. 【小白学习PyTorch教程】十一、基于MNIST数据集训练第一个生成性对抗网络
  3. 【深度学习】Yolov5_DeepSort_Pytorch:基于 Yolov5 + Deep Sort 的实时多目标跟踪器
  4. Python 3.9,来了!
  5. “最佳融合通信创新应用平台”认证!2021 年度全球新经济卓越成就奖揭晓
  6. 网易云信联手神州信息,金融视频营业厅被央视点赞
  7. 线上开票系统设计实践
  8. 视频直播技术:最大限度保障流畅性和清晰度
  9. SQL Server表名为添加中括号[]执行出错
  10. LA3971 组装电脑