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. Python文件中将print的输出内容重定向到变量中

    有时候需要用到别人的代码, 但是又不想修改别人的文件, 想拿到输出的结果, 这时候就需要使用sys模块, 将print输出的内容重定向到变量中. Python调用sys模块中的sys.stdout, ...

  2. jmeter性能测试前及测试后

    压测前:           1.压力测试两种场景:                    1)单场景,压测单个接口. 2)混合场景,多个接口关联压测. 2.压测时间:                ...

  3. 【HDU - 3085】Nightmare Ⅱ(bfs)

    -->Nightmare Ⅱ 原题太复杂,直接简单的讲中文吧 Descriptions: X表示墙 .表示路 M,G表示两个人 Z表示鬼 M要去找G但是有两个鬼(Z)会阻碍他们,每一轮都是M和G ...

  4. java集合框架中的去重问题

    对于自定义的类来说,必须要重写hashcode和equals方法 hashcode方法的作用是确定元素在数据结构中的位置,当两个元素的hash值一样时,需要用equals方法判断两个元素是否是一样的, ...

  5. Jsoup配合 htmlunit 爬取异步加载的网页

    加入 jsoup 和 htmlunit 的依赖 <dependency> <groupId>org.jsoup</groupId> <artifactId&g ...

  6. scrapy基础知识之 RedisCrawlSpider:

    这个RedisCrawlSpider类爬虫继承了RedisCrawlSpider,能够支持分布式的抓取.因为采用的是crawlSpider,所以需要遵守Rule规则,以及callback不能写pars ...

  7. .Net知识大全(个人整理)

    .Net知识大全 本章内容适用于对.NET有一定基础的或者是想通过本文章对.NET基础知识记不清楚的朋友,可以通过本文章进行回顾. 面试的时候可能也会遇到相应的题目,建议面试前进行回顾!!! 1.NE ...

  8. Android实现跳转到应用市场进行版本更新功能

    最近需要做应用版本更新功能,因为之前已经写过一篇版本更新的功能了,虽然请求接口还是用的HttpUrlConnection,想着改改现在应用使用的请求方式也挺快的嘛,心里开始暗喜,可以偷偷懒了,哈哈哈. ...

  9. Java 垃圾收集总结

    概述 垃圾收集(Garbage Collection,GC),它不是Java语言的伴生产物,它的历史比Java还要久远. 人们主要思考GC需要完成的3件事情: 哪些内存需要回收? 什么时候回收? 如何 ...

  10. 基于SpringCloud的Microservices架构实战案例-架构拆解

    自第一篇< 基于SpringCloud的Microservices架构实战案例-序篇>发表出来后,差不多有半年时间了,一直也没有接着拆分完,有如读本书一样,也是需要契机的,还是要把未完成的 ...