Skip to content

Commit 8b0b9b5

Browse files
committed
Add SpanKind parameter to telemetry API to support proper span type classification
1 parent 5d230b8 commit 8b0b9b5

19 files changed

Lines changed: 271 additions & 37 deletions

File tree

components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@
3232
import org.apache.camel.spi.Configurer;
3333
import org.apache.camel.spi.annotations.JdkService;
3434
import org.apache.camel.support.CamelContextHelper;
35-
import org.apache.camel.telemetry.Span;
36-
import org.apache.camel.telemetry.SpanContextPropagationExtractor;
37-
import org.apache.camel.telemetry.SpanContextPropagationInjector;
38-
import org.apache.camel.telemetry.SpanLifecycleManager;
35+
import org.apache.camel.telemetry.*;
3936
import org.slf4j.Logger;
4037
import org.slf4j.LoggerFactory;
4138

@@ -126,7 +123,9 @@ private MicrometerObservabilitySpanLifecycleManager() {
126123
}
127124

128125
@Override
129-
public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) {
126+
public Span create(
127+
String spanName, SpanKind kind, Span parent,
128+
SpanContextPropagationExtractor extractor) {
130129
io.micrometer.tracing.Span span;
131130
if (parent != null) {
132131
MicrometerObservabilitySpanAdapter microObsParentSpan = (MicrometerObservabilitySpanAdapter) parent;
@@ -139,6 +138,9 @@ public Span create(String spanName, Span parent, SpanContextPropagationExtractor
139138
span = builder.start();
140139
}
141140
span.name(spanName);
141+
// Note: Micrometer determines span kind through Observation context types,
142+
// not through direct span.kind() calls. The kind parameter is accepted
143+
// for API compatibility but not used in this implementation.
142144

143145
return new MicrometerObservabilitySpanAdapter(span);
144146
}

components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@
2828
import org.apache.camel.spi.Configurer;
2929
import org.apache.camel.spi.annotations.JdkService;
3030
import org.apache.camel.support.CamelContextHelper;
31-
import org.apache.camel.telemetry.Span;
32-
import org.apache.camel.telemetry.SpanContextPropagationExtractor;
33-
import org.apache.camel.telemetry.SpanContextPropagationInjector;
34-
import org.apache.camel.telemetry.SpanLifecycleManager;
31+
import org.apache.camel.telemetry.*;
3532
import org.slf4j.Logger;
3633
import org.slf4j.LoggerFactory;
3734

@@ -96,8 +93,11 @@ private OpentelemetrySpanLifecycleManager(Tracer tracer, ContextPropagators cont
9693
}
9794

9895
@Override
99-
public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) {
96+
public Span create(
97+
String spanName, SpanKind kind, Span parent,
98+
SpanContextPropagationExtractor extractor) {
10099
SpanBuilder builder = tracer.spanBuilder(spanName);
100+
builder = builder.setSpanKind(mapToSpanKind(kind));
101101
Baggage baggage = null;
102102

103103
if (parent != null) {
@@ -171,6 +171,16 @@ public void inject(Span span, SpanContextPropagationInjector injector, boolean i
171171
}
172172
}
173173

174+
private io.opentelemetry.api.trace.SpanKind mapToSpanKind(org.apache.camel.telemetry.SpanKind kind) {
175+
return switch (kind) {
176+
case CLIENT -> io.opentelemetry.api.trace.SpanKind.CLIENT;
177+
case SERVER -> io.opentelemetry.api.trace.SpanKind.SERVER;
178+
case PRODUCER -> io.opentelemetry.api.trace.SpanKind.PRODUCER;
179+
case CONSUMER -> io.opentelemetry.api.trace.SpanKind.CONSUMER;
180+
case INTERNAL -> io.opentelemetry.api.trace.SpanKind.INTERNAL;
181+
};
182+
}
183+
174184
}
175185

176186
}

components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.camel.telemetry.Span;
2626
import org.apache.camel.telemetry.SpanContextPropagationExtractor;
2727
import org.apache.camel.telemetry.SpanContextPropagationInjector;
28+
import org.apache.camel.telemetry.SpanKind;
2829
import org.apache.camel.telemetry.SpanLifecycleManager;
2930
import org.apache.camel.telemetry.Tracer;
3031
import org.slf4j.Logger;
@@ -87,7 +88,9 @@ private DevSpanLifecycleManager(String traceFormat) {
8788
}
8889

8990
@Override
90-
public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) {
91+
public Span create(
92+
String spanName, SpanKind kind, Span parent,
93+
SpanContextPropagationExtractor extractor) {
9194
Span span = DevSpanAdapter.buildSpan(spanName);
9295
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
9396
if (parent != null) {
@@ -101,6 +104,7 @@ public Span create(String spanName, Span parent, SpanContextPropagationExtractor
101104
LOG.error("TRACE ERROR: wrong format, could not split traceparent {}", upstreamTraceParent);
102105
span.setTag("traceid", traceId);
103106
span.setTag("spanid", UUID.randomUUID().toString().replaceAll("-", ""));
107+
span.setTag("kind", kind.toString());
104108
return span;
105109
}
106110
traceId = split[0];
@@ -110,6 +114,7 @@ public Span create(String spanName, Span parent, SpanContextPropagationExtractor
110114
}
111115
span.setTag("traceid", traceId);
112116
span.setTag("spanid", UUID.randomUUID().toString().replaceAll("-", ""));
117+
span.setTag("kind", kind.toString());
113118
return span;
114119
}
115120

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/DisableEndpointTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.camel.telemetrydev;
1818

19-
import java.io.IOException;
2019
import java.util.List;
2120
import java.util.Map;
2221

@@ -47,9 +46,9 @@ protected CamelContext createCamelContext() throws Exception {
4746
}
4847

4948
@Test
50-
void testProcessorsTraceRequest() throws IOException {
49+
void testProcessorsTraceRequest() {
5150
template.sendBody("direct:start", "my-body");
52-
Map<String, DevTrace> traces = tracesFromLog();
51+
Map<String, DevTrace> traces = awaitTracesFromLog(1);
5352
assertEquals(1, traces.size());
5453
checkTrace(traces.values().iterator().next());
5554
}

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/EnableProcessorsTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.camel.telemetrydev;
1818

19-
import java.io.IOException;
2019
import java.util.List;
2120
import java.util.Map;
2221

@@ -46,9 +45,9 @@ protected CamelContext createCamelContext() throws Exception {
4645
}
4746

4847
@Test
49-
void testProcessorsTraceRequest() throws IOException {
48+
void testProcessorsTraceRequest() {
5049
template.sendBody("direct:start", "my-body");
51-
Map<String, DevTrace> traces = tracesFromLog();
50+
Map<String, DevTrace> traces = awaitTracesFromLog(1);
5251
assertEquals(1, traces.size());
5352
checkTrace(traces.values().iterator().next());
5453
}

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/MDCHeadersTraceTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.camel.telemetrydev;
1818

19-
import java.io.IOException;
2019
import java.util.Map;
2120

2221
import org.apache.camel.CamelContext;
@@ -44,11 +43,11 @@ protected CamelContext createCamelContext() throws Exception {
4443
}
4544

4645
@Test
47-
void testProcessorsTraceRequest() throws InterruptedException, IOException {
46+
void testProcessorsTraceRequest() throws InterruptedException {
4847
MockEndpoint mock = getMockEndpoint("mock:result");
4948
mock.expectedMessageCount(1);
5049
template.sendBody("direct:start", "my-body");
51-
Map<String, DevTrace> traces = tracesFromLog();
50+
Map<String, DevTrace> traces = awaitTracesFromLog(1);
5251
assertEquals(1, traces.size());
5352
mock.assertIsSatisfied();
5453
Map<String, Object> headers = mock.getExchanges().get(0).getIn().getHeaders();

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/SpanPropagationUpstreamTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.camel.telemetrydev;
1818

19-
import java.io.IOException;
2019
import java.util.List;
2120
import java.util.Map;
2221

@@ -41,10 +40,10 @@ protected CamelContext createCamelContext() throws Exception {
4140
}
4241

4342
@Test
44-
void testPropagateUpstreamTraceRequest() throws IOException {
43+
void testPropagateUpstreamTraceRequest() {
4544
template.requestBodyAndHeader("direct:start", "sample body",
4645
"traceparent", "123456789-123456");
47-
Map<String, DevTrace> traces = tracesFromLog();
46+
Map<String, DevTrace> traces = awaitTracesFromLog(1);
4847
assertEquals(1, traces.size());
4948
checkTrace(traces.values().iterator().next());
5049
}

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.apache.camel.telemetrydev;
1818

19-
import java.io.IOException;
2019
import java.util.List;
2120
import java.util.Map;
2221

@@ -45,21 +44,21 @@ protected CamelContext createCamelContext() throws Exception {
4544
}
4645

4746
@Test
48-
void testRouteSingleRequest() throws IOException {
47+
void testRouteSingleRequest() {
4948
Exchange result = template.request("direct:start", null);
5049
// Make sure the trace is propagated downstream
5150
assertNotNull(result.getIn().getHeader("traceparent"));
52-
Map<String, DevTrace> traces = tracesFromLog();
51+
Map<String, DevTrace> traces = awaitTracesFromLog(1);
5352
assertEquals(1, traces.size());
5453
checkTrace(traces.values().iterator().next(), null);
5554
}
5655

5756
@Test
58-
void testRouteMultipleRequests() throws IOException {
57+
void testRouteMultipleRequests() {
5958
for (int i = 1; i <= 10; i++) {
6059
context.createProducerTemplate().sendBody("direct:start", "Hello!");
6160
}
62-
Map<String, DevTrace> traces = tracesFromLog();
61+
Map<String, DevTrace> traces = awaitTracesFromLog(10);
6362
// Each trace should have a unique trace id. It is enough to assert that
6463
// the number of elements in the map is the same of the requests to prove
6564
// all traces have been generated uniquely.

components/camel-telemetry-dev/src/test/java/org/apache/camel/telemetrydev/TelemetryDevTracerTestSupport.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.HashMap;
2424
import java.util.List;
2525
import java.util.Map;
26+
import java.util.concurrent.TimeUnit;
2627

2728
import com.fasterxml.jackson.databind.ObjectMapper;
2829
import org.apache.camel.telemetry.Op;
@@ -32,14 +33,32 @@
3233
import org.apache.logging.log4j.core.LoggerContext;
3334
import org.apache.logging.log4j.core.appender.RollingFileAppender;
3435
import org.junit.jupiter.api.AfterEach;
36+
import org.junit.jupiter.api.BeforeEach;
37+
38+
import static org.awaitility.Awaitility.await;
3539

3640
public class TelemetryDevTracerTestSupport extends ExchangeTestSupport {
3741

3842
private final ObjectMapper mapper = new ObjectMapper();
3943

44+
/*
45+
* Clear the log traces before each test to prevent flaky tests from leftover data
46+
*/
47+
@BeforeEach
48+
public synchronized void clearLogTracesBeforeTest() throws IOException {
49+
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
50+
RollingFileAppender appender = (RollingFileAppender) ctx.getConfiguration().getAppenders().get("file2");
51+
if (appender != null) {
52+
appender.getManager().rollover();
53+
}
54+
}
55+
4056
protected Map<String, DevTrace> tracesFromLog() throws IOException {
4157
Map<String, DevTrace> answer = new HashMap<>();
4258
Path path = Paths.get("target/telemetry-traces.log");
59+
if (!Files.exists(path)) {
60+
return answer;
61+
}
4362
List<String> allTraces = Files.readAllLines(path);
4463
for (String trace : allTraces) {
4564
DevTrace st = mapper.readValue(trace, DevTrace.class);
@@ -57,6 +76,28 @@ protected Map<String, DevTrace> tracesFromLog() throws IOException {
5776
return answer;
5877
}
5978

79+
/**
80+
* Wait for the expected number of traces to be written to the log file. Uses Awaitility to avoid flaky tests from
81+
* timing issues.
82+
*/
83+
protected Map<String, DevTrace> awaitTracesFromLog(int expectedCount) {
84+
await().atMost(10, TimeUnit.SECONDS)
85+
.pollInterval(100, TimeUnit.MILLISECONDS)
86+
.until(() -> {
87+
try {
88+
Map<String, DevTrace> traces = tracesFromLog();
89+
return traces.size() >= expectedCount;
90+
} catch (IOException e) {
91+
return false;
92+
}
93+
});
94+
try {
95+
return tracesFromLog();
96+
} catch (IOException e) {
97+
throw new RuntimeException("Failed to read traces from log", e);
98+
}
99+
}
100+
60101
/*
61102
* This one is required to rollover the log traces database file and make sure each test has its own
62103
* set of fresh data.

components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,12 @@ public interface SpanDecorator {
3838

3939
SpanContextPropagationInjector getInjector(Exchange exchange);
4040

41+
/**
42+
* This method returns the SpanKind for a given operation.
43+
*
44+
* @param op The operation type
45+
* @return The span kind to use for this operation
46+
*/
47+
SpanKind getSpanKind(Op op);
48+
4149
}

0 commit comments

Comments
 (0)