如何向Quarkus OpenTelemetry扩展的VertexTracesDelegator注入自定义逻辑?
解决方案:无需修改VertexTracesDelegator即可拦截Trace事件
1. 利用OpenTelemetry标准SpanProcessor扩展点
Quarkus OpenTelemetry基于官方OpenTelemetry SDK,你可以通过自定义SpanProcessor来全局拦截所有Trace Span的开始与结束事件,完全不需要接触内部的VertexTracesDelegator类:
实现
io.opentelemetry.sdk.trace.SpanProcessor接口,重写核心方法处理事件:public class CustomCpuSpanProcessor implements SpanProcessor { @Override public void onStart(Context parentContext, ReadWriteSpan span) { // 记录Span启动时的CPU时间 long cpuStartTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(); span.setAttribute("cpu.start.nanos", cpuStartTime); } @Override public boolean isStartRequired() { return true; } @Override public void onEnd(ReadableSpan span) { // 计算并记录CPU耗时 long cpuEndTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(); long cpuStartTime = span.getAttribute("cpu.start.nanos", 0L); span.setAttribute("cpu.duration.nanos", cpuEndTime - cpuStartTime); } @Override public boolean isEndRequired() { return true; } }在Quarkus中注册自定义处理器:通过
OpenTelemetryCustomizer扩展点注入,避免直接操作SDK实例:import io.quarkus.arc.Priority; import io.quarkus.opentelemetry.runtime.config.OpenTelemetryConfig; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; @ApplicationScoped public class OpenTelemetryConfigurer { @Produces @Priority(100) // 确保优先级高于默认处理器 public OpenTelemetryCustomizer customSpanProcessorCustomizer(OpenTelemetryConfig config) { return openTelemetrySdkBuilder -> { openTelemetrySdkBuilder.setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor(new CustomCpuSpanProcessor()) .build() ); }; } }
2. 为什么无需修改VertexTracesDelegator
VertexTracesDelegator是Quarkus OpenTelemetry的内部桥接类,负责Vert.x与OpenTelemetry的Trace逻辑适配,但所有Trace事件最终都会流经OpenTelemetry SDK的标准化扩展点(如SpanProcessor),因此直接基于SDK扩展即可覆盖所有场景,包括Vert.x生成的Trace。
3. 针对Vert.x顶点的精准拦截方案
如果只需要拦截特定Vert.x顶点的执行逻辑,也可以用Quarkus拦截器实现,无需修改内部类:
- 定义拦截器绑定注解:
import jakarta.interceptor.InterceptorBinding; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @InterceptorBinding @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface TraceCpuMeasurement { } - 实现拦截器并关联注解:
import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; import java.lang.management.ManagementFactory; @Interceptor @TraceCpuMeasurement public class CpuMeasurementInterceptor { @AroundInvoke public Object measureCpu(InvocationContext ctx) throws Exception { long startTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(); try { return ctx.proceed(); } finally { long endTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(); // 将CPU耗时附加到当前Trace Span var currentSpan = io.opentelemetry.api.trace.Span.current(); if (currentSpan != null && currentSpan.isRecording()) { currentSpan.setAttribute("vertex.cpu.duration.nanos", endTime - startTime); } } } } - 在自定义Vert.x顶点类上添加
@TraceCpuMeasurement注解即可生效。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

