自動計装はJava OpenTelemetry agentによる自動計装 で簡単にできた。 手動計装もドキュメントにしたがってやれば簡単に導入できるが、古めのservletにどうやって実装すればいいかはよくわからなかったのでメモ

2024-06-03 追記 自動計装+必要なところに手動計装っていう組み合わせができた Java OpenTelemetry 自動計装と手動計装を組み合わせてトレースを取得する

依存関係追加

Instrumentation | OpenTelemetry にあるライブラリを追加する

pom.xml

+       <dependencyManagement>
+               <dependencies>
+                       <dependency>
+                               <groupId>io.opentelemetry</groupId>
+                               <artifactId>opentelemetry-bom</artifactId>
+                               <version>1.38.0</version>
+                               <type>pom</type>
+                               <scope>import</scope>
+                       </dependency>
+               </dependencies>
+       </dependencyManagement>
+
        <dependencies>
+               <dependency>
+                       <groupId>io.opentelemetry</groupId>
+                       <artifactId>opentelemetry-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>io.opentelemetry</groupId>
+                       <artifactId>opentelemetry-sdk</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>io.opentelemetry</groupId>
+                       <artifactId>opentelemetry-exporter-otlp</artifactId>
+               </dependency>
+               <dependency>
+                       <!-- 使っているservletのバージョンにあわせる -->
+                       <groupId>io.opentelemetry.instrumentation</groupId>
+                       <artifactId>opentelemetry-servlet-3.0</artifactId>
+                       <version>1.6.2-alpha</version>
+               </dependency>
+               <dependency>
+                       <!-- Not managed by opentelemetry-bom -->
+                       <groupId>io.opentelemetry.semconv</groupId>
+                       <artifactId>opentelemetry-semconv</artifactId>
+                       <version>1.25.0-alpha</version>
+               </dependency>
+
 

SDKを初期化

io.opentelemetry.api.OpenTelemetry を初期化し、 buildAndRegisterGlobal()GlobalOpenTelemetry にsetする。 初期化は一回だけで、二回目以降setしようとすると怒られる。

やり方はいろいろあるが、ここではsingletonで初期化した。

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.ResourceAttributes;
 
public class OpenTelemetryManager {
    private static final OpenTelemetryManager INSTANCE = new OpenTelemetryManager();
 
    private static OpenTelemetry openTelemetry;
 
    private OpenTelemetryManager() {
        OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder().build();
        Resource resource = Resource.getDefault().toBuilder().put(ResourceAttributes.SERVICE_NAME, "my-service").build();
 
        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
                .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
                .setResource(resource)
                .build();
        this.openTelemetry = OpenTelemetrySdk.builder()
                .setTracerProvider(tracerProvider)
                .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(),
                        W3CBaggagePropagator.getInstance())))
                .buildAndRegisterGlobal();
    }
 
    public static OpenTelemetry getOpenTelemetry() {
        return INSTANCE.openTelemetry;
    }
 
    public static Tracer provideTracer() {
        return INSTANCE.openTelemetry.getTracer("io.opentelemetry.example");
    }
}

トレースを取得

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
 
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
        Tracer tracer = OpenTelemetryManager.provideTracer();
        // spanを作成
        Span parentSpan = tracer.spanBuilder(req.getRequestURI()).startSpan();
 
        // scopeを作成(auto closeする)
        try (Scope parentScope = parentSpan.makeCurrent()) {
 
            // ネストしたspanを作成
            Span span = tracer.spanBuilder("very start")
                    .setParent(Context.current().with(parentSpan))
                    .startSpan();
            Scope scope = span.makeCurrent();
 
            // do something....
 
            // 次の区間のトレースを取る場合は一度クローズして作り直す
            scope.close();
            span.end();
            span = tracer.spanBuilder("start asHtml")
                    .setParent(Context.current().with(parentSpan))
                    .startSpan();
            scope = span.makeCurrent();
 
            // do something....
 
            scope.close();
            span.end();
 
        } finally {
            // spanをクローズ
            parentSpan.end();
        }
    }
 
}