This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch feature/CAMEL-24003-activity-span-enrichment
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 684b49dcda6ff3a27711c309288448ef89850b40
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Jul 11 22:45:24 2026 +0200

    CAMEL-24003: Enrich activity data with span decorator attributes from 
camel-telemetry
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../org/apache/camel/telemetry/RecordingSpan.java  | 78 ++++++++++++++++++++++
 .../java/org/apache/camel/telemetry/Tracer.java    | 26 +++++++-
 .../src/main/java/org/apache/camel/Exchange.java   |  8 +++
 .../java/org/apache/camel/ExchangePropertyKey.java |  5 +-
 .../java/org/apache/camel/spi/BacklogTracer.java   |  9 +++
 5 files changed, 124 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/RecordingSpan.java
 
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/RecordingSpan.java
new file mode 100644
index 000000000000..28f40223d963
--- /dev/null
+++ 
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/RecordingSpan.java
@@ -0,0 +1,78 @@
+/*
+ * 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.telemetry;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A {@link Span} wrapper that records all {@code setTag} and {@code 
setComponent} calls into a {@link Map} while
+ * delegating every operation to the underlying span. This is used to capture 
span decorator attributes for the
+ * BacklogTracer activity enrichment without running decorator logic twice.
+ * <p>
+ * The recording is only activated when the BacklogTracer activity feature is 
enabled, so in production there is no
+ * wrapping overhead.
+ */
+class RecordingSpan implements Span {
+
+    private final Span delegate;
+    private final Map<String, String> tags = new LinkedHashMap<>();
+
+    RecordingSpan(Span delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void setTag(String key, String value) {
+        delegate.setTag(key, value);
+        if (value != null) {
+            tags.put(key, value);
+        }
+    }
+
+    @Override
+    public void setComponent(String component) {
+        delegate.setComponent(component);
+        if (component != null) {
+            tags.put("component", component);
+        }
+    }
+
+    @Override
+    public void setError(boolean isError) {
+        delegate.setError(isError);
+    }
+
+    @Override
+    public void log(Map<String, String> fields) {
+        delegate.log(fields);
+    }
+
+    /**
+     * Returns the real span that this wrapper delegates to.
+     */
+    Span getDelegate() {
+        return delegate;
+    }
+
+    /**
+     * Returns the recorded tags as an unmodifiable snapshot.
+     */
+    Map<String, String> getRecordedTags() {
+        return Map.copyOf(tags);
+    }
+}
diff --git 
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
 
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
index 40d148fd5c88..91806d3fd62c 100644
--- 
a/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
+++ 
b/components/camel-telemetry/src/main/java/org/apache/camel/telemetry/Tracer.java
@@ -24,11 +24,13 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.NamedNode;
 import org.apache.camel.Route;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.StaticService;
 import org.apache.camel.api.management.ManagedAttribute;
+import org.apache.camel.spi.BacklogTracer;
 import org.apache.camel.spi.CamelEvent;
 import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.spi.CamelTracingService;
@@ -68,6 +70,7 @@ public abstract class Tracer extends ServiceSupport 
implements CamelTracingServi
     private final TracingEventNotifier eventNotifier = new 
TracingEventNotifier();
     private final SpanStorageManager spanStorageManager = new 
SpanStorageManagerExchange();
     private final SpanDecoratorManager spanDecoratorManager = new 
SpanDecoratorManagerImpl();
+    private boolean activityEnabled;
 
     /*
      * It has to be provided by the specific implementation
@@ -209,6 +212,13 @@ public abstract class Tracer extends ServiceSupport 
implements CamelTracingServi
         InterceptStrategy interceptStrategy = new 
TraceProcessorsInterceptStrategy(this);
         
camelContext.getCamelContextExtension().addInterceptStrategy(interceptStrategy);
 
+        // check if BacklogTracer activity is enabled so we can enrich 
activity data with span attributes
+        BacklogTracer backlogTracer
+                = 
camelContext.getCamelContextExtension().getContextPlugin(BacklogTracer.class);
+        if (backlogTracer != null) {
+            activityEnabled = backlogTracer.isActivityEnabled();
+        }
+
         initTracer();
         ServiceHelper.startService(eventNotifier);
     }
@@ -349,7 +359,21 @@ public abstract class Tracer extends ServiceSupport 
implements CamelTracingServi
         Span span = spanLifecycleManager.create(spanName, spanKind, parentSpan,
                 spanDecorator.getExtractor(exchange));
         span.setTag(TagConstants.OP, op.toString());
-        spanDecorator.beforeTracingEvent(span, exchange, endpoint);
+
+        if (activityEnabled && op == Op.EVENT_SENT) {
+            // wrap with recording span to capture decorator attributes for 
activity enrichment
+            RecordingSpan recording = new RecordingSpan(span);
+            spanDecorator.beforeTracingEvent(recording, exchange, endpoint);
+            // store recorded tags on exchange for BacklogTracer activity to 
pick up
+            Map<String, String> tags = recording.getRecordedTags();
+            if (!tags.isEmpty()) {
+                exchange.setProperty(ExchangePropertyKey.ACTIVITY_SPAN_TAGS, 
tags);
+            }
+            // continue with the real span for activation/storage
+        } else {
+            spanDecorator.beforeTracingEvent(span, exchange, endpoint);
+        }
+
         spanLifecycleManager.activate(span);
         spanStorageManager.push(exchange, span);
         spanLifecycleManager.inject(span, spanDecorator.getInjector(exchange), 
this.traceHeadersInclusion);
diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java 
b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index 27607438f0a8..ac8ec7044d75 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -327,6 +327,14 @@ public interface Exchange extends VariableAware {
     String OTEL_ACTIVE_SPAN = "OpenTracing.activeSpan";
     @Deprecated(since = "4.19.0")
     String OTEL_CLOSE_CLIENT_SCOPE = "OpenTracing.closeClientScope";
+    /**
+     * Exchange property set by tracing implementations (e.g., 
camel-telemetry) containing span decorator attributes as a
+     * {@code Map<String, String>}. Used by the BacklogTracer activity feature 
to enrich endpoint send entries with
+     * component-specific details (e.g., Kafka topic, SQL query, HTTP method).
+     *
+     * @since 4.22
+     */
+    String ACTIVITY_SPAN_TAGS = "CamelActivitySpanTags";
 
     /**
      * Returns the {@link ExchangePattern} (MEP) of this exchange.
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java 
b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
index 8b4dd6415e0c..c3d9ed220f05 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExchangePropertyKey.java
@@ -100,7 +100,8 @@ public enum ExchangePropertyKey {
     @Deprecated(since = "4.19.0")
     OTEL_ACTIVE_SPAN(Exchange.OTEL_ACTIVE_SPAN),
     @Deprecated(since = "4.19.0")
-    OTEL_CLOSE_CLIENT_SCOPE(Exchange.OTEL_CLOSE_CLIENT_SCOPE);
+    OTEL_CLOSE_CLIENT_SCOPE(Exchange.OTEL_CLOSE_CLIENT_SCOPE),
+    ACTIVITY_SPAN_TAGS(Exchange.ACTIVITY_SPAN_TAGS);
 
     private final String name;
 
@@ -246,6 +247,8 @@ public enum ExchangePropertyKey {
             // Deprecated since 4.19.0
             case Exchange.OTEL_CLOSE_CLIENT_SCOPE:
                 return OTEL_CLOSE_CLIENT_SCOPE;
+            case Exchange.ACTIVITY_SPAN_TAGS:
+                return ACTIVITY_SPAN_TAGS;
             default:
                 return null;
         }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
index d56acdc2dde5..2596cd53ee3d 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BacklogTracer.java
@@ -43,6 +43,15 @@ import org.jspecify.annotations.Nullable;
  */
 public interface BacklogTracer {
 
+    /**
+     * Whether the activity tracking feature is enabled.
+     *
+     * @since 4.22
+     */
+    default boolean isActivityEnabled() {
+        return false;
+    }
+
     /**
      * Is the tracer enabled.
      */

Reply via email to