在日常开发里,大家应该都遇到过这种情况:线上出问题,日志信息不够,定位特别慢。传统手段不是要加日志重新发版,就是要远程 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 会自动注入日志增强。
使用场景
线上只想看某个方法的参数和结果,快速定位异常
不想频繁改业务代码、发版
遇到偶发性问题,需要临时开关调试
这种调试注入器就像个“隐形摄像头”,你想看的时候开一下,排完问题再关掉,不影响线上业务。