跳到主要内容

Spring-Boot-Online-debug

在日常开发里,大家应该都遇到过这种情况:线上出问题,日志信息不够,定位特别慢。传统手段不是要加日志重新发版,就是要远程 debug,但这些方式都不太优雅。SpringBoot 项目里可以搞个“无痕调试注入器”,做到随时加观察点、随时撤销,而且不用动核心业务逻辑。

从注解切面开始

先来个轻量级方案:写一个注解 @DebugPoint,配合 Spring AOP 就能把入参、出参和耗时打出来,基本上满足大部分排查需求。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DebugPoint {
    String value() default "";
}

切面代码如下:

@Aspect
@Component
public class DebugAspect {

    private static final Logger log = LoggerFactory.getLogger(DebugAspect.class);

    @Around("@annotation(debugPoint)")
    public Object around(ProceedingJoinPoint pjp, DebugPoint debugPoint) throws Throwable {
        String method = pjp.getSignature().toShortString();
        log.info("[DEBUG] 方法: {}, 标签: {}, 参数: {}", 
                method, debugPoint.value(), Arrays.toString(pjp.getArgs()));

        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long cost = System.currentTimeMillis() - start;

        log.info("[DEBUG] 方法: {}, 耗时: {}ms, 返回: {}", method, cost, result);
        return result;
    }
}

在业务方法上加注解:

@Service
public class OrderService {

    @DebugPoint("订单创建流程")
    public String createOrder(String userId, String productId) {
        return "order-" + UUID.randomUUID();
    }
}

启动应用后,调用 createOrder 就能在日志里看到完整的调试信息,而业务逻辑完全不用改。

再进一步:热插拔 Agent

如果你想做到“不改源码也能打点”,就要上 Java Agent 了。利用 Instrumentation + ByteBuddy,可以在 JVM 里动态修改字节码,实现运行时增强。DebugAgent.java

public class DebugAgent {
    public static void premain(String args, Instrumentation inst) {
        new AgentBuilder.Default()
            .type(ElementMatchers.nameContains("OrderService"))
            .transform((builder, type, cl, module) -> 
                builder.method(ElementMatchers.any())
                       .intercept(Advice.to(DebugAdvice.class)))
            .installOn(inst);
    }
}

DebugAdvice.java

public class DebugAdvice {

    @Advice.OnMethodEnter
    public static void onEnter(@Advice.Origin String method, 
                               @Advice.AllArguments Object[] args) {
        System.out.println("[Agent-Enter] 方法: " + method + ", 参数: " + Arrays.toString(args));
    }

    @Advice.OnMethodExit
    public static void onExit(@Advice.Origin String method, 
                              @Advice.Return Object result) {
        System.out.println("[Agent-Exit] 方法: " + method + ", 返回: " + result);
    }
}

打包成 agent.jar 后,用 JVM 参数挂上:-javaagent:/path/to/agent.jar 这时候即使没有 @DebugPoint 注解,OrderService 里的方法也会被增强,日志会自动打印,真正做到“线上临时加点”。

SpringBoot Demo 骨架

一个最小可运行的 Demo 项目目录大概是这样的:

demo-debug/
 ├── src/main/java/com/example/debug/
 │    ├── DebugPoint.java       # 注解
 │    ├── DebugAspect.java      # AOP切面
 │    ├── OrderService.java     # 业务服务
 │    ├── DemoApplication.java  # 启动类
 ├── agent/
 │    ├── DebugAgent.java       # Java Agent入口
 │    ├── DebugAdvice.java      # 增强逻辑

启动方式两种:

  • 普通启动:直接跑 SpringBoot 项目,用注解调试。
  • 带 agent 启动:加 -javaagent 参数,Agent 会自动注入日志增强。

使用场景线上

  • 只想看某个方法的参数和结果,快速定位异常
  • 不想频繁改业务代码、发版
  • 遇到偶发性问题,需要临时开关调试

这种调试注入器就像个“隐形摄像头”,你想看的时候开一下,排完问题再关掉,不影响线上业务。