BTrace案例

wen java案例 4

BTrace 实战案例详解

BTrace 是 Java 动态追踪工具,可以在不重启应用的情况下对运行中的 Java 程序进行安全的热插拔监控。

BTrace案例

环境准备

# 下载 BTrace
wget https://github.com/btraceio/btrace/releases/download/v2.2.0/btrace-v2.2.0-bin.tar.gz
tar -xzf btrace-v2.2.0-bin.tar.gz
# 配置环境变量
export BTRACE_HOME=/path/to/btrace-v2.2.0
export PATH=$PATH:$BTRACE_HOME/bin
# 查看帮助
btrace -h

基础案例集合

案例 1:方法调用耗时统计

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MethodTimeTracer {
    // 计时用的线程局部变量
    @TLS
    private static long startTime;
    // 监控指定方法
    @OnMethod(
        clazz = "com.example.service.UserService",
        method = "getUserById",
        location = @Location(Kind.ENTRY)
    )
    public static void onEntry(@ProbeMethodName String methodName) {
        startTime = timeMillis();
        println("=== Enter " + methodName + " ===");
        println("Start Time: " + startTime);
    }
    @OnMethod(
        clazz = "com.example.service.UserService",
        method = "getUserById",
        location = @Location(Kind.RETURN)
    )
    public static void onReturn(@ProbeMethodName String methodName,
                                @Duration long duration) {
        println("=== Exit " + methodName + " ===");
        println("Cost: " + (timeMillis() - startTime) + " ms");
        println("Duration: " + duration + " ns");
        println("================");
    }
}

案例 2:参数和返回值跟踪

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MethodArgTracer {
    @OnMethod(
        clazz = "com.example.service.OrderService",
        method = "createOrder",
        location = @Location(Kind.ENTRY)
    )
    public static void traceEntry(@Self Object instance,
                                  String orderId,
                                  double amount,
                                  int userId) {
        println("=== Create Order ===");
        println("Instance: " + instance.getClass().getName());
        println("OrderId: " + orderId);
        println("Amount: " + amount);
        println("UserId: " + userId);
    }
    @OnMethod(
        clazz = "com.example.service.OrderService",
        method = "createOrder",
        location = @Location(Kind.RETURN)
    )
    public static void traceReturn(@Return boolean result) {
        println("Create Order Result: " + result);
        println("===================");
    }
    // 捕获异常
    @OnMethod(
        clazz = "com.example.service.OrderService",
        method = "createOrder",
        location = @Location(Kind.ERROR)
    )
    public static void traceError(@Thrown Throwable e) {
        println("Create Order Error: " + e);
        printlnStack();
    }
}

案例 3:线程信息监控

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ThreadMonitor {
    // 监控线程启动
    @OnMethod(
        clazz = "java.lang.Thread",
        method = "start"
    )
    public static void onThreadStart(@Self Thread thread) {
        println("=== Thread Started ===");
        println("Name: " + thread.getName());
        println("Priority: " + thread.getPriority());
        println("======================");
    }
    // 定期打印所有线程状态
    @OnTimer(10000) // 每10秒执行一次
    public static void printThreadInfo() {
        println("=== Thread Dump ===");
        Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
        for (Thread t : threads) {
            if (t.isAlive()) {
                println("Thread: " + t.getName() + 
                       ", State: " + t.getState() + 
                       ", Daemon: " + t.isDaemon());
            }
        }
    }
    // 监控线程池
    @OnMethod(
        clazz = "java.util.concurrent.ThreadPoolExecutor",
        method = "execute"
    )
    public static void onTaskSubmit(@Self ThreadPoolExecutor executor) {
        println("=== Task Submitted ===");
        println("Active Count: " + executor.getActiveCount());
        println("Queue Size: " + executor.getQueue().size());
        println("Pool Size: " + executor.getPoolSize());
        println("Core Pool Size: " + executor.getCorePoolSize());
        println("Max Pool Size: " + executor.getMaximumPoolSize());
        println("Completed Tasks: " + executor.getCompletedTaskCount());
    }
}

高级案例

案例 4:内存和 GC 监控

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class MemoryMonitor {
    // 每5秒打印一次内存使用
    @OnTimer(5000)
    public static void printMemory() {
        println("=== Memory Usage ===");
        Runtime runtime = Runtime.getRuntime();
        long total = runtime.totalMemory();
        long free = runtime.freeMemory();
        long used = total - free;
        long max = runtime.maxMemory();
        println("Total: " + formatMemory(total));
        println("Used: " + formatMemory(used));
        println("Free: " + formatMemory(free));
        println("Max: " + formatMemory(max));
        println("Usage: " + (used * 100 / max) + "%");
        println("====================");
    }
    private static String formatMemory(long bytes) {
        return (bytes / (1024 * 1024)) + " MB";
    }
    // 监控对象分配
    @OnMethod(
        clazz = "java.lang.OutOfMemoryError",
        method = "<init>"
    )
    public static void onOOM(@Self OutOfMemoryError e) {
        println("!!! OutOfMemoryError !!!");
        println(e.getMessage());
        printlnStack();
        // 触发一次完整的堆转储
        jstack();
    }
    // 监控垃圾回收
    @OnMethod(
        clazz = "java.lang.System",
        method = "gc"
    )
    public static void onGC() {
        println("=== System.gc() called ===");
        printMemory();
    }
}

案例 5:HTTP 请求监控(Spring Boot)

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class HttpMonitor {
    @TLS
    private static String requestId;
    // 监控 Controller 层
    @OnMethod(
        clazz = "org.springframework.web.servlet.DispatcherServlet",
        method = "doDispatch"
    )
    public static void onRequest(@Self Object servlet,
                                 @ProbeMethodName String method) {
        requestId = Strings.strcat("Req-", String.valueOf(timeMillis()));
        println("=== " + requestId + " ===");
        println("Request received at: " + new Date());
    }
    @OnMethod(
        clazz = "org.springframework.web.servlet.DispatcherServlet",
        method = "doDispatch",
        location = @Location(Kind.RETURN)
    )
    public static void onRequestReturn(@Duration long duration) {
        println("Request completed in: " + (duration / 1000000) + " ms");
        println("======================");
    }
    // 监控特定接口
    @OnMethod(
        clazz = "com.example.controller.UserController",
        method = "/api/users"
    )
    public static void onUserApi(@Self Object controller,
                                 @ProbeMethodName String method) {
        println("=== User API Called ===");
        println("Controller: " + controller.getClass().getName());
        println("Method: " + method);
    }
}

案例 6:JDBC 连接监控

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class JDBCMonitor {
    private static Map<Object, Object> connectionMap = new HashMap<Object, Object>();
    // 监控 Connection 创建
    @OnMethod(
        clazz = "com.mysql.cj.jdbc.ConnectionImpl",
        method = "<init>"
    )
    public static void onConnectionCreate(@Self Object connection) {
        println("=== Connection Created ===");
        println("Connection: " + connection);
        connectionMap.put(identity(connection), currentTime());
    }
    // 监控 SQL 执行
    @OnMethod(
        clazz = "com.mysql.cj.jdbc.StatementImpl",
        method = "executeQuery"
    )
    public static void onQuery(@Self Object statement,
                               String sql) {
        println("=== SQL Query ===");
        println("SQL: " + sql);
        println("Time: " + currentTime());
        // 获取连接信息
        if (statement != null) {
            println("Statement: " + statement.getClass().getName());
        }
    }
    // 监控连接关闭
    @OnMethod(
        clazz = "com.mysql.cj.jdbc.ConnectionImpl",
        method = "close"
    )
    public static void onConnectionClose(@Self Object connection) {
        println("=== Connection Closed ===");
        println("Connection: " + connection);
        connectionMap.remove(identity(connection));
        println("Active Connections: " + connectionMap.size());
    }
    // 定期检查活跃连接
    @OnTimer(60000)
    public static void checkConnections() {
        println("=== Active Connections: " + connectionMap.size() + " ===");
        if (connectionMap.size() > 10) {
            println("WARNING: Too many active connections!");
        }
    }
}

案例 7:监控特定异常

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ExceptionMonitor {
    // 监控所有异常
    @OnMethod(
        clazz = "java.lang.Throwable",
        method = "<init>"
    )
    public static void onException(@Self Throwable t) {
        String className = t.getClass().getName();
        // 过滤特定异常
        if (className.contains("BusinessException") ||
            className.contains("SystemException")) {
            println("=== Exception Created ===");
            println("Type: " + className);
            println("Message: " + t.getMessage());
            // 直接打印堆栈
            jstack();
            // 简单的计数器
            incrementAndPrint("ExceptionCount");
        }
    }
    @Property
    private static long exceptionCount;
    private static void incrementAndPrint(String name) {
        exceptionCount++;
        println(name + ": " + exceptionCount);
    }
    // 监控异常抛出点
    @OnMethod(
        clazz = "/com\\.example\\..*/",
        method = "/.*/"
    )
    public static void traceMethod(@ProbeClassName String className,
                                   @ProbeMethodName String methodName,
                                   @Self Object instance) {
        if (instance instanceof Throwable) {
            println("Throwing in: " + className + "." + methodName);
        }
    }
}

实用脚本模式

案例 8:性能瓶颈分析

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class PerformanceAnalyzer {
    @TLS
    private static Map<String, Long> methodTimes = new HashMap<String, Long>();
    @TLS
    private static Map<String, Integer> methodCounts = new HashMap<String, Integer>();
    @OnMethod(
        clazz = "/com\\.example\\..*/",
        method = "/.*/"
    )
    public static void traceMethod(@ProbeClassName String className,
                                   @ProbeMethodName String methodName) {
        String key = className + "." + methodName;
        methodCounts.put(key, 
            methodCounts.containsKey(key) ? 
            methodCounts.get(key) + 1 : 1);
        if (methodCounts.get(key) % 100 == 0) {
            println("Method " + key + " called " + 
                   methodCounts.get(key) + " times");
        }
    }
    // 定期输出热点方法
    @OnTimer(60000)
    public static void printHotspots() {
        println("=== Method Call Counts ===");
        for (Map.Entry<String, Integer> entry : methodCounts.entrySet()) {
            if (entry.getValue() > 100) {
                println(entry.getKey() + ": " + entry.getValue() + " calls);
            }
        }
    }
}

案例 9:线上问题定位

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class ProblemDiagnoser {
    // 监控死锁
    @OnTimer(30000)
    public static void detectDeadlock() {
        println("=== Deadlock Detection ===");
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        if (deadlockedThreads != null) {
            println("!!! DEADLOCK DETECTED !!!");
            ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(deadlockedThreads);
            for (ThreadInfo info : threadInfo) {
                println("Thread: " + info.getThreadName());
                println("State: " + info.getThreadState());
                println("Lock: " + info.getLockName());
                println("==================================");
            }
        }
    }
    // 监控 CPU 使用率高的线程
    @OnTimer(10000)
    public static void monitorCPU() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] threadIds = threadMXBean.getAllThreadIds();
        for (long id : threadIds) {
            long cpuTime = threadMXBean.getThreadCpuTime(id);
            if (cpuTime > 1000000000) { // CPU时间超过1秒
                ThreadInfo threadInfo = threadMXBean.getThreadInfo(id);
                println("High CPU Thread: " + threadInfo.getThreadName());
                println("CPU Time: " + (cpuTime / 1000000000) + "s");
                printlnThreadStack(threadInfo);
            }
        }
    }
}

执行案例

# 查找进程 ID
jps -l
# 执行 BTrace 脚本(阻塞模式)
btrace <pid> MethodTimeTracer.java
# 后台执行
btrace -p <port> <pid> MethodTimeTracer.java &
# 使用退出命令
btrace -o /tmp/result.txt <pid> MethodTimeTracer.java
# 带参数的执行
btrace -d <classpath> <pid> MethodTimeTracer.java

注意事项

# 1. 生产环境安全模式(只读操作)
btrace -c <pid> MethodTimeTracer.java
# 2. 限制输出
btrace -w <pid> MethodTimeTracer.java  # 只输出到当前窗口
btrace -s <pid> MethodTimeTracer.java  # 同步模式
# 3. 优雅退出
jcmd <pid> BTrace.stop

这些案例覆盖了 BTrace 的主要应用场景,实际使用时需要根据业务场景调整类名、方法名和监控逻辑,在使用生产环境时,建议先在测试环境验证脚本的正确性和安全性。

抱歉,评论功能暂时关闭!