This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 74612964230 Micrometer Observation for Camel 4.0.0 (#9619) 74612964230 is described below commit 74612964230fa14d86fd7502a2dcb7a2ef435a5e Author: Marcin Grzejszczak <mar...@grzejszczak.pl> AuthorDate: Mon Mar 27 10:54:33 2023 +0200 Micrometer Observation for Camel 4.0.0 (#9619) * WIP on Micrometer Observation * Fixed missing Server side Kind * Migrated OTel tests * WIP * Camel Observation * OTel added --- camel-dependencies/pom.xml | 1 + catalog/camel-allcomponents/pom.xml | 5 + components/camel-observation/pom.xml | 83 +++++++ .../services/org/apache/camel/other.properties | 7 + .../src/generated/resources/observation.json | 15 ++ .../src/main/docs/observation.adoc | 88 +++++++ .../camel/observation/AttributeProcessor.java | 116 +++++++++ .../GetCorrelationContextProcessor.java | 109 +++++++++ .../MicrometerObservationSpanAdapter.java | 170 +++++++++++++ .../observation/MicrometerObservationTracer.java | 180 ++++++++++++++ .../SetCorrelationContextProcessor.java | 115 +++++++++ .../org.apache.camel.tracing.SpanDecorator | 52 ++++ .../apache/camel/observation}/ABCRouteTest.java | 27 ++- .../CamelMicrometerObservationTestSupport.java | 267 +++++++++++++++++++++ .../observation}/ClientRecipientListRouteTest.java | 25 +- .../apache/camel/observation}/CurrentSpanTest.java | 30 ++- .../observation}/CustomComponentNameRouteTest.java | 24 +- .../observation}/MulticastParallelRouteTest.java | 26 +- .../camel/observation}/MulticastRouteTest.java | 25 +- .../camel/observation}/RouteConcurrentTest.java | 17 +- .../camel/observation}/SpanProcessorsTest.java | 25 +- .../org/apache/camel/observation/SpanTestData.java | 107 +++++++++ .../camel/observation/TestSEDASpanDecorator.java} | 28 +-- .../apache/camel/observation}/TwoServiceTest.java | 15 +- .../observation}/TwoServiceWithExcludeTest.java | 9 +- .../CamelDefaultTracingObservationHandler.java | 54 +++++ ...opagatingReceiverTracingObservationHandler.java | 56 +++++ ...PropagatingSenderTracingObservationHandler.java | 46 ++++ .../src/test/resources/log4j2.properties | 29 +++ .../camel/opentelemetry/OpenTelemetryTracer.java | 7 +- .../apache/camel/opentelemetry/ABCRouteTest.java | 19 +- .../ClientRecipientListRouteTest.java | 17 +- .../camel/opentelemetry/CurrentSpanTest.java | 10 +- .../CustomComponentNameRouteTest.java | 17 +- .../opentelemetry/MulticastParallelRouteTest.java | 19 +- .../camel/opentelemetry/MulticastRouteTest.java | 18 +- .../camel/opentelemetry/RouteConcurrentTest.java | 11 +- .../camel/opentelemetry/SpanProcessorsTest.java | 18 +- .../apache/camel/opentelemetry/TwoServiceTest.java | 7 + .../opentelemetry/TwoServiceWithExcludeTest.java | 4 + .../camel-tracing/src/main/docs/tracing.adoc | 2 +- .../apache/camel/tracing/ActiveSpanManager.java | 27 ++- .../java/org/apache/camel/tracing/SpanAdapter.java | 20 ++ .../main/java/org/apache/camel/tracing/Tracer.java | 12 +- .../decorators/AbstractHttpSpanDecorator.java | 4 +- .../camel/tracing/decorators/CqlSpanDecorator.java | 2 +- .../decorators/ElasticsearchSpanDecorator.java | 4 +- .../tracing/decorators/JdbcSpanDecorator.java | 2 +- .../tracing/decorators/JettySpanDecorator.java | 2 +- .../tracing/decorators/KafkaSpanDecorator.java | 2 +- .../tracing/decorators/MongoDBSpanDecorator.java | 2 +- .../camel/tracing/decorators/SqlSpanDecorator.java | 2 +- components/pom.xml | 1 + parent/pom.xml | 6 + 54 files changed, 1877 insertions(+), 109 deletions(-) diff --git a/camel-dependencies/pom.xml b/camel-dependencies/pom.xml index 30477ac1de9..43c45e40502 100644 --- a/camel-dependencies/pom.xml +++ b/camel-dependencies/pom.xml @@ -365,6 +365,7 @@ <maven-war-plugin-version>3.3.1</maven-war-plugin-version> <metrics-version>4.2.17</metrics-version> <micrometer-version>1.10.5</micrometer-version> + <micrometer-tracing-version>1.0.3</micrometer-tracing-version> <microprofile-config-version>3.0.2</microprofile-config-version> <microprofile-fault-tolerance-version>4.0.2</microprofile-fault-tolerance-version> <milo-version>0.6.8</milo-version> diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 9b0fcaca661..27523a90ad0 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -1213,6 +1213,11 @@ <artifactId>camel-oaipmh</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-observation</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-ognl</artifactId> diff --git a/components/camel-observation/pom.xml b/components/camel-observation/pom.xml new file mode 100644 index 00000000000..2aa0eba2c8d --- /dev/null +++ b/components/camel-observation/pom.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>4.0.0-SNAPSHOT</version> + </parent> + + + <artifactId>camel-observation</artifactId> + <packaging>jar</packaging> + <name>Camel :: Micrometer Observation</name> + <description>Observability using Micrometer Observation</description> + + <properties> + <firstVersion>3.21.0</firstVersion> + <label>monitoring,microservice</label> + <title>Micrometer Observability</title> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-support</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-tracing</artifactId> + </dependency> + + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-core</artifactId> + <version>${micrometer-version}</version> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-tracing</artifactId> + <version>${micrometer-tracing-version}</version> + </dependency> + + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-spring-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-tracing-integration-test</artifactId> + <version>${micrometer-tracing-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.opentelemetry</groupId> + <artifactId>opentelemetry-sdk-testing</artifactId> + <version>${opentelemetry-version}</version> + <scope>test</scope> + </dependency> + + </dependencies> + +</project> diff --git a/components/camel-observation/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-observation/src/generated/resources/META-INF/services/org/apache/camel/other.properties new file mode 100644 index 00000000000..6f2bf52d17c --- /dev/null +++ b/components/camel-observation/src/generated/resources/META-INF/services/org/apache/camel/other.properties @@ -0,0 +1,7 @@ +# Generated by camel build tools - do NOT edit this file! +name=observation +groupId=org.apache.camel +artifactId=camel-observation +version=4.0.0-SNAPSHOT +projectName=Camel :: Micrometer Observation +projectDescription=Observability using Micrometer Observation diff --git a/components/camel-observation/src/generated/resources/observation.json b/components/camel-observation/src/generated/resources/observation.json new file mode 100644 index 00000000000..617c15ed534 --- /dev/null +++ b/components/camel-observation/src/generated/resources/observation.json @@ -0,0 +1,15 @@ +{ + "other": { + "kind": "other", + "name": "observation", + "title": "Micrometer Observability", + "description": "Observability using Micrometer Observation", + "deprecated": false, + "firstVersion": "3.21.0", + "label": "monitoring,microservice", + "supportLevel": "Stable", + "groupId": "org.apache.camel", + "artifactId": "camel-observation", + "version": "4.0.0-SNAPSHOT" + } +} diff --git a/components/camel-observation/src/main/docs/observation.adoc b/components/camel-observation/src/main/docs/observation.adoc new file mode 100644 index 00000000000..e905cf239dc --- /dev/null +++ b/components/camel-observation/src/main/docs/observation.adoc @@ -0,0 +1,88 @@ += Micrometer Observability Component +:doctitle: Micrometer Observability +:shortname: observation +:artifactid: camel-observation +:description: Observability using Micrometer Observation +:since: 3.21 +:supportlevel: Stable +//Manually maintained attributes +:camel-spring-boot-name: observation + +*Since Camel {since}* + +The Micrometer Observation component is used for performing observability of incoming and +outgoing Camel messages using https://micrometer.io/docs/observation[Micrometer Observation]. + +By configuring the `ObservationRegistry` you can add behaviour to your observations such as metrics (e.g. via `Micrometer`) or tracing (e.g. via `OpenTelemetry` or `Brave`) or any custom behaviour. + +Events are captured for incoming and outgoing messages being sent to/from Camel. + +== Configuration + +The configuration properties for the Micrometer Observations are: + +[width="100%",cols="10%,10%,80%",options="header",] +|======================================================================= +|Option |Default |Description + +|excludePatterns | | Sets exclude pattern(s) that will disable tracing for Camel +messages that matches the pattern. The content is a Set<String> where the key is a pattern. The pattern +uses the rules from Intercept. +|encoding |false| Sets whether the header keys need to be encoded (connector specific) or not. The value is a boolean. +Dashes need for instances to be encoded for JMS property keys. + +|======================================================================= + + +=== Configuration + +Include the `camel-opentelemetry` component in your POM, along with any specific dependencies associated with the +chosen OpenTelemetry compliant Tracer. + +To explicitly configure OpenTelemetry support, instantiate the `OpenTelemetryTracer` and initialize the camel +context. You can optionally specify a `Tracer`, or alternatively it can be implicitly discovered using the +`Registry` + +[source,java] +-------------------------------------------------------------------------------------------------- +ObservationRegistry observationRegistry = ObservationRegistry.create(); +MicrometerObservationTracer micrometerObservationTracer = new MicrometerObservationTracer(); + +// This component comes from Micrometer Core - it's used for creation of metrics +MeterRegistry meterRegistry = new SimpleMeterRegistry(); + +// This component comes from Micrometer Tracing - it's an abstraction over tracers +io.micrometer.tracing.Tracer otelTracer = otelTracer(); +// This component comes from Micrometer Tracing - example of B3 header propagation via OpenTelemetry +OtelPropagator otelPropagator = new OtelPropagator(ContextPropagators.create(B3Propagator.injectingSingleHeader()), tracer); + +// Configuration ObservationRegistry for metrics +observationRegistry.observationConfig().observationHandler(new DefaultMeterObservationHandler(meterRegistry)); + +// Configuration ObservationRegistry for tracing +observationRegistry.observationConfig().observationHandler(new ObservationHandler.FirstMatchingCompositeObservationHandler(new CamelPropagatingSenderTracingObservationHandler<>(otelTracer, otelPropagator), new CamelPropagatingReceiverTracingObservationHandler<>(otelTracer, otelPropagator), new CamelDefaultTracingObservationHandler(otelTracer))); + +// Both components ObserationRegistry and MeterRegistry should be set manually or they will be resolved from CamelContext if present +micrometerObservationTracer.setObservationRegistry(observationRegistry); +micrometerObservationTracer.setTracer(otelTracer); + +// Initialize the MicrometerObservationTracer +micrometerObservationTracer.init(context); +-------------------------------------------------------------------------------------------------- + +== Spring Boot + +// TODO: Not done yet + +If you are using Spring Boot then you can add +the `camel-observation-starter` dependency, and turn on OpenTracing by annotating +the main class with `@CamelObservation`. + +The `MicrometerObservationTracer` will be implicitly obtained from the camel context's `Registry`, unless +a `MicrometerObservationTracer` bean has been defined by the application. + +include::spring-boot:partial$starter.adoc[] + +== MDC Logging + +When MDC Logging is enabled for the active Camel context the Trace ID and Span ID will be added and removed from the MDC for each route, the keys are `trace_id` and `span_id`, respectively. diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java b/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java new file mode 100644 index 00000000000..c551908aa4a --- /dev/null +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/AttributeProcessor.java @@ -0,0 +1,116 @@ +/* + * 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.observation; + +import io.micrometer.observation.Observation; +import org.apache.camel.AsyncCallback; +import org.apache.camel.Exchange; +import org.apache.camel.Expression; +import org.apache.camel.Traceable; +import org.apache.camel.spi.IdAware; +import org.apache.camel.spi.RouteIdAware; +import org.apache.camel.support.AsyncProcessorSupport; +import org.apache.camel.tracing.ActiveSpanManager; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A processor which adds a attribute on the active {@link Observation} with an {@link org.apache.camel.Expression} + */ +public class AttributeProcessor extends AsyncProcessorSupport implements Traceable, IdAware, RouteIdAware { + + private static final Logger LOG = LoggerFactory.getLogger(AttributeProcessor.class); + private final String attributeName; + private final Expression expression; + private String id; + private String routeId; + + public AttributeProcessor(String tagName, Expression expression) { + this.attributeName = ObjectHelper.notNull(tagName, "tagName"); + this.expression = ObjectHelper.notNull(expression, "expression"); + } + + @Override + public boolean process(Exchange exchange, AsyncCallback callback) { + try { + MicrometerObservationSpanAdapter camelSpan = (MicrometerObservationSpanAdapter) ActiveSpanManager.getSpan(exchange); + Observation observation = camelSpan.getMicrometerObservation(); + if (observation != null) { + String tag = expression.evaluate(exchange, String.class); + observation.highCardinalityKeyValue(attributeName, tag); + } else { + LOG.warn("Micrometer Observation: could not find managed span for exchange={}", exchange); + } + } catch (Exception e) { + exchange.setException(e); + } finally { + // callback must be invoked + callback.done(true); + } + + return true; + } + + @Override + public String getTraceLabel() { + return "attribute[" + attributeName + ", " + expression + "]"; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public String getRouteId() { + return routeId; + } + + @Override + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getAttributeName() { + return attributeName; + } + + public Expression getExpression() { + return expression; + } + + @Override + protected void doStart() throws Exception { + // noop + } + + @Override + protected void doStop() throws Exception { + // noop + } + + @Override + public String toString() { + return id; + } +} diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/GetCorrelationContextProcessor.java b/components/camel-observation/src/main/java/org/apache/camel/observation/GetCorrelationContextProcessor.java new file mode 100644 index 00000000000..44176ef1d08 --- /dev/null +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/GetCorrelationContextProcessor.java @@ -0,0 +1,109 @@ +/* + * 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.observation; + +import org.apache.camel.AsyncCallback; +import org.apache.camel.Exchange; +import org.apache.camel.Traceable; +import org.apache.camel.spi.IdAware; +import org.apache.camel.spi.RouteIdAware; +import org.apache.camel.support.AsyncProcessorSupport; +import org.apache.camel.tracing.ActiveSpanManager; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GetCorrelationContextProcessor extends AsyncProcessorSupport implements Traceable, IdAware, RouteIdAware { + private static final Logger LOG = LoggerFactory.getLogger(GetCorrelationContextProcessor.class); + private final String headerName; + private final String keyName; + private String id; + private String routeId; + + public GetCorrelationContextProcessor(String keyName, String headerName) { + this.keyName = ObjectHelper.notNull(keyName, "keyName"); + this.headerName = ObjectHelper.notNull(headerName, "headerName"); + } + + @Override + public boolean process(Exchange exchange, AsyncCallback callback) { + try { + MicrometerObservationSpanAdapter camelSpan = (MicrometerObservationSpanAdapter) ActiveSpanManager.getSpan(exchange); + if (camelSpan != null) { + String item = camelSpan.getContextPropagationItem(keyName); + exchange.getMessage().setHeader(headerName, item); + } else { + LOG.warn("Observation: could not find managed span for exchange={}", exchange); + } + } catch (Exception e) { + exchange.setException(e); + } finally { + // callback must be invoked + callback.done(true); + } + + return true; + } + + @Override + public String getTraceLabel() { + return "getCorrelationContext[" + keyName + ", " + headerName + "]"; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public String getRouteId() { + return routeId; + } + + @Override + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getKeyName() { + return keyName; + } + + public String getHeaderName() { + return headerName; + } + + @Override + protected void doStart() throws Exception { + // noop + } + + @Override + protected void doStop() throws Exception { + // noop + } + + @Override + public String toString() { + return id; + } +} diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationSpanAdapter.java b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationSpanAdapter.java new file mode 100644 index 00000000000..1eb6c4237d2 --- /dev/null +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationSpanAdapter.java @@ -0,0 +1,170 @@ +/* + * 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.observation; + +import java.util.Map; + +import io.micrometer.observation.Observation; +import io.micrometer.tracing.Baggage; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.TracingObservationHandler.TracingContext; +import org.apache.camel.tracing.SpanAdapter; +import org.apache.camel.tracing.Tag; + +public class MicrometerObservationSpanAdapter implements SpanAdapter { + private static final String DEFAULT_EVENT_NAME = "log"; + + private final Observation observation; + + private final Tracer tracer; + + MicrometerObservationSpanAdapter(Observation observation, Tracer tracer) { + this.observation = observation; + this.tracer = tracer; + } + + Observation getMicrometerObservation() { + return this.observation; + } + + @Override + public void setComponent(String component) { + this.observation.lowCardinalityKeyValue("component", component); + } + + @Override + public void setError(boolean error) { + this.observation.lowCardinalityKeyValue("error", String.valueOf(error)); + } + + @Override + public void setTag(Tag key, String value) { + this.observation.highCardinalityKeyValue(key.toString(), value); + } + + @Override + public void setTag(Tag key, Number value) { + setTag(key, value.toString()); + } + + @Override + public void setTag(String key, String value) { + this.observation.highCardinalityKeyValue(key, value); + } + + @Override + public void setTag(String key, Number value) { + setTag(key, value.toString()); + } + + @Override + public void setTag(String key, Boolean value) { + setTag(key, value.toString()); + } + + @Override + public void setLowCardinalityTag(Tag key, String value) { + observation.lowCardinalityKeyValue(key.toString(), value); + } + + @Override + public void setLowCardinalityTag(Tag key, Number value) { + observation.lowCardinalityKeyValue(key.toString(), value.toString()); + } + + @Override + public void setLowCardinalityTag(String key, String value) { + observation.lowCardinalityKeyValue(key, value); + } + + @Override + public void setLowCardinalityTag(String key, Number value) { + observation.lowCardinalityKeyValue(key, value.toString()); + } + + @Override + public void setLowCardinalityTag(String key, Boolean value) { + observation.lowCardinalityKeyValue(key, value.toString()); + } + + @Override + public void log(Map<String, String> fields) { + String event = fields.get("event"); + if ("error".equalsIgnoreCase(event)) { + if (fields.containsKey("message")) { + observation.error(new RuntimeException(fields.get("message"))); + } else { + setError(true); + } + } else { + observation.event(() -> getMessageNameFromFields(fields)); + } + } + + @Override + public String traceId() { + TracingContext tracingContext = getTracingContext(); + return tracingContext.getSpan() != null ? tracingContext.getSpan().context().traceId() : null; + } + + private TracingContext getTracingContext() { + return observation.getContextView().getOrDefault(TracingContext.class, new TracingContext()); + } + + @Override + public String spanId() { + TracingContext tracingContext = getTracingContext(); + return tracingContext.getSpan() != null ? tracingContext.getSpan().context().spanId() : null; + } + + @Override + public AutoCloseable makeCurrent() { + return observation.openScope(); + } + + String getMessageNameFromFields(Map<String, ?> fields) { + Object eventValue = fields == null ? null : fields.get("message"); + if (eventValue != null) { + return eventValue.toString(); + } + + return DEFAULT_EVENT_NAME; + } + + public void setCorrelationContextItem(String key, String value) { + Baggage baggage = tracer.createBaggage(key); + Span span = getTracingContext().getSpan(); + if (span == null) { + return; + } + baggage.set(span.context(), value); + } + + public String getContextPropagationItem(String key) { + Span span = getTracingContext().getSpan(); + if (span == null) { + return null; + } + Baggage baggage = tracer.getBaggage(span.context(), key); + if (baggage != null) { + return baggage.get(span.context()); + } + return null; + } + +} diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java new file mode 100644 index 00000000000..5d10ac65780 --- /dev/null +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/MicrometerObservationTracer.java @@ -0,0 +1,180 @@ +/* + * 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.observation; + +import java.util.Set; + +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.transport.ReceiverContext; +import io.micrometer.observation.transport.RequestReplyReceiverContext; +import io.micrometer.observation.transport.RequestReplySenderContext; +import io.micrometer.observation.transport.SenderContext; +import io.micrometer.tracing.Tracer; +import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.api.management.ManagedResource; +import org.apache.camel.tracing.ExtractAdapter; +import org.apache.camel.tracing.InjectAdapter; +import org.apache.camel.tracing.SpanAdapter; +import org.apache.camel.tracing.SpanDecorator; +import org.apache.camel.tracing.SpanKind; +import org.apache.camel.tracing.decorators.AbstractInternalSpanDecorator; + +@ManagedResource(description = "MicrometerObservationTracer") +public class MicrometerObservationTracer extends org.apache.camel.tracing.Tracer { + + static final String SPAN_DECORATOR_INTERNAL = "camel.micrometer.abstract-internal"; + + private static final String CAMEL_CONTEXT_NAME = "camel.component"; + + private Tracer tracer = Tracer.NOOP; + + private ObservationRegistry observationRegistry; + + public ObservationRegistry getObservationRegistry() { + return observationRegistry; + } + + public void setObservationRegistry(ObservationRegistry observationRegistry) { + this.observationRegistry = observationRegistry; + } + + public Tracer getTracer() { + return tracer; + } + + public void setTracer(Tracer tracer) { + this.tracer = tracer; + } + + private Observation.Context spanKindToContextOnExtract( + org.apache.camel.tracing.SpanKind kind, SpanDecorator sd, Exchange exchange) { + ExtractAdapter adapter = sd.getExtractAdapter(exchange.getIn().getHeaders(), encoding); + switch (kind) { + case PRODUCER: + throw new UnsupportedOperationException("You can't extract when sending a message"); + case SPAN_KIND_SERVER: + RequestReplyReceiverContext<Object, Message> replyReceiverContext + = new RequestReplyReceiverContext<>((carrier, key) -> String.valueOf(adapter.get(key))); + replyReceiverContext.setResponse(exchange.getMessage()); + replyReceiverContext.setCarrier(exchange.getIn()); + return replyReceiverContext; + case CONSUMER: + case SPAN_KIND_CLIENT: + ReceiverContext<Message> receiverContext + = new ReceiverContext<>((carrier, key) -> String.valueOf(adapter.get(key))); + receiverContext.setCarrier(exchange.getIn()); + return receiverContext; + default: + return new Observation.Context(); + } + } + + private Observation.Context spanKindToContextOnInject( + org.apache.camel.tracing.SpanKind kind, InjectAdapter adapter, Exchange exchange) { + switch (kind) { + case SPAN_KIND_CLIENT: + RequestReplySenderContext<Object, Message> senderContext + = new RequestReplySenderContext<>((carrier, key, value) -> adapter.put(key, value)); + senderContext.setResponse(exchange.getMessage()); + senderContext.setCarrier(exchange.getIn()); + return senderContext; + case PRODUCER: + SenderContext<Message> context = new SenderContext<>((carrier, key, value) -> adapter.put(key, value)); + context.setCarrier(exchange.getIn()); + return context; + case SPAN_KIND_SERVER: + case CONSUMER: + throw new UnsupportedOperationException("You can't inject when receiving a message"); + default: + return new Observation.Context(); + } + } + + @Override + protected void initTracer() { + if (observationRegistry == null) { + Set<ObservationRegistry> registries = getCamelContext().getRegistry().findByType(ObservationRegistry.class); + if (registries.size() == 1) { + observationRegistry = registries.iterator().next(); + } + } + + if (tracer == null) { + Set<Tracer> tracers = getCamelContext().getRegistry().findByType(Tracer.class); + if (tracers.size() == 1) { + tracer = tracers.iterator().next(); + } + } + + if (observationRegistry == null) { + // No Observation Registry is available, so setup Noop + observationRegistry = ObservationRegistry.NOOP; + } + } + + @Override + protected SpanAdapter startSendingEventSpan( + String operationName, SpanKind kind, SpanAdapter parentObservation, Exchange exchange, + InjectAdapter injectAdapter) { + Observation.Context context = spanKindToContextOnInject(kind, injectAdapter, exchange); + Observation observation = Observation.createNotStarted(CAMEL_CONTEXT_NAME, () -> context, observationRegistry); + observation.contextualName(operationName); + if (parentObservation != null) { + observation.parentObservation(getParentObservation(parentObservation)); + } + return new MicrometerObservationSpanAdapter(observation.start(), tracer); + } + + @Override + protected void initContextPropagators() { + + } + + private static Observation getParentObservation(SpanAdapter parentObservation) { + MicrometerObservationSpanAdapter observationWrapper = (MicrometerObservationSpanAdapter) parentObservation; + return observationWrapper.getMicrometerObservation(); + } + + @Override + protected SpanAdapter startExchangeBeginSpan( + Exchange exchange, SpanDecorator sd, String operationName, org.apache.camel.tracing.SpanKind kind, + SpanAdapter parent) { + boolean parentPresent = parent != null; + Observation.Context context = spanKindToContextOnExtract(kind, sd, exchange); + boolean internalSpanDecorator = sd instanceof AbstractInternalSpanDecorator; + context.put(SPAN_DECORATOR_INTERNAL, internalSpanDecorator); + Observation observation = Observation.createNotStarted(operationName, () -> context, observationRegistry); + if (parentPresent) { + observation.parentObservation(getParentObservation(parent)); + } + return new MicrometerObservationSpanAdapter(observation.start(), tracer); + } + + @Override + protected void finishSpan(SpanAdapter span) { + MicrometerObservationSpanAdapter observationSpanAdapter = (MicrometerObservationSpanAdapter) span; + observationSpanAdapter.getMicrometerObservation().stop(); + } + + @Override + protected void inject(SpanAdapter span, InjectAdapter adapter) { + // Inject happens on start of an observation + } + +} diff --git a/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java b/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java new file mode 100644 index 00000000000..80d329ffafa --- /dev/null +++ b/components/camel-observation/src/main/java/org/apache/camel/observation/SetCorrelationContextProcessor.java @@ -0,0 +1,115 @@ +/* + * 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.observation; + +import org.apache.camel.AsyncCallback; +import org.apache.camel.Exchange; +import org.apache.camel.Expression; +import org.apache.camel.Traceable; +import org.apache.camel.spi.IdAware; +import org.apache.camel.spi.RouteIdAware; +import org.apache.camel.support.AsyncProcessorSupport; +import org.apache.camel.tracing.ActiveSpanManager; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author rvargasp + */ +public class SetCorrelationContextProcessor extends AsyncProcessorSupport implements Traceable, IdAware, RouteIdAware { + + private static final Logger LOG = LoggerFactory.getLogger(SetCorrelationContextProcessor.class); + + private String id; + private String routeId; + private final String baggageName; + private final Expression expression; + + public SetCorrelationContextProcessor(String baggageName, Expression expression) { + this.baggageName = ObjectHelper.notNull(baggageName, "baggageName"); + this.expression = ObjectHelper.notNull(expression, "expression"); + } + + @Override + public boolean process(Exchange exchange, AsyncCallback callback) { + try { + MicrometerObservationSpanAdapter camelSpan = (MicrometerObservationSpanAdapter) ActiveSpanManager.getSpan(exchange); + if (camelSpan != null) { + String item = expression.evaluate(exchange, String.class); + camelSpan.setCorrelationContextItem(baggageName, item); + } else { + LOG.warn("OpenTelemetry: could not find managed span for exchange={}", exchange); + } + } catch (Exception e) { + exchange.setException(e); + } finally { + // callback must be invoked + callback.done(true); + } + + return true; + } + + @Override + public String getTraceLabel() { + return "setCorrelationContext[" + baggageName + ", " + expression + "]"; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public String getRouteId() { + return routeId; + } + + @Override + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getBaggageName() { + return baggageName; + } + + public Expression getExpression() { + return expression; + } + + @Override + protected void doStart() throws Exception { + // noop + } + + @Override + protected void doStop() throws Exception { + // noop + } + + @Override + public String toString() { + return id; + } +} diff --git a/components/camel-observation/src/main/resources/META-INF/services/org.apache.camel.tracing.SpanDecorator b/components/camel-observation/src/main/resources/META-INF/services/org.apache.camel.tracing.SpanDecorator new file mode 100644 index 00000000000..0887c54b427 --- /dev/null +++ b/components/camel-observation/src/main/resources/META-INF/services/org.apache.camel.tracing.SpanDecorator @@ -0,0 +1,52 @@ +# +# 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. +# + +org.apache.camel.tracing.decorators.AhcSpanDecorator +org.apache.camel.tracing.decorators.AmqpSpanDecorator +org.apache.camel.tracing.decorators.AzureServiceBusSpanDecorator +org.apache.camel.tracing.decorators.CometdSpanDecorator +org.apache.camel.tracing.decorators.CometdsSpanDecorator +org.apache.camel.tracing.decorators.CqlSpanDecorator +org.apache.camel.tracing.decorators.DirectSpanDecorator +org.apache.camel.tracing.decorators.DisruptorSpanDecorator +org.apache.camel.tracing.decorators.DisruptorvmSpanDecorator +org.apache.camel.tracing.decorators.ElasticsearchSpanDecorator +org.apache.camel.tracing.decorators.HttpSpanDecorator +org.apache.camel.tracing.decorators.HttpsSpanDecorator +org.apache.camel.tracing.decorators.IronmqSpanDecorator +org.apache.camel.tracing.decorators.JdbcSpanDecorator +org.apache.camel.tracing.decorators.JettySpanDecorator +org.apache.camel.tracing.decorators.JmsSpanDecorator +org.apache.camel.tracing.decorators.KafkaSpanDecorator +org.apache.camel.tracing.decorators.LogSpanDecorator +org.apache.camel.tracing.decorators.MongoDBSpanDecorator +org.apache.camel.tracing.decorators.NettyHttpSpanDecorator +org.apache.camel.tracing.decorators.NatsSpanDecorator +org.apache.camel.tracing.decorators.NsqSpanDecorator +org.apache.camel.tracing.decorators.PahoSpanDecorator +org.apache.camel.tracing.decorators.PlatformHttpSpanDecorator +org.apache.camel.tracing.decorators.RestSpanDecorator +org.apache.camel.tracing.decorators.SedaSpanDecorator +org.apache.camel.tracing.decorators.ServletSpanDecorator +org.apache.camel.tracing.decorators.SjmsSpanDecorator +org.apache.camel.tracing.decorators.Sjms2SpanDecorator +org.apache.camel.tracing.decorators.SqlSpanDecorator +org.apache.camel.tracing.decorators.StompSpanDecorator +org.apache.camel.tracing.decorators.TimerSpanDecorator +org.apache.camel.tracing.decorators.UndertowSpanDecorator +org.apache.camel.tracing.decorators.VertxHttpSpanDecorator + diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/ABCRouteTest.java similarity index 66% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/ABCRouteTest.java index 320f281d3f9..1822cdb1ad2 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/ABCRouteTest.java @@ -14,21 +14,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class ABCRouteTest extends CamelOpenTelemetryTestSupport { +class ABCRouteTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setKind(SpanKind.SERVER) + .setParentId(1), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setKind(SpanKind.SERVER) + .setParentId(3), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setKind(SpanKind.SERVER) + .setParentId(5), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.CLIENT) + .setParentId(6), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER) + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; ABCRouteTest() { diff --git a/components/camel-observation/src/test/java/org/apache/camel/observation/CamelMicrometerObservationTestSupport.java b/components/camel-observation/src/test/java/org/apache/camel/observation/CamelMicrometerObservationTestSupport.java new file mode 100644 index 00000000000..c3ffbb9c458 --- /dev/null +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/CamelMicrometerObservationTestSupport.java @@ -0,0 +1,267 @@ +/* + * 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.observation; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.observation.ObservationHandler; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.tracing.handler.DefaultTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; +import io.micrometer.tracing.otel.bridge.OtelBaggageManager; +import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext; +import io.micrometer.tracing.otel.bridge.OtelPropagator; +import io.micrometer.tracing.otel.bridge.OtelTracer; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import org.apache.camel.CamelContext; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.apache.camel.tracing.SpanDecorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class CamelMicrometerObservationTestSupport extends CamelTestSupport { + + static final AttributeKey<String> CAMEL_URI_KEY = AttributeKey.stringKey("camel-uri"); + static final AttributeKey<String> COMPONENT_KEY = AttributeKey.stringKey("component"); + static final AttributeKey<String> PRE_KEY = AttributeKey.stringKey("pre"); + static final AttributeKey<String> POST_KEY = AttributeKey.stringKey("post"); + + private static final Logger LOG = LoggerFactory.getLogger(CamelMicrometerObservationTestSupport.class); + + private InMemorySpanExporter inMemorySpanExporter = InMemorySpanExporter.create(); + private SpanTestData[] expected; + private Tracer tracer; + private MicrometerObservationTracer micrometerObservationTracer; + private SdkTracerProvider tracerFactory; + + private MeterRegistry meterRegistry = new SimpleMeterRegistry(); + + private ObservationRegistry observationRegistry; + + CamelMicrometerObservationTestSupport(SpanTestData[] expected) { + this.expected = expected; + } + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + micrometerObservationTracer = new MicrometerObservationTracer(); + + tracerFactory = SdkTracerProvider.builder() + .addSpanProcessor(new LoggingSpanProcessor()) + .addSpanProcessor(SimpleSpanProcessor.create(inMemorySpanExporter)).build(); + + tracer = tracerFactory.get("tracerTest"); + + observationRegistry = ObservationRegistry.create(); + observationRegistry.observationConfig().observationHandler(new DefaultMeterObservationHandler(meterRegistry)); + + io.micrometer.tracing.Tracer otelTracer = otelTracer(); + OtelPropagator otelPropagator + = new OtelPropagator(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())), tracer); + observationRegistry.observationConfig().observationHandler( + new ObservationHandler.FirstMatchingCompositeObservationHandler( + new PropagatingSenderTracingObservationHandler<>(otelTracer, otelPropagator), + new PropagatingReceiverTracingObservationHandler<>(otelTracer, otelPropagator), + new DefaultTracingObservationHandler(otelTracer))); + + micrometerObservationTracer.setObservationRegistry(observationRegistry); + // if you want baggage + micrometerObservationTracer.setTracer(otelTracer); + micrometerObservationTracer.setExcludePatterns(getExcludePatterns()); + micrometerObservationTracer.addDecorator(new TestSEDASpanDecorator()); + micrometerObservationTracer.init(context); + return context; + } + + private OtelTracer otelTracer() { + OtelCurrentTraceContext otelCurrentTraceContext = new OtelCurrentTraceContext(); + OtelBaggageManager otelBaggageManager + = new OtelBaggageManager(otelCurrentTraceContext, Collections.emptyList(), Collections.emptyList()); + return new OtelTracer(tracer, otelCurrentTraceContext, o -> { + + }, otelBaggageManager); + } + + protected Set<String> getExcludePatterns() { + return new HashSet<>(); + } + + protected void verify() { + verify(expected, false); + } + + protected void verify(boolean async) { + verify(expected, async); + } + + protected List<SpanData> verify(SpanTestData[] expected, boolean async) { + List<SpanData> spans = inMemorySpanExporter.getFinishedSpanItems(); + spans.forEach(mockSpan -> { + LOG.info("Span: " + mockSpan); + LOG.info("\tComponent: " + mockSpan.getAttributes().get(COMPONENT_KEY)); + LOG.info("\tTags: " + mockSpan.getAttributes()); + LOG.info("\tLogs: "); + + }); + assertEquals(expected.length, spans.size(), "Incorrect number of spans"); + verifySameTrace(); + + if (async) { + final List<SpanData> unsortedSpans = spans; + spans = Arrays.stream(expected) + .map(td -> findSpan(td, unsortedSpans)).distinct().collect(Collectors.toList()); + assertEquals(expected.length, spans.size(), "Incorrect number of spans after sorting"); + } + + for (int i = 0; i < expected.length; i++) { + verifySpan(i, expected, spans); + } + + return spans; + } + + protected SpanData findSpan(SpanTestData testdata, List<SpanData> spans) { + return spans.stream().filter(s -> { + boolean matched = s.getName().equals(testdata.getOperation()); + if (s.getAttributes().get(CAMEL_URI_KEY) != null) { + matched = matched && s.getAttributes().get(CAMEL_URI_KEY).equals(testdata.getUri()); + } + matched = matched && s.getKind().equals(testdata.getKind()); + return matched; + }).findFirst().orElse(null); + } + + protected Tracer getTracer() { + return tracer; + } + + protected void verifyTraceSpanNumbers(int numOfTraces, int numSpansPerTrace) { + Map<String, List<SpanData>> traces = new HashMap<>(); + + List<SpanData> finishedSpans = inMemorySpanExporter.getFinishedSpanItems(); + // Sort spans into separate traces + for (int i = 0; i < finishedSpans.size(); i++) { + List<SpanData> spans = traces.computeIfAbsent(finishedSpans.get(i).getTraceId(), k -> new ArrayList<>()); + spans.add(finishedSpans.get(i)); + } + + assertEquals(numOfTraces, traces.size()); + + for (Map.Entry<String, List<SpanData>> spans : traces.entrySet()) { + assertEquals(numSpansPerTrace, spans.getValue().size()); + } + } + + protected void verifySpan(int index, SpanTestData[] testdata, List<SpanData> spans) { + SpanData span = spans.get(index); + SpanTestData td = testdata[index]; + + String component = span.getAttributes().get(COMPONENT_KEY); + assertNotNull(component); + + if (td.getUri() != null) { + assertEquals(SpanDecorator.CAMEL_COMPONENT + URI.create(td.getUri()).getScheme(), component, td.getLabel()); + } + + if ("camel-seda".equals(component)) { + assertNotNull(span.getAttributes().get(PRE_KEY)); + assertNotNull(span.getAttributes().get(POST_KEY)); + } + + assertEquals(td.getOperation(), span.getName(), td.getLabel()); + + assertEquals(td.getKind(), span.getKind(), td.getLabel()); + + if (!td.getLogMessages().isEmpty()) { + assertEquals(td.getLogMessages().size(), span.getEvents().size(), td.getLabel()); + for (int i = 0; i < td.getLogMessages().size(); i++) { + assertEquals(td.getLogMessages().get(i), span.getEvents().get(i).getName()); // The difference between OTel directly and Observation is that we log with a name + } + } + + if (td.getParentId() != -1) { + assertEquals(spans.get(td.getParentId()).getSpanId(), span.getParentSpanId(), td.getLabel()); + } + if (!td.getTags().isEmpty()) { + for (Map.Entry<String, String> entry : td.getTags().entrySet()) { + assertEquals(entry.getValue(), span.getAttributes().get(AttributeKey.stringKey(entry.getKey()))); + } + } + + } + + protected void verifySameTrace() { + assertEquals(1, inMemorySpanExporter.getFinishedSpanItems().stream().map(s -> s.getTraceId()).distinct().count()); + } + + private static class LoggingSpanProcessor implements SpanProcessor { + private static final Logger LOG = LoggerFactory.getLogger(LoggingSpanProcessor.class); + + @Override + public void onStart(Context context, ReadWriteSpan readWriteSpan) { + LOG.debug("Span started: name - '{}', kind - '{}', id - '{}-{}", readWriteSpan.getName(), readWriteSpan.getKind(), + readWriteSpan.getSpanContext().getTraceId(), readWriteSpan.getSpanContext().getSpanId()); + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(ReadableSpan readableSpan) { + LOG.debug("Span ended: name - '{}', kind - '{}', id - '{}-{}", readableSpan.getName(), readableSpan.getKind(), + readableSpan.getSpanContext().getTraceId(), readableSpan.getSpanContext().getSpanId()); + } + + @Override + public boolean isEndRequired() { + return true; + } + } +} diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/ClientRecipientListRouteTest.java similarity index 66% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/ClientRecipientListRouteTest.java index 1fa8ff97fad..10f87b95545 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/ClientRecipientListRouteTest.java @@ -14,22 +14,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class ClientRecipientListRouteTest extends CamelOpenTelemetryTestSupport { +class ClientRecipientListRouteTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3), + .setKind(SpanKind.SERVER) + .setParentId(1), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.SERVER) .setParentId(3), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setParentId(6) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(3), + .setKind(SpanKind.SERVER) + .setParentId(5), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER) + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; ClientRecipientListRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/CurrentSpanTest.java similarity index 91% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/CurrentSpanTest.java index b28da91eb1d..158e5b8290a 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/CurrentSpanTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; import java.io.IOException; import java.util.List; @@ -24,11 +24,11 @@ import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.internal.data.ExceptionEventData; import org.apache.camel.AsyncCallback; import org.apache.camel.CamelContext; import org.apache.camel.CamelExecutionException; @@ -50,10 +50,10 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -class CurrentSpanTest extends CamelOpenTelemetryTestSupport { +class CurrentSpanTest extends CamelMicrometerObservationTestSupport { CurrentSpanTest() { super(new SpanTestData[0]); } @@ -76,6 +76,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { new SpanTestData().setLabel("syncmock:result").setUri("syncmock://result").setOperation("syncmock") .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("direct:bar").setUri("direct://bar").setOperation("bar") + .setKind(SpanKind.SERVER), + new SpanTestData().setLabel("direct:bar").setUri("direct://bar").setOperation("bar").setKind(SpanKind.CLIENT) }; // sync pipeline @@ -94,6 +96,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { new SpanTestData().setLabel("asyncmock1:result").setUri("asyncmock1://result").setOperation("asyncmock1") .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("direct:foo").setUri("direct://foo").setOperation("foo") + .setKind(SpanKind.SERVER), + new SpanTestData().setLabel("direct:foo").setUri("direct://foo").setOperation("foo").setKind(SpanKind.CLIENT) }; // sync to async pipeline @@ -112,7 +116,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { SpanTestData[] expectedSpans = { new SpanTestData().setLabel("syncmock:result").setUri("syncmock://result").setOperation("syncmock") .setKind(SpanKind.CLIENT), - new SpanTestData().setLabel("asyncmock1:start").setUri("asyncmock1://start").setOperation("asyncmock1"), + new SpanTestData().setLabel("asyncmock1:start").setUri("asyncmock1://start").setOperation("asyncmock1") + .setKind(SpanKind.SERVER), new SpanTestData().setLabel("asyncmock1:start").setUri("asyncmock1://start").setOperation("asyncmock1") .setKind(SpanKind.CLIENT), }; @@ -130,7 +135,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { SpanTestData[] expectedSpans = { new SpanTestData().setLabel("asyncmock2:result").setUri("asyncmock2://result").setOperation("asyncmock2") .setKind(SpanKind.CLIENT), - new SpanTestData().setLabel("asyncmock2:start").setUri("asyncmock2://start").setOperation("asyncmock2"), + new SpanTestData().setLabel("asyncmock2:start").setUri("asyncmock2://start").setOperation("asyncmock2") + .setKind(SpanKind.SERVER), new SpanTestData().setLabel("asyncmock2:start").setUri("asyncmock2://start").setOperation("asyncmock2") .setKind(SpanKind.CLIENT), }; @@ -146,7 +152,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { @Test void testAsyncFailure() { SpanTestData[] expectedSpans = { - new SpanTestData().setLabel("asyncmock:fail").setUri("asyncmock://fail").setOperation("asyncmock"), + new SpanTestData().setLabel("asyncmock:fail").setUri("asyncmock://fail").setOperation("asyncmock") + .setKind(SpanKind.SERVER), new SpanTestData().setLabel("asyncmock:fail").setUri("asyncmock://fail").setOperation("asyncmock") .setKind(SpanKind.CLIENT), }; @@ -157,8 +164,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { List<SpanData> spans = verify(expectedSpans, false); assertEquals(spans.get(0).getParentSpanId(), spans.get(1).getSpanId()); - assertTrue(spans.get(0).getAttributes().get(AttributeKey.booleanKey("error"))); - assertTrue(spans.get(1).getAttributes().get(AttributeKey.booleanKey("error"))); + assertNotNull(((ExceptionEventData) spans.get(0).getEvents().get(0)).getException()); + assertNotNull(((ExceptionEventData) spans.get(1).getEvents().get(0)).getException()); } @@ -172,6 +179,9 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { new SpanTestData().setLabel("syncmock:result").setUri("syncmock://result").setOperation("syncmock") .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("direct:start").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER), + new SpanTestData().setLabel("direct:start").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; // sync pipeline @@ -191,7 +201,7 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { assertFalse(Span.current().getSpanContext().isValid()); } - verifyTraceSpanNumbers(30, 10); + verifyTraceSpanNumbers(30, 11); } @Override diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/CustomComponentNameRouteTest.java similarity index 68% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/CustomComponentNameRouteTest.java index 27ddfdbd462..f815c995baa 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/CustomComponentNameRouteTest.java @@ -14,22 +14,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class CustomComponentNameRouteTest extends CamelOpenTelemetryTestSupport { +class CustomComponentNameRouteTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("myseda:b server").setUri("myseda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setKind(SpanKind.SERVER) + .setParentId(1), + new SpanTestData().setLabel("myseda:b server").setUri("myseda://b").setOperation("b").setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("myseda:c server").setUri("myseda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setKind(SpanKind.SERVER) + .setParentId(3), + new SpanTestData().setLabel("myseda:c server").setUri("myseda://c").setOperation("c").setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("myseda:a server").setUri("myseda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setKind(SpanKind.SERVER) + .setParentId(5), + new SpanTestData().setLabel("myseda:a server").setUri("myseda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER).setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; CustomComponentNameRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/MulticastParallelRouteTest.java similarity index 67% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/MulticastParallelRouteTest.java index 15fb907365e..ee8e38b0a78 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/MulticastParallelRouteTest.java @@ -14,22 +14,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class MulticastParallelRouteTest extends CamelOpenTelemetryTestSupport { +class MulticastParallelRouteTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setKind(SpanKind.SERVER) + .setParentId(1), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setParentId(4) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setKind(SpanKind.SERVER) + .setParentId(3), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("routing at c"), + .setParentId(4) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setKind(SpanKind.SERVER) + .setParentId(5), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER).setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; MulticastParallelRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/MulticastRouteTest.java similarity index 69% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/MulticastRouteTest.java index c464d593682..2b50680b2f9 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/MulticastRouteTest.java @@ -14,22 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class MulticastRouteTest extends CamelOpenTelemetryTestSupport { +class MulticastRouteTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2), + .setKind(SpanKind.SERVER) + .setParentId(1), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2), - new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.SERVER) .setParentId(3), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.SERVER) + .setParentId(5), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.CLIENT) + .setParentId(6), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER).setParentId(7), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start").setKind(SpanKind.CLIENT) }; MulticastRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/RouteConcurrentTest.java similarity index 78% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/RouteConcurrentTest.java index 964932bdb95..9ab0dbaabdd 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/RouteConcurrentTest.java @@ -14,10 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; import java.util.concurrent.TimeUnit; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.NotifyBuilder; import org.apache.camel.builder.RouteBuilder; @@ -25,12 +26,20 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; -class RouteConcurrentTest extends CamelOpenTelemetryTestSupport { +class RouteConcurrentTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { - new SpanTestData().setLabel("seda:foo server").setUri("seda://foo?concurrentConsumers=5").setOperation("foo"), + new SpanTestData().setLabel("seda:foo server").setUri("seda://foo").setOperation("foo") + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:bar server").setUri("seda://bar").setOperation("bar") + .setParentId(2) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:foo server").setUri("seda://foo?concurrentConsumers=5").setOperation("foo") + .setKind(SpanKind.SERVER) + .setParentId(0), new SpanTestData().setLabel("seda:bar server").setUri("seda://bar?concurrentConsumers=5").setOperation("bar") - .setParentId(0) + .setKind(SpanKind.SERVER) + .setParentId(1), }; RouteConcurrentTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/SpanProcessorsTest.java similarity index 74% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/SpanProcessorsTest.java index 8be835be8b8..706e0ea8691 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/SpanProcessorsTest.java @@ -14,8 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.Exchange; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; @@ -23,17 +24,31 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -class SpanProcessorsTest extends CamelOpenTelemetryTestSupport { +class SpanProcessorsTest extends CamelMicrometerObservationTestSupport { private static final SpanTestData[] TEST_DATA = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b") + .setKind(SpanKind.SERVER) + .setParentId(1) .addTag("b-tag", "request-header-value"), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setKind(SpanKind.SERVER) + .setParentId(3), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.SERVER) + .setParentId(5), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setKind(SpanKind.CLIENT) + .setParentId(6), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.SERVER).setParentId(7), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start").setKind(SpanKind.CLIENT) }; SpanProcessorsTest() { diff --git a/components/camel-observation/src/test/java/org/apache/camel/observation/SpanTestData.java b/components/camel-observation/src/test/java/org/apache/camel/observation/SpanTestData.java new file mode 100644 index 00000000000..aff1d7ea7ee --- /dev/null +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/SpanTestData.java @@ -0,0 +1,107 @@ +/* + * 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.observation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.trace.SpanKind; + +public class SpanTestData { + + private String label; + private String uri; + private String operation; + private SpanKind kind = SpanKind.INTERNAL; + private int parentId = -1; + private List<String> logMessages = new ArrayList<>(); + private Map<String, String> tags = new HashMap<>(); + private ArrayList<SpanTestData> childs = new ArrayList<>(); + private Map<String, String> baggage = new HashMap<>(); + + public String getLabel() { + return label; + } + + public SpanTestData setLabel(String label) { + this.label = label; + return this; + } + + public String getUri() { + return uri; + } + + public SpanTestData setUri(String uri) { + this.uri = uri; + return this; + } + + public String getOperation() { + return operation; + } + + public SpanTestData setOperation(String operation) { + this.operation = operation; + return this; + } + + public SpanKind getKind() { + return kind; + } + + public SpanTestData setKind(SpanKind kind) { + this.kind = kind; + return this; + } + + public int getParentId() { + return parentId; + } + + public SpanTestData setParentId(int parentId) { + this.parentId = parentId; + return this; + } + + public SpanTestData addLogMessage(String mesg) { + logMessages.add(mesg); + return this; + } + + public List<String> getLogMessages() { + return logMessages; + } + + public SpanTestData addTag(String key, String val) { + tags.put(key, val); + return this; + } + + public Map<String, String> getTags() { + return tags; + } + + public SpanTestData setChilds(SpanTestData[] childs) { + Collections.addAll(this.childs, childs); + return this; + } + +} diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java b/components/camel-observation/src/test/java/org/apache/camel/observation/TestSEDASpanDecorator.java similarity index 67% copy from components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/TestSEDASpanDecorator.java index 12745783cb6..a2d89df37e2 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/TestSEDASpanDecorator.java @@ -14,35 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.tracing.decorators; +package org.apache.camel.observation; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.tracing.SpanAdapter; -import org.apache.camel.tracing.Tag; +import org.apache.camel.tracing.decorators.SedaSpanDecorator; -public class JdbcSpanDecorator extends AbstractSpanDecorator { - - @Override - public String getComponent() { - return "jdbc"; - } - - @Override - public String getComponentClassName() { - return "org.apache.camel.component.jdbc.JdbcComponent"; - } +class TestSEDASpanDecorator extends SedaSpanDecorator { @Override public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); + span.setTag("pre", "test"); + } - span.setTag(Tag.DB_TYPE, "sql"); - - Object body = exchange.getIn().getBody(); - if (body instanceof String) { - span.setTag(Tag.DB_STATEMENT, (String) body); - } + @Override + public void post(SpanAdapter span, Exchange exchange, Endpoint endpoint) { + super.post(span, exchange, endpoint); + span.setTag("post", "test"); } } diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceTest.java similarity index 74% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceTest.java index 563a089ce41..82872b6bb64 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceTest.java @@ -14,18 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class TwoServiceTest extends CamelOpenTelemetryTestSupport { +class TwoServiceTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("ServiceB server").setUri("direct://ServiceB").setOperation("ServiceB") - .setParentId(1), + .setParentId(1) + .setKind(SpanKind.SERVER), + new SpanTestData().setLabel("ServiceB server").setUri("direct://ServiceB").setOperation("ServiceB") + .setParentId(2) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setParentId(3) + .setKind(SpanKind.SERVER), new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setKind(SpanKind.CLIENT) }; TwoServiceTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java b/components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceWithExcludeTest.java similarity index 83% copy from components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java copy to components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceWithExcludeTest.java index dc8fd800ca6..76b3f494c18 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/TwoServiceWithExcludeTest.java @@ -14,19 +14,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.opentelemetry; +package org.apache.camel.observation; import java.util.Collections; import java.util.Set; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; -class TwoServiceWithExcludeTest extends CamelOpenTelemetryTestSupport { +class TwoServiceWithExcludeTest extends CamelMicrometerObservationTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setParentId(1) + .setKind(SpanKind.SERVER), + new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setKind(SpanKind.CLIENT) }; TwoServiceWithExcludeTest() { diff --git a/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelDefaultTracingObservationHandler.java b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelDefaultTracingObservationHandler.java new file mode 100644 index 00000000000..1e9d37c9531 --- /dev/null +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelDefaultTracingObservationHandler.java @@ -0,0 +1,54 @@ +/* + * 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.observation.otel; + +import io.micrometer.common.util.StringUtils; +import io.micrometer.observation.Observation; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.DefaultTracingObservationHandler; + +public class CamelDefaultTracingObservationHandler extends DefaultTracingObservationHandler { + + /** + * Creates a new instance of {@link DefaultTracingObservationHandler}. + * + * @param tracer the tracer to use to record events + */ + public CamelDefaultTracingObservationHandler(Tracer tracer) { + super(tracer); + } + + // Current implementation of OpenTelemetry is not doing hyphens + // e.g. ServiceB does not become service-b + @Override + public String getSpanName(Observation.Context context) { + String name = context.getName(); + if (StringUtils.isNotBlank(context.getContextualName())) { + name = context.getContextualName(); + } + return name; + } + + @Override + public void tagSpan(Observation.Context context, Span span) { + super.tagSpan(context, span); + if (context.getError() != null) { + span.tag("error", "true"); + } + } +} diff --git a/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingReceiverTracingObservationHandler.java b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingReceiverTracingObservationHandler.java new file mode 100644 index 00000000000..85e3a9c236f --- /dev/null +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingReceiverTracingObservationHandler.java @@ -0,0 +1,56 @@ +/* + * 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.observation.otel; + +import io.micrometer.observation.transport.ReceiverContext; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; +import io.micrometer.tracing.propagation.Propagator; + +public class CamelPropagatingReceiverTracingObservationHandler<T extends ReceiverContext> + extends PropagatingReceiverTracingObservationHandler<T> { + + static final String SPAN_DECORATOR_INTERNAL = "camel.micrometer.abstract-internal"; + + /** + * Creates a new instance of {@link PropagatingReceiverTracingObservationHandler}. + * + * @param tracer the tracer to use to record events + * @param propagator the mechanism to propagate tracing information from the carrier + */ + public CamelPropagatingReceiverTracingObservationHandler(Tracer tracer, Propagator propagator) { + super(tracer, propagator); + } + + @Override + public Span.Builder customizeExtractedSpan(T context, Span.Builder builder) { + boolean internalComponent = context.getOrDefault(SPAN_DECORATOR_INTERNAL, false); + if (internalComponent && context.getParentObservation() == null) { + builder.kind(null); + } + return builder; + } + + @Override + public void tagSpan(T context, Span span) { + super.tagSpan(context, span); + if (context.getError() != null) { + span.tag("error", "true"); + } + } +} diff --git a/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingSenderTracingObservationHandler.java b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingSenderTracingObservationHandler.java new file mode 100644 index 00000000000..43f6dfec9b1 --- /dev/null +++ b/components/camel-observation/src/test/java/org/apache/camel/observation/otel/CamelPropagatingSenderTracingObservationHandler.java @@ -0,0 +1,46 @@ +/* + * 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.observation.otel; + +import io.micrometer.observation.transport.SenderContext; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; +import io.micrometer.tracing.propagation.Propagator; + +public class CamelPropagatingSenderTracingObservationHandler<T extends SenderContext> + extends PropagatingSenderTracingObservationHandler<T> { + + /** + * Creates a new instance of {@link PropagatingReceiverTracingObservationHandler}. + * + * @param tracer the tracer to use to record events + * @param propagator the mechanism to propagate tracing information from the carrier + */ + public CamelPropagatingSenderTracingObservationHandler(Tracer tracer, Propagator propagator) { + super(tracer, propagator); + } + + @Override + public void tagSpan(T context, Span span) { + super.tagSpan(context, span); + if (context.getError() != null) { + span.tag("error", "true"); + } + } +} diff --git a/components/camel-observation/src/test/resources/log4j2.properties b/components/camel-observation/src/test/resources/log4j2.properties new file mode 100644 index 00000000000..f54d01bb554 --- /dev/null +++ b/components/camel-observation/src/test/resources/log4j2.properties @@ -0,0 +1,29 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +appender.file.type=File +appender.file.name=file +appender.file.fileName=target/camel-observation-test.log +appender.file.layout.type=PatternLayout +appender.file.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.out.type=Console +appender.out.name=out +appender.out.layout.type=PatternLayout +appender.out.layout.pattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +logger.opentelemetry.name=org.apache.camel.observation +logger.opentelemetry.level=INFO +rootLogger.level=INFO +rootLogger.appenderRef.file.ref=file diff --git a/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java b/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java index 956cb7017d2..2d8fe70a54a 100644 --- a/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java +++ b/components/camel-opentelemetry/src/main/java/org/apache/camel/opentelemetry/OpenTelemetryTracer.java @@ -111,7 +111,8 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { @Override protected SpanAdapter startSendingEventSpan( - String operationName, org.apache.camel.tracing.SpanKind kind, SpanAdapter parent) { + String operationName, org.apache.camel.tracing.SpanKind kind, SpanAdapter parent, Exchange exchange, + InjectAdapter injectAdapter) { Baggage baggage = null; SpanBuilder builder = tracer.spanBuilder(operationName).setSpanKind(mapToSpanKind(kind)); if (parent != null) { @@ -135,7 +136,7 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { baggage = spanFromExchange.getBaggage(); } else { ExtractAdapter adapter = sd.getExtractAdapter(exchange.getIn().getHeaders(), encoding); - Context ctx = contextPropagators.getTextMapPropagator().extract(Context.current(), adapter, + Context ctx = GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().extract(Context.current(), adapter, new OpenTelemetryGetter(adapter)); Span span = Span.fromContext(ctx); baggage = Baggage.fromContext(ctx); @@ -165,7 +166,7 @@ public class OpenTelemetryTracer extends org.apache.camel.tracing.Tracer { } else { ctx = Context.current().with(otelSpan); } - contextPropagators.getTextMapPropagator().inject(ctx, adapter, new OpenTelemetrySetter()); + GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(ctx, adapter, new OpenTelemetrySetter()); } } diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java index 320f281d3f9..1fc3f6cd956 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ABCRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -23,12 +24,24 @@ import org.junit.jupiter.api.Test; class ABCRouteTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setParentId(1).addLogMessage("routing at b"), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setParentId(3).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setParentId(5).addLogMessage("routing at a").addLogMessage("End of routing"), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.CLIENT) + .setParentId(6), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; ABCRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java index 1fa8ff97fad..3731ea861c1 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/ClientRecipientListRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -24,12 +25,24 @@ class ClientRecipientListRouteTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3), + .setParentId(1), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") .setParentId(3), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setParentId(6) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(3), + .setParentId(5), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; ClientRecipientListRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java index b28da91eb1d..25d147ede96 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CurrentSpanTest.java @@ -75,7 +75,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { SpanTestData[] expectedSpans = { new SpanTestData().setLabel("syncmock:result").setUri("syncmock://result").setOperation("syncmock") .setKind(SpanKind.CLIENT), - new SpanTestData().setLabel("direct:bar").setUri("direct://bar").setOperation("bar") + new SpanTestData().setLabel("direct:bar").setUri("direct://bar").setOperation("bar"), + new SpanTestData().setLabel("direct:bar").setUri("direct://bar").setOperation("bar").setKind(SpanKind.CLIENT) }; // sync pipeline @@ -93,7 +94,8 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { SpanTestData[] expectedSpans = { new SpanTestData().setLabel("asyncmock1:result").setUri("asyncmock1://result").setOperation("asyncmock1") .setKind(SpanKind.CLIENT), - new SpanTestData().setLabel("direct:foo").setUri("direct://foo").setOperation("foo") + new SpanTestData().setLabel("direct:foo").setUri("direct://foo").setOperation("foo"), + new SpanTestData().setLabel("direct:foo").setUri("direct://foo").setOperation("foo").setKind(SpanKind.CLIENT) }; // sync to async pipeline @@ -171,7 +173,9 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("syncmock:result").setUri("syncmock://result").setOperation("syncmock") .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start").setUri("direct://start").setOperation("start"), new SpanTestData().setLabel("direct:start").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; // sync pipeline @@ -191,7 +195,7 @@ class CurrentSpanTest extends CamelOpenTelemetryTestSupport { assertFalse(Span.current().getSpanContext().isValid()); } - verifyTraceSpanNumbers(30, 10); + verifyTraceSpanNumbers(30, 11); } @Override diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java index 27ddfdbd462..e497693cb37 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/CustomComponentNameRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -24,12 +25,22 @@ class CustomComponentNameRouteTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("myseda:b server").setUri("myseda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setParentId(1).addLogMessage("routing at b"), + new SpanTestData().setLabel("myseda:b server").setUri("myseda://b").setOperation("b").setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("myseda:c server").setUri("myseda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setParentId(3).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + new SpanTestData().setLabel("myseda:c server").setUri("myseda://c").setOperation("c").setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("myseda:a server").setUri("myseda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setParentId(5).addLogMessage("routing at a").addLogMessage("End of routing"), + new SpanTestData().setLabel("myseda:a server").setUri("myseda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; CustomComponentNameRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java index 15fb907365e..43ba4b82433 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastParallelRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -24,12 +25,24 @@ class MulticastParallelRouteTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b"), + .setParentId(1).addLogMessage("routing at b"), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setParentId(4) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setParentId(3).addLogMessage("routing at c"), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("routing at c"), + .setParentId(4) + .setKind(SpanKind.CLIENT), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setParentId(5).addLogMessage("routing at a").addLogMessage("End of routing"), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(6) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setKind(SpanKind.CLIENT) }; MulticastParallelRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java index c464d593682..6c807703a95 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/MulticastRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -24,12 +25,23 @@ class MulticastRouteTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2), + .setParentId(1), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2), - new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") .setParentId(3), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(5), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setKind(SpanKind.CLIENT) + .setParentId(6), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start").setKind(SpanKind.CLIENT) }; MulticastRouteTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java index 964932bdb95..c1617f0d6c6 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/RouteConcurrentTest.java @@ -18,6 +18,7 @@ package org.apache.camel.opentelemetry; import java.util.concurrent.TimeUnit; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.NotifyBuilder; import org.apache.camel.builder.RouteBuilder; @@ -28,9 +29,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class RouteConcurrentTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { - new SpanTestData().setLabel("seda:foo server").setUri("seda://foo?concurrentConsumers=5").setOperation("foo"), + new SpanTestData().setLabel("seda:foo server").setUri("seda://foo").setOperation("foo") + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:bar server").setUri("seda://bar").setOperation("bar") + .setParentId(2) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("seda:foo server").setUri("seda://foo?concurrentConsumers=5").setOperation("foo") + .setParentId(0), new SpanTestData().setLabel("seda:bar server").setUri("seda://bar?concurrentConsumers=5").setOperation("bar") - .setParentId(0) + .setParentId(1), }; RouteConcurrentTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java index 8be835be8b8..7708ebd45d2 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/SpanProcessorsTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.Exchange; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; @@ -27,13 +28,24 @@ class SpanProcessorsTest extends CamelOpenTelemetryTestSupport { private static final SpanTestData[] TEST_DATA = { new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") - .setParentId(2).addLogMessage("routing at b") + .setParentId(1).addLogMessage("routing at b") .addTag("b-tag", "request-header-value"), + new SpanTestData().setLabel("seda:b server").setUri("seda://b").setOperation("b") + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") + .setParentId(3).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), new SpanTestData().setLabel("seda:c server").setUri("seda://c").setOperation("c") - .setParentId(2).addLogMessage("Exchange[ExchangePattern: InOut, BodyType: String, Body: Hello]"), + .setKind(SpanKind.CLIENT) + .setParentId(4), + new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") + .setParentId(5).addLogMessage("routing at a").addLogMessage("End of routing"), new SpanTestData().setLabel("seda:a server").setUri("seda://a").setOperation("a") - .setParentId(3).addLogMessage("routing at a").addLogMessage("End of routing"), + .setKind(SpanKind.CLIENT) + .setParentId(6), new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start") + .setParentId(7), + new SpanTestData().setLabel("direct:start server").setUri("direct://start").setOperation("start").setKind(SpanKind.CLIENT) }; SpanProcessorsTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java index 563a089ce41..4b1c28fb39c 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.opentelemetry; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -25,7 +26,13 @@ class TwoServiceTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("ServiceB server").setUri("direct://ServiceB").setOperation("ServiceB") .setParentId(1), + new SpanTestData().setLabel("ServiceB server").setUri("direct://ServiceB").setOperation("ServiceB") + .setParentId(2) + .setKind(SpanKind.CLIENT), + new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setParentId(3), new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setKind(SpanKind.CLIENT) }; TwoServiceTest() { diff --git a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java index dc8fd800ca6..44665b7306d 100644 --- a/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java +++ b/components/camel-opentelemetry/src/test/java/org/apache/camel/opentelemetry/TwoServiceWithExcludeTest.java @@ -19,6 +19,7 @@ package org.apache.camel.opentelemetry; import java.util.Collections; import java.util.Set; +import io.opentelemetry.api.trace.SpanKind; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -27,6 +28,9 @@ class TwoServiceWithExcludeTest extends CamelOpenTelemetryTestSupport { private static SpanTestData[] testdata = { new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setParentId(1), + new SpanTestData().setLabel("ServiceA server").setUri("direct://ServiceA").setOperation("ServiceA") + .setKind(SpanKind.CLIENT) }; TwoServiceWithExcludeTest() { diff --git a/components/camel-tracing/src/main/docs/tracing.adoc b/components/camel-tracing/src/main/docs/tracing.adoc index b31aa71441b..f6bb931bca0 100644 --- a/components/camel-tracing/src/main/docs/tracing.adoc +++ b/components/camel-tracing/src/main/docs/tracing.adoc @@ -10,4 +10,4 @@ This module is common interfaces and API for distributed tracing. -This module is not intended to be used by end users, but they should use `camel-opentelemetry`. +This module is not intended to be used by end users, but they should use `camel-opentelemetry`, `camel-opentracing` or `camel-zipkin` instead. diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/ActiveSpanManager.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/ActiveSpanManager.java index 287a5d615ba..bce194f27ec 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/ActiveSpanManager.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/ActiveSpanManager.java @@ -119,7 +119,7 @@ public final class ActiveSpanManager { public Holder(Holder parent, SpanAdapter span) { this.parent = parent; this.span = span; - this.scope = span.makeCurrent(); + this.scope = new ScopeWrapper(span.makeCurrent(), Thread.currentThread().getId()); } public Holder getParent() { @@ -142,4 +142,29 @@ public final class ActiveSpanManager { } } + /** + * Makes closing scopes idempotent and prevents restoring scope on the wrong thread: Should be removed if + * https://github.com/open-telemetry/opentelemetry-java/issues/5055 is fixed. + */ + private static class ScopeWrapper implements AutoCloseable { + private final long startThreadId; + private final AutoCloseable inner; + private boolean closed; + + public ScopeWrapper(AutoCloseable inner, long startThreadId) { + this.startThreadId = startThreadId; + this.inner = inner; + } + + @Override + public void close() throws Exception { + if (!closed && Thread.currentThread().getId() == startThreadId) { + closed = true; + inner.close(); + } else { + LOG.debug("not closing scope, closed - {}, started on thread - '{}', current thread - '{}'", + closed, startThreadId, Thread.currentThread().getId()); + } + } + } } diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/SpanAdapter.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/SpanAdapter.java index d256f4528be..8413c3760a3 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/SpanAdapter.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/SpanAdapter.java @@ -33,6 +33,26 @@ public interface SpanAdapter { void setTag(String key, Boolean value); + default void setLowCardinalityTag(Tag key, String value) { + setTag(key, value); + } + + default void setLowCardinalityTag(Tag key, Number value) { + setTag(key, value); + } + + default void setLowCardinalityTag(String key, String value) { + setTag(key, value); + } + + default void setLowCardinalityTag(String key, Number value) { + setTag(key, value); + } + + default void setLowCardinalityTag(String key, Boolean value) { + setTag(key, value); + } + void log(Map<String, String> log); String traceId(); diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java index 705a7570b64..53e6c1e4078 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/Tracer.java @@ -77,9 +77,10 @@ public abstract class Tracer extends ServiceSupport implements RoutePolicyFactor protected abstract void initTracer(); - protected abstract void initContextPropagators(); + protected abstract SpanAdapter startSendingEventSpan( + String operationName, SpanKind kind, SpanAdapter parent, Exchange exchange, InjectAdapter injectAdapter); - protected abstract SpanAdapter startSendingEventSpan(String operationName, SpanKind kind, SpanAdapter parent); + protected abstract void initContextPropagators(); protected abstract SpanAdapter startExchangeBeginSpan( Exchange exchange, SpanDecorator sd, String operationName, SpanKind kind, SpanAdapter parent); @@ -254,10 +255,11 @@ public abstract class Tracer extends ServiceSupport implements RoutePolicyFactor } SpanAdapter parent = ActiveSpanManager.getSpan(ese.getExchange()); + InjectAdapter injectAdapter = sd.getInjectAdapter(ese.getExchange().getIn().getHeaders(), encoding); SpanAdapter span = startSendingEventSpan(sd.getOperationName(ese.getExchange(), ese.getEndpoint()), - sd.getInitiatorSpanKind(), parent); + sd.getInitiatorSpanKind(), parent, ese.getExchange(), injectAdapter); sd.pre(span, ese.getExchange(), ese.getEndpoint()); - inject(span, sd.getInjectAdapter(ese.getExchange().getIn().getHeaders(), encoding)); + inject(span, injectAdapter); ActiveSpanManager.activate(ese.getExchange(), span); if (LOG.isTraceEnabled()) { LOG.trace("Tracing: start client span={}", span); @@ -295,7 +297,7 @@ public abstract class Tracer extends ServiceSupport implements RoutePolicyFactor } private boolean shouldExclude(SpanDecorator sd, Exchange exchange, Endpoint endpoint) { - return sd instanceof AbstractInternalSpanDecorator || !sd.newSpan() + return !sd.newSpan() || isExcluded(exchange, endpoint); } } diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/AbstractHttpSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/AbstractHttpSpanDecorator.java index c4919271cc7..2b7081d7d36 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/AbstractHttpSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/AbstractHttpSpanDecorator.java @@ -71,7 +71,7 @@ public abstract class AbstractHttpSpanDecorator extends AbstractSpanDecorator { if (httpUrl != null) { span.setTag(Tag.HTTP_URL, httpUrl); } - span.setTag(Tag.HTTP_METHOD, getHttpMethod(exchange, endpoint)); + span.setLowCardinalityTag(Tag.HTTP_METHOD, getHttpMethod(exchange, endpoint)); } protected String getHttpURL(Exchange exchange, Endpoint endpoint) { @@ -101,7 +101,7 @@ public abstract class AbstractHttpSpanDecorator extends AbstractSpanDecorator { if (message != null) { Integer responseCode = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); if (responseCode != null) { - span.setTag(Tag.HTTP_STATUS, responseCode); + span.setLowCardinalityTag(Tag.HTTP_STATUS, responseCode); } } } diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/CqlSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/CqlSpanDecorator.java index f8e07caabc0..61db6f68211 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/CqlSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/CqlSpanDecorator.java @@ -43,7 +43,7 @@ public class CqlSpanDecorator extends AbstractSpanDecorator { @Override public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); - span.setTag(Tag.DB_TYPE, CASSANDRA_DB_TYPE); + span.setLowCardinalityTag(Tag.DB_TYPE, CASSANDRA_DB_TYPE); URI uri = URI.create(endpoint.getEndpointUri()); if (uri.getPath() != null && uri.getPath().length() > 0) { // Strip leading '/' from path diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/ElasticsearchSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/ElasticsearchSpanDecorator.java index 5538c9df686..bb249cba2b8 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/ElasticsearchSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/ElasticsearchSpanDecorator.java @@ -31,7 +31,7 @@ public class ElasticsearchSpanDecorator extends AbstractSpanDecorator { @Override public String getComponent() { - return "elasticsearch"; + return "elasticsearch-rest"; } @Override @@ -50,7 +50,7 @@ public class ElasticsearchSpanDecorator extends AbstractSpanDecorator { @Override public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); - span.setTag(Tag.DB_TYPE, ELASTICSEARCH_DB_TYPE); + span.setLowCardinalityTag(Tag.DB_TYPE, ELASTICSEARCH_DB_TYPE); Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri()); if (queryParameters.containsKey("indexName")) { diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java index 12745783cb6..0d079aef364 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JdbcSpanDecorator.java @@ -37,7 +37,7 @@ public class JdbcSpanDecorator extends AbstractSpanDecorator { public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); - span.setTag(Tag.DB_TYPE, "sql"); + span.setLowCardinalityTag(Tag.DB_TYPE, "sql"); Object body = exchange.getIn().getBody(); if (body instanceof String) { diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JettySpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JettySpanDecorator.java index 92b4837412f..94a418a60a3 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JettySpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/JettySpanDecorator.java @@ -25,7 +25,7 @@ public class JettySpanDecorator extends AbstractHttpSpanDecorator { @Override public String getComponentClassName() { - return "org.apache.camel.component.jetty10.JettyHttpComponent10"; + return "org.apache.camel.component.jetty9.JettyHttpComponent9"; } } diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/KafkaSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/KafkaSpanDecorator.java index 2116d316f67..ebb1e88e927 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/KafkaSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/KafkaSpanDecorator.java @@ -82,7 +82,7 @@ public class KafkaSpanDecorator extends AbstractMessagingSpanDecorator { /** * Extracts header value from the exchange for given header - * + * * @param exchange the {@link Exchange} * @param header the header name * @param type the class type of the exchange header diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/MongoDBSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/MongoDBSpanDecorator.java index 31add1d1e76..3ec32145bb2 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/MongoDBSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/MongoDBSpanDecorator.java @@ -49,7 +49,7 @@ public class MongoDBSpanDecorator extends AbstractSpanDecorator { public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); - span.setTag(Tag.DB_TYPE, getComponent()); + span.setLowCardinalityTag(Tag.DB_TYPE, getComponent()); Map<String, String> queryParameters = toQueryParameters(endpoint.getEndpointUri()); String database = queryParameters.get("database"); if (database != null) { diff --git a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/SqlSpanDecorator.java b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/SqlSpanDecorator.java index f25517e0e82..99409aeb397 100644 --- a/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/SqlSpanDecorator.java +++ b/components/camel-tracing/src/main/java/org/apache/camel/tracing/decorators/SqlSpanDecorator.java @@ -38,7 +38,7 @@ public class SqlSpanDecorator extends AbstractSpanDecorator { @Override public void pre(SpanAdapter span, Exchange exchange, Endpoint endpoint) { super.pre(span, exchange, endpoint); - span.setTag(Tag.DB_TYPE, "sql"); + span.setLowCardinalityTag(Tag.DB_TYPE, "sql"); String sqlquery = exchange.getIn().getHeader(CAMEL_SQL_QUERY, String.class); if (sqlquery != null) { diff --git a/components/pom.xml b/components/pom.xml index a3c96457841..0e518dd6f24 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -221,6 +221,7 @@ <module>camel-netty</module> <module>camel-nitrite</module> <module>camel-oaipmh</module> + <module>camel-observation</module> <module>camel-ognl</module> <module>camel-olingo2</module> <module>camel-olingo4</module> diff --git a/parent/pom.xml b/parent/pom.xml index 198ed3c4d90..56eb1e431e9 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -360,6 +360,7 @@ <maven-war-plugin-version>3.3.1</maven-war-plugin-version> <metrics-version>4.2.17</metrics-version> <micrometer-version>1.10.5</micrometer-version> + <micrometer-tracing-version>1.0.3</micrometer-tracing-version> <microprofile-config-version>3.0.2</microprofile-config-version> <microprofile-fault-tolerance-version>4.0.2</microprofile-fault-tolerance-version> <milo-version>0.6.8</milo-version> @@ -1839,6 +1840,11 @@ <artifactId>camel-oaipmh</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-observation</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-ognl</artifactId>