Issue 003: OpenTelemetry Distributed Tracing
Summary
Adopt OpenTelemetry tracing end-to-end. Wire micrometer-tracing-bridge-otel
into the Spring backend, propagate trace context to the Python AI
sidecar, deploy a local OpenTelemetry Collector + Jaeger via
docker-compose, and add a regression IT that captures the expected
span topology of a typical analyze request.
Motivation
- P95 latency investigations today rely on logs; we have no causal
chain showing which dependency is the bottleneck.
- Micrometer counters (commit
6447917) answer "did it time out?"
but not "where in the pipeline?".
springdoc-openapi already shows that instrumentation hooks
exist; the tracing side is the missing half.
Tasks
Acceptance criteria
- A full analyze request produces 6+ spans, including
GET /api/research/stock/{symbol}/analyze,
analysis.cacheLookup, analysis.leaseWait,
ai.sidecar.analyze-stock, document.keywordSearch,
document.vectorSearch.
- Traces from Java and the Python sidecar share the same
traceparent.
docker compose up exposes Jaeger on http://localhost:16686
and shows traces for any analyze request issued against the
compose stack.
- The regression IT fails if any of the expected spans go missing
in a refactor.
Out of scope
- Tail-based sampling.
- Replacing Micrometer metrics.
- Continuous profiling.
References
Estimate
3 weeks. Split into 4 PRs:
- Backend dependencies + auto-instrumentation (~400 LoC, 1 PR)
- Custom spans + W3C propagation to sidecar (~300 LoC, 1 PR)
- Collector + Jaeger compose profile (~120 LoC, 1 PR)
- Regression IT + docs (~250 LoC, 1 PR)
Issue 003: OpenTelemetry Distributed Tracing
Summary
Adopt OpenTelemetry tracing end-to-end. Wire
micrometer-tracing-bridge-otelinto the Spring backend, propagate trace context to the Python AI
sidecar, deploy a local OpenTelemetry Collector + Jaeger via
docker-compose, and add a regression IT that captures the expected
span topology of a typical analyze request.
Motivation
chain showing which dependency is the bottleneck.
6447917) answer "did it time out?"but not "where in the pipeline?".
springdoc-openapialready shows that instrumentation hooksexist; the tracing side is the missing half.
Tasks
micrometer-tracing-bridge-otelandopentelemetry-exporter-otlptobackend/pom.xml.management.tracing.sampling.probability=0.1,management.tracing.propagation.type=w3c, andmanagement.otlp.tracing.endpoint(env-overridable).*Controllermethod with@Observedand theWorkflowOrchestratorstage methods.@NewSpan("analysis.computeContextHash")to the hashhelper and
@ContinueSpanto the sidecar call.OtelMiddlewaretoai-service/app/observability.py;register in
ai-service/app/main.py; passtraceparentthroughin the Python call chain.
otel-collectorandjaegerservices todocker-compose.ymlandscripts/otel-collector-config.yaml.InMemorySpanExporter-based regression IT insrc/test/java/com/finsight/it/observability/.traceparenton4xx/5xxresponses (decision fromthe RFC).
docs/operations.md: how to read traces in Jaeger, how tocopy a trace ID from a 500 response.
Acceptance criteria
GET /api/research/stock/{symbol}/analyze,analysis.cacheLookup,analysis.leaseWait,ai.sidecar.analyze-stock,document.keywordSearch,document.vectorSearch.traceparent.docker compose upexposes Jaeger onhttp://localhost:16686and shows traces for any analyze request issued against the
compose stack.
in a refactor.
Out of scope
References
docs/rfcs/RFC-003-opentelemetry-distributed-tracing.md(https://docs.spring.io/spring-boot/docs/3.3.x/reference/html/actuator.html#actuator.micrometer-tracing)
(https://opentelemetry.io/docs/specs/semconv/)
Estimate
3 weeks. Split into 4 PRs: