Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import java.util.Map;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.Span;
Expand All @@ -29,18 +28,12 @@
public class OpenTelemetrySpanAdapter implements org.apache.camel.telemetry.Span {

private static final String DEFAULT_EVENT_NAME = "log";
static final String BAGGAGE_CAMEL_FLAG = "camelScope";

private final Span otelSpan;
private final Baggage baggage;
private Scope scope;
private Scope baggageScope;

protected OpenTelemetrySpanAdapter(Span otelSpan, Baggage baggage) {
protected OpenTelemetrySpanAdapter(Span otelSpan) {
this.otelSpan = otelSpan;
// We store an important flag in the baggage in order to verify if the
// root span was generated internally or from a third party dependency.
this.baggage = baggage.toBuilder().put(BAGGAGE_CAMEL_FLAG, "true").build();
}

protected Span getSpan() {
Expand All @@ -49,26 +42,18 @@ protected Span getSpan() {

protected void makeCurrent() {
this.scope = this.otelSpan.makeCurrent();
this.baggageScope = this.baggage.makeCurrent();
}

protected void end() {
this.otelSpan.end();
}

protected void close() {
if (baggageScope != null) {
this.baggageScope.close();
}
if (scope != null) {
this.scope.close();
}
}

protected Baggage getBaggage() {
return this.baggage;
}

@Override
public void log(Map<String, String> fields) {
this.otelSpan.addEvent(getEventNameFromFields(fields), convertToAttributes(fields));
Expand Down Expand Up @@ -126,7 +111,7 @@ private Attributes convertToAttributes(Map<String, ?> fields) {

@Override
public String toString() {
return "OpenTelemetrySpanAdapter [span=" + otelSpan + ", baggage=" + baggage + "]";
return "OpenTelemetrySpanAdapter [span=" + otelSpan + "]";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.apache.camel.opentelemetry2;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
Expand Down Expand Up @@ -98,24 +97,14 @@ private OpentelemetrySpanLifecycleManager(Tracer tracer, ContextPropagators cont
@Override
public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) {
SpanBuilder builder = tracer.spanBuilder(spanName);
Baggage baggage = null;

if (parent != null) {
OpenTelemetrySpanAdapter otelParentSpan = (OpenTelemetrySpanAdapter) parent;
builder = builder.setParent(Context.current().with(otelParentSpan.getSpan()));
baggage = otelParentSpan.getBaggage();
} else {
Context current = Context.root();
// If the current span was generated by Camel, then, this is a "dirty" context.
// A "dirty" context happens when the Camel thread local is reused and
// due to the way Camel async works, can't reliably clean its context before reusing it.
if (Baggage.current().getEntryValue(OpenTelemetrySpanAdapter.BAGGAGE_CAMEL_FLAG) == null) {
// Not "dirty" context. In this case a Span exists and the current span was generated by some third party dependency (ie, vertx)
// therefore we need to consider this span as the root on such a trace.
current = Context.current();
}
// Try to get parent from context propagation (upstream traces)
Context ctx = contextPropagators.getTextMapPropagator().extract(current, extractor,
// Always use Context.root() as the base — context propagation is handled
// exclusively via Exchange headers (W3C traceparent), not ThreadLocal.
Context ctx = contextPropagators.getTextMapPropagator().extract(Context.root(), extractor,
new TextMapGetter<SpanContextPropagationExtractor>() {
@Override
public Iterable<String> keys(SpanContextPropagationExtractor carrier) {
Expand All @@ -132,10 +121,9 @@ public String get(SpanContextPropagationExtractor carrier, String key) {
});

builder = builder.setParent(ctx);
baggage = Baggage.fromContext(ctx);
}

return new OpenTelemetrySpanAdapter(builder.startSpan(), baggage);
return new OpenTelemetrySpanAdapter(builder.startSpan());
}

@Override
Expand All @@ -160,9 +148,6 @@ public void close(Span span) {
public void inject(Span span, SpanContextPropagationInjector injector, boolean includeTracing) {
OpenTelemetrySpanAdapter otelSpan = (OpenTelemetrySpanAdapter) span;
Context ctx = Context.current().with(otelSpan.getSpan());
if (otelSpan.getBaggage() != null) {
ctx = ctx.with(otelSpan.getBaggage());
}
contextPropagators.getTextMapPropagator().inject(ctx, injector,
(carrier, key, value) -> carrier.put(key, value));
if (includeTracing) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.trace.data.SpanData;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
Expand All @@ -38,7 +37,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class SpanInjection extends OpenTelemetryTracerTestSupport {
public class SpanInjectionTest extends OpenTelemetryTracerTestSupport {

@Override
protected CamelContext createCamelContext() throws Exception {
Expand All @@ -54,30 +53,30 @@ protected CamelContext createCamelContext() throws Exception {

@Test
void testRouteSingleRequest() throws IOException {
// NOTE: we simulate that any external third party is the root parent, as we want Camel traces to depend on it.
Span span = otelExtension.getOpenTelemetry().getTracer("traceTest").spanBuilder("mySpan").startSpan();
// Simulate an external third party parent span by injecting traceparent header
// into the Exchange (the way HTTP components do it), not via ThreadLocal.
Span span = otelExtension.getOpenTelemetry().getTracer("traceTest").spanBuilder("mySpan").setNoParent().startSpan();
String expectedTrace = span.getSpanContext().getTraceId();
String expectedSpan = span.getSpanContext().getSpanId();
try (Scope scope = span.makeCurrent()) {
template.sendBody("direct:start", "my-body");
Map<String, OtelTrace> traces = otelExtension.getTraces();
assertEquals(1, traces.size());
checkTrace(traces.values().iterator().next(), expectedTrace, expectedSpan);
}
String traceparent = "00-" + expectedTrace + "-" + expectedSpan + "-01";
template.sendBodyAndHeader("direct:start", "my-body", "traceparent", traceparent);
Map<String, OtelTrace> traces = otelExtension.getTraces();
assertEquals(1, traces.size());
checkTrace(traces.values().iterator().next(), expectedTrace, expectedSpan);
}

@Test
void testRouteMultipleRequests() throws IOException {
otelExtension.clearSpans();
int i = 10;
Map<String, String> tracesRef = new HashMap<>();
for (int j = 0; j < i; j++) {
// NOTE: we simulate that any external third party is the root parent, as we want Camel traces to depend on it.
Span span = otelExtension.getOpenTelemetry().getTracer("traceTest").spanBuilder("mySpan").startSpan();
// We hold the reference of each parent span for each trace
// Simulate external parent via traceparent header
Span span = otelExtension.getOpenTelemetry().getTracer("traceTest").spanBuilder("mySpan").setNoParent().startSpan();
tracesRef.put(span.getSpanContext().getTraceId(), span.getSpanContext().getSpanId());
try (Scope scope = span.makeCurrent()) {
context.createProducerTemplate().sendBody("direct:start", "Hello!");
}
String traceparent
= "00-" + span.getSpanContext().getTraceId() + "-" + span.getSpanContext().getSpanId() + "-01";
context.createProducerTemplate().sendBodyAndHeader("direct:start", "Hello!", "traceparent", traceparent);
}
Map<String, OtelTrace> traces = otelExtension.getTraces();
// Each trace should have a unique trace id. It is enough to assert that
Expand Down Expand Up @@ -123,7 +122,7 @@ private void checkTrace(OtelTrace trace, String parentTrace, String parentSpan)
assertEquals(Op.EVENT_PROCESS.toString(), innerProcessor.getAttributes().get(AttributeKey.stringKey("op")));

// Validate hierarchy
// The parent now must be a valid trace as it was generated by a third party (our test in this case).
// The parent now must be a valid trace as it was provided via traceparent header.
assertTrue(testProducer.getParentSpanContext().isValid());
assertEquals(parentSpan, testProducer.getParentSpanContext().getSpanId());

Expand Down
Loading