-
Notifications
You must be signed in to change notification settings - Fork 0
OpenTelemetry Integration
Frody edited this page Sep 3, 2026
·
1 revision
The scopeflow-otel module bridges ScopeFlow context entries to OpenTelemetry Baggage, enabling context propagation through OTel-instrumented HTTP clients, gRPC channels, and messaging systems.
<dependency>
<groupId>io.github.frodygr</groupId>
<artifactId>scopeflow-otel</artifactId>
</dependency>ScopeFlow scopeFlow = ScopeFlowBuilder.create()
.propagator(new MdcPropagator(MdcKeyPolicy.allowAll()))
.propagator(OtelBaggagePropagator.create())
.build();ScopeFlow scopeFlow = ScopeFlowBuilder.create()
.propagator(OtelBaggagePropagator.create(Set.of("request.id", "tenant.id")))
.build();ScopeFlow.open("request", {request.id=abc-123})
│
└── OtelBaggagePropagator.onScopeOpened()
├── Read current Baggage
├── Build new Baggage: current + {request.id=abc-123}
├── Context.current().with(newBaggage).makeCurrent()
└── Push OTel Scope onto stack
scope.put("tenant.id", "acme")
│
└── OtelBaggagePropagator.onScopeEnriched()
├── Close current OTel Scope (restores parent Baggage)
├── Build new Baggage: {request.id=abc-123, tenant.id=acme}
├── Context.current().with(newBaggage).makeCurrent()
└── Push new OTel Scope onto stack
scope.close()
│
└── OtelBaggagePropagator.onScopeClosed()
├── Pop OTel Scope from stack
├── scope.close() → restores previous OTel Context
└── Previous Baggage active again
When your application calls another service, OTel's instrumentation automatically propagates Baggage via HTTP headers:
GET /api/data HTTP/1.1
Host: service-b
baggage: request.id=abc-123,tenant.id=acme
traceparent: 00-trace-id-span-id-01
The receiving service's OTel instrumentation reads the baggage header, making the values available via Baggage.current().
try (Scope outer = scopeFlow.open("http",
Map.of("request.id", "req-1"))) {
// OTel Baggage: {request.id=req-1}
try (Scope inner = scopeFlow.open("db",
Map.of("query.id", "q-1"))) {
// OTel Baggage: {request.id=req-1, query.id=q-1}
Baggage.current().getEntryValue("request.id"); // "req-1"
Baggage.current().getEntryValue("query.id"); // "q-1"
}
// OTel Baggage: {request.id=req-1} (query.id removed)
}
// OTel Baggage: {} (everything restored)Both propagators can coexist. MDC runs first (order -100), OTel runs after (order 100):
ScopeFlow scopeFlow = ScopeFlowBuilder.create()
.propagator(new MdcPropagator(MdcKeyPolicy.of(Set.of("request.id"))))
.propagator(OtelBaggagePropagator.create(Set.of("request.id", "tenant.id")))
.build();
try (Scope scope = scopeFlow.open("request",
Map.of("request.id", "abc", "tenant.id", "acme"))) {
// MDC has: request.id=abc (tenant.id filtered by MDC policy)
// OTel Baggage has: request.id=abc, tenant.id=acme
log.info("Processing"); // Logs show request.id
httpClient.get("/other-service"); // Baggage propagates both
}All Baggage entries created by ScopeFlow include a metadata tag "scopeflow" for easy identification:
Baggage.current().forEach((key, entry) -> {
if ("scopeflow".equals(entry.getMetadata().getValue())) {
// This entry was set by ScopeFlow
}
});ScopeFlow • Distributed Context & Tracing Propagation • Licensed under Apache-2.0