Java动态,安全追踪工具

在我们日常的开发中,总是难以避免的要解决线上的问题.如果线上的问题我们在本地调试的时候无论调试多少次发现明明本地调用了这个方法呀,怎么线上就是没调呢?还有就是出了问题的时候由于没有打日志,所以不得不去价格logger,然后换个包,然后再重启,然后再调用,如果在用户很多的时候这么搞,无疑面临着巨大的风险,还不得不去处理用户的大量的投诉,在领导面前也只能默默的低着头承受着批评

那么有没有一种方法可以不用重启应用,又可以在线上追踪代码呢? 答案当然是有的,就是使用Btrace这个工具了

BTrace是sun公司推出的一款Java 动态、安全追踪(监控)工具,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数、返回值、全局变量和堆栈信息等,并且做到最少的侵入,占用最少的系统资源。

应用场景:

  1. 服务慢,但是不知道慢在哪一步,哪个函数慢
  2. 谁调用了System.gc(),调用栈如何?
  3. 谁构造了一个超大的ArrayList?
  4. 执行某个方法抛出异常时,分析运行时参数

......

快速开始

btrace的官方的网址是http://github.com/btraceio/btrace , 可以从里面下载最新的版本, 目前版本是1.3.11.2

下载好之后doc里面有很多例子, 我们这里来跑一个UserGuide的例子

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod; import static com.sun.deploy.trace.Trace.println; /**
* @author luozhiyun on 2019-01-06.
*/
@BTrace
public class HelloWorld { @OnMethod(clazz="java.lang.Thread", method="start")
public static void onThreadStart() {
println("thread start!"); } }

然后在要监控的机器上打jps, 找到应用的pid, 然后 btrace $pid HelloWorld.java就可以跑起来了

如果还想要监控其他内容,直接修改HelloWorld.java的内容,然后再执行一次btrace就可以了,完全不需要重启应用!!!

拦截

1.直接指出类和方法 ,如HelloWorld的例子

2.正则表达式拦截

如下, 拦截所有的有关InputStream类的readXXX方法,正则表达式要写在两个/中间

package com.sun.btrace.samples;

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*; /**
* This BTrace class demonstrates that we can
* probe into multiple classes and methods by a
* single probe specification using regular
* expressions for class and/or method names as
* given below. In the example, we put probe into
* all readXXX methods of all InputStream classes.
*/
@BTrace public class MultiClass {
@OnMethod(
clazz="/java\\.io\\..*Input.*/",
method="/read.*/"
)
public static void onread(@ProbeClassName String pcn) {
println("read on " + pcn);
}
}

3.拦截所有有这个注解的类或方法

如: @OnMethod( clazz="@javax.jws.WebService", method="@javax.jws.WebMethod" )

4.拦截接口的实现类

如:@OnMethod( clazz="+java.lang.Runnable", method="run" )

5.拦截构造器

@OnMethod(clazz="java.net.ServerSocket", method="<init>")

6.拦截静态内部类

@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello") 只要在类和内部类前面加上$

拦截时机

Kind.ENTRY

在@onMethod这个注解里面可以加上一个location参数,表示拦截的这个方法的拦截时机,如果不加,默认就是Kind.ENTRY

Kind.RETURN

如果要获得返回结果或执行时间, 那么就要加上Kind.RETURN.

OnMethod(clazz = "java.net.ServerSocket", method = "getLocalPort", location = @Location(Kind.RETURN))
public static void onGetPort(@Return int port, @Duration long duration) duration的单位是纳秒,要除以 1,000,000 才是毫秒。

Kind.Error, Kind.Throw和 Kind.Catch

这几个主要用于异常情况的跟踪

在拦截函数的参数定义里注入一个Throwable的参数,代表异常。

@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(Kind.ERROR))
public static void onBind(Throwable exception, @Duration long duration)

Kind.Line

下例监控代码是否到达了Socket类的第363行

@OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))

public static void onBind4() {

   println("socket bind reach line:363");

}

Kind.Call

下例就是打印出run()方法里面调用的所有其他方法

import com.sun.btrace.annotations.*;

import static com.sun.btrace.BTraceUtils.println;

/**
* @author luozhiyun on 2019-01-06.
*/
@BTrace
public class HelloWorld { @OnMethod(clazz="BtraceCase", method="run",
location =@Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
public static void onThreadStart(@Self Object self, @TargetInstance Object instance,
@TargetMethodOrField String method, @Duration long duration) {
println("self: " + self);
println("instance: " + instance);
println("method: " + method);
println("duration: " + duration); }
}

BtraceCase

import java.util.Random;

/**
* @author luozhiyun on 2019-01-06.
*/
public class BtraceCase {
public static Random random = new Random();
public int size; public static void main(String[] args) throws Exception {
Thread.sleep(1000*15);
new BtraceCase().run();
} public void run() throws Exception {
while (true) {
add(random.nextInt(10), random.nextInt(10));
}
} public int add(int a, int b) throws Exception {
Thread.sleep(random.nextInt(10) * 100);
return a + b;
}
}

打印结果:

self: BtraceCase@63947c6b
instance: BtraceCase@63947c6b
method: add
duration: 402211413
self: BtraceCase@63947c6b
instance: java.util.Random@2b193f2d
method: nextInt
duration: 2602

所调用的类及方法名所注入到@TargetInstance与 @TargetMethodOrField中.如果想获得执行时间,必须把Where定义成AFTER

打印this,参数与返回值

import com.sun.btrace.AnyType;

@OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN))

public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)

Self:

如果是静态函数,self为空 , 用@Self可以表示当前方法,也就是this

参数:

参数列表要么不要定义, 要定义就要定义完整,否则BTrace无法处理不同参数的同名函数

结果:

结果的类型用AnyType来定义,特别是用正则表达式匹配多个函数的时候,连void都可以表示

一点经验

  1. 用于匹配方法入参或返回类型时,因嫌麻烦不想引入外部依赖(一般也没有必要),外部类型请用AnyType代替,而不是Object!因为你可能用Object来准确匹配方法返回参数或返回类型。

  2. 启动跟踪脚本时,请使用和启动Java进程相同的Linux账号,不然会因为权限问题而attach失败。

  3. 另一个和BTrace类似的Java诊断工具greys-anatomy,由阿里释出,感兴趣的也可以学习一下。

  4. 若报错"Port 2020 unavailable.",则使用btrace -p 2021 ...来指定其它端口。

  5. Linux下已经有个命令也叫btrace,注意别用混了。

  6. 由于Btrace会把脚本逻辑直接侵入到运行的代码中,所以在使用上做很多限制:

    1、不能创建对象

    2、不能使用数组

    3、不能抛出或捕获异常

    4、不能使用循环

    5、不能使用synchronized关键字

    6、属性和方法必须使用static修饰

    根据官方声明,不恰当的使用BTrace可能导致JVM崩溃,如在BTrace脚本使用错误的class文件,所以在上生产环境之前,务必在本地充分的验证脚本的正确性。

Java动态,安全追踪工具的更多相关文章

  1. java动态编译笔记

    1 前言 Java的动态编译知识,真真在实际开发中并不是经常遇到.但是学习java动态编译有助于我们从更深一层次去了解java.对掌握jdk的动态代理模式,这样我们在学习其他一些开源框架的时候就能够知 ...

  2. Java动态代理全面分析

    代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1  主题:规定代理类和真实对象共同对外暴露的接口: 2  代理类:专门代理真实对象的类: 3 ...

  3. 推荐6款常用的Java开源报表制作工具

    JasperReports是一个基于Java的开源报表工具,它可以在Java环境下像其它IDE报表工具一样来制作报表.JasperReports 支持PDF.HTML.XLS.CSV和XML文件输出格 ...

  4. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  5. [转]java动态代理(JDK和cglib)

    转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理 ...

  6. 彻底理解JAVA动态代理

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 代理模式的结构如下图所示. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public ...

  7. 四种java代码静态检查工具

    [转载]常用 Java 静态代码分析工具的分析与比较 转载自 开源中国社区 http://www.oschina.net/question/129540_23043       1月16日厦门 OSC ...

  8. Java 动态眨眼 EyesJPanel (整理)

    /** * Java 动态眨眼 EyesJPanel (整理) * * 2016-1-2 深圳 南山平山村 曾剑锋 * 注意事项: * 1.本程序为java程序,同时感谢您花费宝贵的时间来阅读本文档: ...

  9. Java动态代理简单应用

    概念 代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象.这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色. Java动态代理比 ...

随机推荐

  1. Java:HashMap原理与设计缘由

    前言 Java中使用最多的数据结构基本就是ArrayList和HashMap,HashMap的原理也常常出现在各种面试题中,本文就HashMap的设计与设计缘由作出一一讲解,并解答面试常见的一些问题. ...

  2. Linux/windows com串口 java 接收数据 并解析 web程序

    1.首先应公司要求再 com 口本来使用 .net 由于 .net 适用 linux 太麻烦 改为java 准备工作 准备 RXTXconmm.jar(版本很重要) 因为版本问题我搞了一天. 主要讲述 ...

  3. Jenkins+Python+GitLab持续集成

    创建任务 登录Jenkins,点击左侧列表的新建选项.输入任务名称,选择构建一个自由风格的软件项目,点击确定. 配置 在任务配置界面,可以设置General标签中的丢弃旧的构建选项,设置保持构建的天数 ...

  4. GO代码生成代码小思小试

    推进需求 GO 项目,可整体生成一个运行文件到处跑,是极爽之事.但如果有资源文件要得带着跑,则破坏了这种体验. 例如下边这个项目结构,resource 目录下为资源文件,main.go 中会通过路径引 ...

  5. MySQL索引的数据结构-B+树介绍

    目录 一.树 二.B+树 2.1 B+树性质 三.聚集索引和辅助索引 3.1 聚集索引 3.2 辅助索引 3.3 聚集索引和非聚集索引的区别 四.再看B+树 4.1 B+树的插入操作 4.2 B+树的 ...

  6. 自己实现定制自己的专属java锁,来高效规避不稳定的第三方

    java juc 包下面已经提供了很多并发锁工具供我们使用,但在日常开发中,为了各种原因我们总是会用多线程来并发处理一些问题,然而并不是所有的场景都可以使用juc 或者java本身提供的锁来方便的帮助 ...

  7. Java学习笔记之---static

    Java学习笔记之---static static不能修饰类,局部变量 (一)静态成员的特征 static+属性  静态属性 无论实例化出来多少个对象,都会共用同一块静态空间,类对象共享 静态成员从第 ...

  8. 9.22考试 crf的视察 题解

    这道题当时第一反应就是一道典型的NOIP第一题的难度,绝对要A掉,不然分数一定会被拉开. 然后就开始分析,暴力是一开始想的是用二维树状数组打加上暴力枚举长度,然而这道题满足二分性质,所以时间复杂度就是 ...

  9. 【题解】搬书-C++

    搬书 Description 陈老师桌上的书有三堆,每一堆都有厚厚的一叠,你想逗一下陈老师,于是你设计一个最累的方式给他,让他把书 拿下来给同学们.若告诉你这三堆分别有i,j,k本书,以及每堆从下到上 ...

  10. csv文件数据导出到mongo数据库

    from pymongo import MongoClientimport csv# 创建连接MongoDB数据库函数def connection(): # 1:连接本地MongoDB数据库服务 co ...