Java异常链不支持尾部追加监控指标,但可通过自定义MonitoredException携带维度标签,在全局异常处理器中结合Micrometer打点,并利用MDC透传或OpenTelemetry增强追踪。
java 中异常链本身不直接支持“在尾部追加监控指标”,但可以通过封装异常、结合监控 sdk(如 micrometer、skywalking、opentelemetry)或自定义异常上下文,在异常传播的终点(如全局异常处理器)提取并上报带维度的监控数据。关键不是修改异常链结构,而是让异常携带可被监控系统识别的元信息,并在合适的时机触发指标打点。
用自定义异常包装器携带监控维度
定义一个继承 RuntimeException(或 Exception)的包装类,内部持有一组业务维度标签(如 service、endpoint、errorType、tenantId):
public class MonitoredException extends RuntimeException {
private final Map<String, String> tags = new HashMap<>();
<pre class="brush:php;toolbar:false;">public MonitoredException(String message, Throwable cause) {
super(message, cause);
}
public MonitoredException tag(String key, String value) {
this.tags.put(key, value);
return this;
}
public Map<String, String> getTags() {
return Collections.unmodifiableMap(tags);
}}
抛出时链式添加维度:
throw new MonitoredException("DB timeout", e)
.tag("service", "order-service")
.tag("endpoint", "/api/v1/order/create")
.tag("tenant_id", "t-789");在全局异常处理器中统一打点
Spring Boot 项目常用 @ControllerAdvice 捕获异常。此处检查是否为 MonitoredException,提取标签并调用 Micrometer 的 Counter 或 Timer:
立即学习“Java免费学习笔记(深入)”;
@ControllerAdvice
public class GlobalExceptionHandler {
<pre class="brush:php;toolbar:false;">private final Counter errorCounter;
private final Timer errorTimer;
public GlobalExceptionHandler(MeterRegistry registry) {
this.errorCounter = Counter.builder("app.exception.count")
.description("Count of business exceptions with dimensions")
.register(registry);
this.errorTimer = Timer.builder("app.exception.latency")
.description("Latency of exception occurrence")
.register(registry);
}
@ExceptionHandler(MonitoredException.class)
public ResponseEntity<String> handleMonitoredException(
HttpServletRequest request,
MonitoredException ex) {
// 提取自定义维度
Map<String, String> tags = ex.getTags();
String errorCode = Optional.ofNullable(ex.getCause())
.map(Throwable::getClass)
.map(Class::getSimpleName)
.orElse("Unknown");
// 打点:带多维标签的计数器
errorCounter.tag("error_code", errorCode)
.tags(tags)
.increment();
// 可选:记录异常耗时(需配合前置时间戳)
errorTimer.record(Duration.ofMillis(System.currentTimeMillis() - ...));
return ResponseEntity.status(500).body(ex.getMessage());
}}
利用 MDC 实现跨线程/异步链路透传维度
若异常发生在异步线程(如 @Async、CompletableFuture),原始 MDC 上下文会丢失。可在抛出异常前将维度写入 MDC,并确保全局处理器读取:
- 在业务入口处:
MDC.put("tenant_id", "t-789"); MDC.put("endpoint", "/order/create"); - 在
MonitoredException构造时自动捕获当前 MDC:new HashMap(MDC.getCopyOfContextMap()) - 全局处理器中优先使用异常自带的
tags,缺失时 fallback 到 MDC
与 OpenTelemetry 集成做错误追踪增强
如果已接入 OpenTelemetry,可在异常被捕获时向当前 Span 添加属性,并触发事件:
Span.current().setAttribute("error.service", "order-service");
Span.current().setAttribute("error.tenant_id", "t-789");
Span.current().addEvent("exception_caught", Attributes.of(
stringKey("error.type"), ex.getClass().getSimpleName(),
stringKey("error.message"), ex.getMessage()
));这类属性会随 trace 一起上报到后端(如 Jaeger、Zipkin),和监控指标形成交叉验证——指标看聚合趋势,trace 看单条根因。


















