diff --git a/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java b/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java index 57df9d9ba08ff..1583666bdd382 100644 --- a/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java +++ b/components/camel-micrometer-observability/src/main/java/org/apache/camel/micrometer/observability/MicrometerObservabilityTracer.java @@ -127,7 +127,7 @@ private MicrometerObservabilitySpanLifecycleManager() { } @Override - public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) { + public Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor extractor) { io.micrometer.tracing.Span span; if (parent != null) { MicrometerObservabilitySpanAdapter microObsParentSpan = (MicrometerObservabilitySpanAdapter) parent; diff --git a/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java b/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java index 381488b6e3002..d5f1115be8d62 100644 --- a/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java +++ b/components/camel-opentelemetry2/src/main/java/org/apache/camel/opentelemetry2/OpenTelemetryTracer.java @@ -19,6 +19,7 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.ContextPropagators; @@ -96,7 +97,7 @@ private OpentelemetrySpanLifecycleManager(Tracer tracer, ContextPropagators cont } @Override - public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) { + public Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor extractor) { SpanBuilder builder = tracer.spanBuilder(spanName); Baggage baggage = null; @@ -135,6 +136,10 @@ public String get(SpanContextPropagationExtractor carrier, String key) { baggage = Baggage.fromContext(ctx); } + if (spanKind != null) { + builder.setSpanKind(SpanKind.valueOf(spanKind)); + } + return new OpenTelemetrySpanAdapter(builder.startSpan(), baggage); } diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java new file mode 100644 index 0000000000000..41e0d021d6d5a --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/SpanKindTest.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2; + +import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.trace.data.SpanData; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.opentelemetry2.CamelOpenTelemetryExtension.OtelTrace; +import org.apache.camel.opentelemetry2.mock.MockHttpComponent; +import org.apache.camel.opentelemetry2.mock.MockKafkaComponent; +import org.apache.camel.telemetry.Op; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test that verifies SpanKind is set correctly for different component types. + */ +public class SpanKindTest extends OpenTelemetryTracerTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + OpenTelemetryTracer tst = new OpenTelemetryTracer(); + tst.setTracer(otelExtension.getOpenTelemetry().getTracer("spanKindTest")); + tst.setContextPropagators(otelExtension.getOpenTelemetry().getPropagators()); + CamelContext context = super.createCamelContext(); + + // Register mock HTTP component for testing + context.addComponent("mock-http", new MockHttpComponent()); + // Register mock Kafka component for testing + context.addComponent("mock-kafka", new MockKafkaComponent()); + + CamelContextAware.trySetCamelContext(tst, context); + tst.init(context); + return context; + } + + @Test + void testDirectComponentHasInternalSpanKind() { + template.sendBody("direct:start", "test"); + + List traces = List.copyOf(otelExtension.getTraces().values()); + assertEquals(1, traces.size()); + + List spans = traces.get(0).getSpans(); + + // Find the direct:start EVENT_SENT span + SpanData directSentSpan = getSpan(spans, "direct://start", Op.EVENT_SENT); + assertEquals(SpanKind.INTERNAL, directSentSpan.getKind(), + "direct:start EVENT_SENT should have INTERNAL SpanKind"); + + // Find the direct:start EVENT_RECEIVED span + SpanData directReceivedSpan = getSpan(spans, "direct://start", Op.EVENT_RECEIVED); + assertEquals(SpanKind.INTERNAL, directReceivedSpan.getKind(), + "direct:start EVENT_RECEIVED should have INTERNAL SpanKind"); + } + + @Test + void testHttpComponentHasClientServerSpanKind() throws Exception { + MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); + mockEndpoint.expectedMessageCount(1); + + template.sendBody("direct:httpClient", "test message"); + + mockEndpoint.assertIsSatisfied(); + + List traces = List.copyOf(otelExtension.getTraces().values()); + assertEquals(1, traces.size()); + + List spans = traces.get(0).getSpans(); + + // Find the mock-http EVENT_SENT span (client side) + SpanData httpClientSpan = getSpan(spans, "mock-http://testEndpoint", Op.EVENT_SENT); + assertEquals(SpanKind.CLIENT, httpClientSpan.getKind(), + "HTTP EVENT_SENT should have CLIENT SpanKind"); + } + + @Test + void testKafkaComponentHasProducerSpanKindAndInheritedProperties() throws Exception { + MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); + mockEndpoint.expectedMessageCount(1); + + // Send with Kafka headers that would normally be set before/during sending + template.sendBodyAndHeaders("direct:kafkaProducer", "test message", + Map.of("kafka.KEY", "test-key", + "kafka.PARTITION", 0, + "kafka.OFFSET", "12345")); + + mockEndpoint.assertIsSatisfied(); + + List traces = List.copyOf(otelExtension.getTraces().values()); + assertEquals(1, traces.size()); + + List spans = traces.get(0).getSpans(); + + // Find the mock-kafka EVENT_SENT span + SpanData kafkaSpan = getSpan(spans, "mock-kafka://testTopic", Op.EVENT_SENT); + + // Verify OpenTelemetry-specific SpanKind + assertEquals(SpanKind.PRODUCER, kafkaSpan.getKind(), + "Kafka EVENT_SENT should have PRODUCER SpanKind"); + + // Verify inherited properties from camel-telemetry KafkaSpanDecorator + assertEquals("0", kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.partition")), + "Should have kafka.partition tag from inherited decorator"); + assertEquals("12345", kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.offset")), + "Should have kafka.offset tag from inherited decorator"); + assertEquals("test-key", kafkaSpan.getAttributes().get(AttributeKey.stringKey("kafka.key")), + "Should have kafka.key tag from inherited decorator"); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .log("Processing message"); + + // Mock HTTP client route - tests CLIENT SpanKind + from("direct:httpClient") + .to("mock-http://testEndpoint") + .to("mock:result"); + + // Mock Kafka producer route - tests PRODUCER SpanKind and inherited properties + from("direct:kafkaProducer") + .to("mock-kafka://testTopic") + .to("mock:result"); + } + }; + } + +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java new file mode 100644 index 0000000000000..9d74ab627166f --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpComponent.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +import java.util.Map; + +/** + * Mock HTTP component for testing SpanKind. This component is recognized by HttpSpanDecorator based on its class name. + */ +public class MockHttpComponent extends org.apache.camel.support.DefaultComponent { + + @Override + protected org.apache.camel.Endpoint createEndpoint(String uri, String remaining, Map parameters) + throws Exception { + MockHttpEndpoint endpoint = new MockHttpEndpoint(uri, this); + setProperties(endpoint, parameters); + return endpoint; + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java new file mode 100644 index 0000000000000..ee1d5e061862a --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpEndpoint.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Mock HTTP endpoint for testing SpanKind. + */ +class MockHttpEndpoint extends org.apache.camel.support.DefaultEndpoint { + + public MockHttpEndpoint(String endpointUri, org.apache.camel.Component component) { + super(endpointUri, component); + } + + @Override + public org.apache.camel.Producer createProducer() throws Exception { + return new MockHttpProducer(this); + } + + @Override + public org.apache.camel.Consumer createConsumer(org.apache.camel.Processor processor) throws Exception { + throw new IllegalArgumentException("Not used in MockHttpEndpoint"); + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java new file mode 100644 index 0000000000000..11530a4432552 --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpProducer.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Mock HTTP producer that just echoes the input. + */ +class MockHttpProducer extends org.apache.camel.support.DefaultProducer { + + public MockHttpProducer(org.apache.camel.Endpoint endpoint) { + super(endpoint); + } + + @Override + public void process(org.apache.camel.Exchange exchange) throws Exception { + // Simple echo - set response body to request body + exchange.getMessage().setBody("HTTP Response: " + exchange.getIn().getBody()); + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java new file mode 100644 index 0000000000000..e8597fbfca97f --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockHttpSpanDecorator.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Span decorator for mock HTTP component used in tests. + */ +public class MockHttpSpanDecorator extends org.apache.camel.telemetry.decorators.AbstractHttpSpanDecorator { + + @Override + public String getComponent() { + return "mock-http"; + } + + @Override + public String getComponentClassName() { + return MockHttpComponent.class.getName(); + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java new file mode 100644 index 0000000000000..8ee1a0725ecd6 --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaComponent.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +import java.util.Map; + +/** + * Mock Kafka component for testing SpanKind and inherited properties. + */ +public class MockKafkaComponent extends org.apache.camel.support.DefaultComponent { + + @Override + protected org.apache.camel.Endpoint createEndpoint(String uri, String remaining, Map parameters) + throws Exception { + MockKafkaEndpoint endpoint = new MockKafkaEndpoint(uri, this); + setProperties(endpoint, parameters); + return endpoint; + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java new file mode 100644 index 0000000000000..c9fcd1d58a7e3 --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaEndpoint.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Mock Kafka endpoint for testing SpanKind and inherited properties. + */ +class MockKafkaEndpoint extends org.apache.camel.support.DefaultEndpoint { + + public MockKafkaEndpoint(String endpointUri, org.apache.camel.Component component) { + super(endpointUri, component); + } + + @Override + public org.apache.camel.Producer createProducer() throws Exception { + return new MockKafkaProducer(this); + } + + @Override + public org.apache.camel.Consumer createConsumer(org.apache.camel.Processor processor) throws Exception { + throw new UnsupportedOperationException("Consumer not implemented for mock Kafka"); + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java new file mode 100644 index 0000000000000..033993a5d226d --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaProducer.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Mock Kafka producer that simulates Kafka headers. + */ +class MockKafkaProducer extends org.apache.camel.support.DefaultProducer { + + public MockKafkaProducer(org.apache.camel.Endpoint endpoint) { + super(endpoint); + } + + @Override + public void process(org.apache.camel.Exchange exchange) throws Exception { + // Simulate Kafka response with partition, offset, and key + // These headers would normally be set by the real Kafka producer + exchange.getMessage().setHeader("kafka.PARTITION", 0); + exchange.getMessage().setHeader("kafka.OFFSET", "12345"); + exchange.getMessage().setHeader("kafka.KEY", "test-key"); + exchange.getMessage().setBody("Kafka Response: " + exchange.getIn().getBody()); + } +} diff --git a/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java new file mode 100644 index 0000000000000..6d0cc534c437f --- /dev/null +++ b/components/camel-opentelemetry2/src/test/java/org/apache/camel/opentelemetry2/mock/MockKafkaSpanDecorator.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.opentelemetry2.mock; + +/** + * Span decorator for mock Kafka component used in tests. Extends the real KafkaSpanDecorator to inherit all + * Kafka-specific behavior (partition, offset, key tags) and adds SpanKind. + */ +public class MockKafkaSpanDecorator extends org.apache.camel.telemetry.decorators.KafkaSpanDecorator { + + @Override + public String getComponent() { + return "mock-kafka"; + } + + @Override + public String getComponentClassName() { + return MockKafkaComponent.class.getName(); + } +} diff --git a/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator b/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator new file mode 100644 index 0000000000000..3a7f879efce0d --- /dev/null +++ b/components/camel-opentelemetry2/src/test/resources/META-INF/services/org.apache.camel.telemetry.SpanDecorator @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Mock HTTP decorator for testing +org.apache.camel.opentelemetry2.mock.MockHttpSpanDecorator +# Mock Kafka decorator for testing +org.apache.camel.opentelemetry2.mock.MockKafkaSpanDecorator diff --git a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java index f4d0cdee05a8f..c57b5e133ec79 100644 --- a/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java +++ b/components/camel-telemetry-dev/src/main/java/org/apache/camel/telemetrydev/TelemetryDevTracer.java @@ -87,7 +87,7 @@ private DevSpanLifecycleManager(String traceFormat) { } @Override - public Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor) { + public Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor extractor) { Span span = DevSpanAdapter.buildSpan(spanName); String traceId = UUID.randomUUID().toString().replaceAll("-", ""); if (parent != null) { diff --git a/components/camel-telemetry/src/main/docs/telemetry.adoc b/components/camel-telemetry/src/main/docs/telemetry.adoc index 6daad9776f257..148d7a9cfe67b 100644 --- a/components/camel-telemetry/src/main/docs/telemetry.adoc +++ b/components/camel-telemetry/src/main/docs/telemetry.adoc @@ -147,7 +147,7 @@ The `initTracer()` is in charge to inject a concrete implementation of `SpanLife ```java public interface SpanLifecycleManager { - Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor); + Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor extractor); void activate(Span span); diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java index 7b019e38fd168..95492f127ee64 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanDecorator.java @@ -38,4 +38,5 @@ public interface SpanDecorator { SpanContextPropagationInjector getInjector(Exchange exchange); + String getSpanKind(String operation); } diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java index 501dd2430f20f..837cc269657db 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/SpanLifecycleManager.java @@ -21,7 +21,7 @@ */ public interface SpanLifecycleManager { - Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor); + Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor extractor); void activate(Span span); diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java index 514fd20445655..0c5ada29eb238 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java @@ -270,7 +270,9 @@ protected void beginEventSpan(Exchange exchange, Endpoint endpoint, Op op) throw SpanDecorator spanDecorator = spanDecoratorManager.get(endpoint); Span parentSpan = spanStorageManager.peek(exchange); String spanName = spanDecorator.getOperationName(exchange, endpoint); - Span span = spanLifecycleManager.create(spanName, parentSpan, spanDecorator.getExtractor(exchange)); + String spanKind = spanDecorator.getSpanKind(op.toString()); + Span span = spanLifecycleManager.create(spanName, spanKind, parentSpan, + spanDecorator.getExtractor(exchange)); span.setTag(TagConstants.OP, op.toString()); spanDecorator.beforeTracingEvent(span, exchange, endpoint); spanLifecycleManager.activate(span); @@ -286,7 +288,9 @@ protected void beginProcessorSpan(Exchange exchange, String processorName) throw // there is some inconsistency LOG.warn("Processor tracing parent should not be null!"); } - Span span = spanLifecycleManager.create(processorName, parentSpan, spanDecorator.getExtractor(exchange)); + String spanKind = spanDecorator.getSpanKind(Op.EVENT_PROCESS.toString()); + Span span = spanLifecycleManager.create(processorName, spanKind, + parentSpan, spanDecorator.getExtractor(exchange)); span.setTag(TagConstants.OP, Op.EVENT_PROCESS.toString()); spanDecorator.beforeTracingEvent(span, exchange, null); spanLifecycleManager.activate(span); diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java index e0ae5b91a3764..24a1216e796c5 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractHttpSpanDecorator.java @@ -19,6 +19,7 @@ import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.Message; +import org.apache.camel.telemetry.Op; import org.apache.camel.telemetry.Span; import org.apache.camel.telemetry.TagConstants; @@ -105,4 +106,15 @@ public void afterTracingEvent(Span span, Exchange exchange) { } } } + + @Override + public String getSpanKind(String operationName) { + if (Op.EVENT_RECEIVED.name().equals(operationName)) { + return "SERVER"; + } else if (Op.EVENT_SENT.name().equals(operationName)) { + return "CLIENT"; + } else { + return "INTERNAL"; + } + } } diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java index bd4852ffaea2e..e592fe2c3b18c 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractMessagingSpanDecorator.java @@ -18,6 +18,7 @@ import org.apache.camel.Endpoint; import org.apache.camel.Exchange; +import org.apache.camel.telemetry.Op; import org.apache.camel.telemetry.Span; import org.apache.camel.telemetry.TagConstants; @@ -60,4 +61,14 @@ protected String getMessageId(Exchange exchange) { return null; } + @Override + public String getSpanKind(String operationName) { + if (Op.EVENT_RECEIVED.name().equals(operationName)) { + return "CONSUMER"; + } else if (Op.EVENT_SENT.name().equals(operationName)) { + return "PRODUCER"; + } else { + return "INTERNAL"; + } + } } diff --git a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java index ee7e8002e2e2f..5995ad3ed32be 100644 --- a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java +++ b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/decorators/AbstractSpanDecorator.java @@ -175,4 +175,9 @@ public SpanContextPropagationExtractor getExtractor(Exchange exchange) { public SpanContextPropagationInjector getInjector(Exchange exchange) { return new CamelHeadersSpanContextPropagationInjector(exchange.getIn().getHeaders()); } + + @Override + public String getSpanKind(String operationName) { + return "INTERNAL"; + } } diff --git a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java index 48e0467b96127..6479e8ebf2133 100644 --- a/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java +++ b/components/camel-telemetry/src/test/java/org/apache/camel/telemetry/mock/MockTracer.java @@ -53,7 +53,7 @@ private class MockSpanLifecycleManager implements SpanLifecycleManager { Map inMemoryStorageMap = new HashMap<>(); @Override - public Span create(String spanName, Span parentSpan, SpanContextPropagationExtractor extractor) { + public Span create(String spanName, String spanKind, Span parentSpan, SpanContextPropagationExtractor extractor) { Span span = MockSpanAdapter.buildSpan(spanName); String traceId = UUID.randomUUID().toString().replaceAll("-", ""); if (parentSpan != null) {