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

davsclaus pushed a commit to branch CAMEL-26195-otel-metrics-counter
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 1e0711c37fe11a14dfff2f4fe6dcc5505a6cf16c
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Nov 3 00:05:01 2026 +0100

    CAMEL-26195: camel-opentelemetry-metrics - a counter increment or decrement 
that does not give a number fails the exchange
    
    With increment or decrement set, a counter added 1 when the expression did
    not give a number (a Map, List, POJO or null body for increment=${body}, or
    a missing header). It now fails the exchange with an 
IllegalArgumentException
    and the counter is not updated. A counter without both options still adds 1.
    
    The CamelMetricsCounterIncrement and CamelMetricsCounterDecrement headers 
are
    read as Long, but were documented as Double in the catalog; now long.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../catalog/components/opentelemetry-metrics.json  |  4 +-
 .../docs/opentelemetry-metrics-component.adoc      |  2 +
 .../metrics/opentelemetry-metrics.json             |  4 +-
 .../main/docs/opentelemetry-metrics-component.adoc |  2 +
 .../opentelemetry/metrics/CounterProducer.java     |  5 ++
 .../metrics/OpenTelemetryConstants.java            |  4 +-
 .../opentelemetry/metrics/CounterRouteTest.java    | 77 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  8 +++
 .../dsl/OpenTelemetryEndpointBuilderFactory.java   |  4 +-
 9 files changed, 102 insertions(+), 8 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opentelemetry-metrics.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opentelemetry-metrics.json
index 3ae287b7bcfc..63b6574d4142 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opentelemetry-metrics.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opentelemetry-metrics.json
@@ -31,8 +31,8 @@
   "headers": {
     "CamelMetricsTimerAction": { "index": 0, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryTimerAction", "enum": [ 
"START", "STOP" ], "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override timer action in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_TIMER_ACTION"
 },
     "CamelMetricsHistogramValue": { "index": 1, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override histogram value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_HISTOGRAM_VALUE"
 },
-    "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override decrement value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_DECREMENT"
 },
-    "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override increment value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_INCREMENT"
 },
+    "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override decrement value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_DECREMENT"
 },
+    "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override increment value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_INCREMENT"
 },
     "CamelMetricsName": { "index": 4, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Override name value in URI", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_NAME"
 },
     "CamelMetricsDescription": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Override description value in URI", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_DESCRIPTION"
 },
     "CamelMetricsAttributes": { "index": 6, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": 
"io.opentelemetry.api.common.Attributes", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "To 
augment meter attributes defined as URI parameters", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_ATTRIBUTES"
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opentelemetry-metrics-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opentelemetry-metrics-component.adoc
index 6890a325bb17..4c30d69c9a03 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opentelemetry-metrics-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opentelemetry-metrics-component.adoc
@@ -111,6 +111,8 @@ opentelemetry-metrics:counter:name[?options]
 
 If neither `increment` or `decrement` is defined then value of the counter 
will be incremented by one.
 If `increment` and `decrement` are both defined only the increment operation 
is called.
+If `increment` or `decrement` is defined, but neither of them gives a number 
(for example `+increment=${body}+` with
+a `Map`, a POJO or a `null` body), the exchange fails with an 
`IllegalArgumentException`, and the counter is not updated.
 
 [tabs]
 ====
diff --git 
a/components/camel-opentelemetry-metrics/src/generated/resources/META-INF/org/apache/camel/opentelemetry/metrics/opentelemetry-metrics.json
 
b/components/camel-opentelemetry-metrics/src/generated/resources/META-INF/org/apache/camel/opentelemetry/metrics/opentelemetry-metrics.json
index 3ae287b7bcfc..63b6574d4142 100644
--- 
a/components/camel-opentelemetry-metrics/src/generated/resources/META-INF/org/apache/camel/opentelemetry/metrics/opentelemetry-metrics.json
+++ 
b/components/camel-opentelemetry-metrics/src/generated/resources/META-INF/org/apache/camel/opentelemetry/metrics/opentelemetry-metrics.json
@@ -31,8 +31,8 @@
   "headers": {
     "CamelMetricsTimerAction": { "index": 0, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryTimerAction", "enum": [ 
"START", "STOP" ], "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override timer action in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_TIMER_ACTION"
 },
     "CamelMetricsHistogramValue": { "index": 1, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override histogram value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_HISTOGRAM_VALUE"
 },
-    "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override decrement value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_DECREMENT"
 },
-    "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override increment value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_INCREMENT"
 },
+    "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override decrement value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_DECREMENT"
 },
+    "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Override increment value in URI", 
"constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_COUNTER_INCREMENT"
 },
     "CamelMetricsName": { "index": 4, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Override name value in URI", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_NAME"
 },
     "CamelMetricsDescription": { "index": 5, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Override description value in URI", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_DESCRIPTION"
 },
     "CamelMetricsAttributes": { "index": 6, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": 
"io.opentelemetry.api.common.Attributes", "deprecated": false, 
"deprecationNote": "", "autowired": false, "secret": false, "description": "To 
augment meter attributes defined as URI parameters", "constantName": 
"org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants#HEADER_METRIC_ATTRIBUTES"
 }
diff --git 
a/components/camel-opentelemetry-metrics/src/main/docs/opentelemetry-metrics-component.adoc
 
b/components/camel-opentelemetry-metrics/src/main/docs/opentelemetry-metrics-component.adoc
index 6890a325bb17..4c30d69c9a03 100644
--- 
a/components/camel-opentelemetry-metrics/src/main/docs/opentelemetry-metrics-component.adoc
+++ 
b/components/camel-opentelemetry-metrics/src/main/docs/opentelemetry-metrics-component.adoc
@@ -111,6 +111,8 @@ opentelemetry-metrics:counter:name[?options]
 
 If neither `increment` or `decrement` is defined then value of the counter 
will be incremented by one.
 If `increment` and `decrement` are both defined only the increment operation 
is called.
+If `increment` or `decrement` is defined, but neither of them gives a number 
(for example `+increment=${body}+` with
+a `Map`, a POJO or a `null` body), the exchange fails with an 
`IllegalArgumentException`, and the counter is not updated.
 
 [tabs]
 ====
diff --git 
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/CounterProducer.java
 
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/CounterProducer.java
index 7bb72b1e60b0..c481cab2592b 100644
--- 
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/CounterProducer.java
+++ 
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/CounterProducer.java
@@ -75,6 +75,11 @@ public class CounterProducer extends 
AbstractOpenTelemetryProducer<LongUpDownCou
             counter.add(finalIncrement, attributes);
         } else if (finalDecrement != null) {
             counter.add(-finalDecrement, attributes);
+        } else if (getEndpoint().getIncrement() != null || 
getEndpoint().getDecrement() != null) {
+            // an increment or decrement is configured, but it did not give a 
number (such as a Map or null body)
+            throw new IllegalArgumentException(
+                    "The increment (" + getEndpoint().getIncrement() + ") or 
decrement (" + getEndpoint().getDecrement()
+                                               + ") of counter " + metricsName 
+ " did not evaluate to a number");
         } else {
             counter.add(1L, attributes);
         }
diff --git 
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/OpenTelemetryConstants.java
 
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/OpenTelemetryConstants.java
index 2a2d61c78b94..8c21da2361b1 100644
--- 
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/OpenTelemetryConstants.java
+++ 
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/OpenTelemetryConstants.java
@@ -26,9 +26,9 @@ public class OpenTelemetryConstants {
     public static final String HEADER_TIMER_ACTION = HEADER_PREFIX + 
"TimerAction";
     @Metadata(description = "Override histogram value in URI", javaType = 
"long")
     public static final String HEADER_HISTOGRAM_VALUE = HEADER_PREFIX + 
"HistogramValue";
-    @Metadata(description = "Override decrement value in URI", javaType = 
"Double")
+    @Metadata(description = "Override decrement value in URI", javaType = 
"long")
     public static final String HEADER_COUNTER_DECREMENT = HEADER_PREFIX + 
"CounterDecrement";
-    @Metadata(description = "Override increment value in URI", javaType = 
"Double")
+    @Metadata(description = "Override increment value in URI", javaType = 
"long")
     public static final String HEADER_COUNTER_INCREMENT = HEADER_PREFIX + 
"CounterIncrement";
     @Metadata(description = "Override name value in URI", javaType = "String")
     public static final String HEADER_METRIC_NAME = HEADER_PREFIX + "Name";
diff --git 
a/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/CounterRouteTest.java
 
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/CounterRouteTest.java
index 9756532f35ce..fad84866bf8f 100644
--- 
a/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/CounterRouteTest.java
+++ 
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/CounterRouteTest.java
@@ -17,6 +17,8 @@
 package org.apache.camel.opentelemetry.metrics;
 
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import io.opentelemetry.api.common.AttributeKey;
 import io.opentelemetry.api.common.Attributes;
@@ -24,6 +26,7 @@ import io.opentelemetry.api.metrics.Meter;
 import io.opentelemetry.sdk.metrics.data.LongPointData;
 import io.opentelemetry.sdk.metrics.data.PointData;
 import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
@@ -36,6 +39,8 @@ import static 
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.HEAD
 import static 
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.HEADER_METRIC_NAME;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 public class CounterRouteTest extends CamelTestSupport {
 
@@ -76,6 +81,14 @@ public class CounterRouteTest extends CamelTestSupport {
                         
.setHeader(OpenTelemetryConstants.HEADER_COUNTER_INCREMENT, 
simple("${body.length}"))
                         .to("opentelemetry-metrics:counter:F")
                         .to("mock:result");
+
+                from("direct:in7")
+                        
.to("opentelemetry-metrics:counter:G?increment=${body}")
+                        .to("mock:result");
+
+                from("direct:in8")
+                        
.to("opentelemetry-metrics:counter:H?increment=${header.inc}&decrement=${header.dec}")
+                        .to("mock:result");
             }
         };
     }
@@ -226,6 +239,70 @@ public class CounterRouteTest extends CamelTestSupport {
         MockEndpoint.assertIsSatisfied(context);
     }
 
+    @Test
+    public void testIncrementFromBody() throws Exception {
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(1);
+        template.sendBody("direct:in7", "42");
+
+        assertEquals(42L, getMetric("G").getValue());
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testIncrementFromBodyNotNumber() throws Exception {
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(0);
+
+        assertIncrementFails(Map.of("count", 5));
+        assertIncrementFails(List.of(new Object()));
+        assertIncrementFails(new Object());
+        assertIncrementFails(null);
+
+        // the counter is not updated (it was added 1 for each of them)
+        assertNull(findMetric("G"));
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testIncrementOrDecrementFromHeader() throws Exception {
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(2);
+        template.sendBodyAndHeader("direct:in8", null, "inc", 10L);
+        template.sendBodyAndHeader("direct:in8", null, "dec", 3L);
+
+        assertEquals(7L, getMetric("H").getValue());
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Test
+    public void testIncrementOrDecrementFromHeaderMissing() throws Exception {
+        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+        mockEndpoint.expectedMessageCount(0);
+
+        CamelExecutionException e = assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:in8", "Hello"));
+        assertInstanceOf(IllegalArgumentException.class, e.getCause());
+        assertNull(findMetric("H"));
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    private void assertIncrementFails(Object body) {
+        CamelExecutionException e = assertThrows(CamelExecutionException.class,
+                () -> template.sendBody("direct:in7", body));
+        assertInstanceOf(IllegalArgumentException.class, e.getCause());
+        assertEquals("The increment (${body}) or decrement (null) of counter G 
did not evaluate to a number",
+                e.getCause().getMessage());
+    }
+
+    protected LongPointData findMetric(String metricName) {
+        return (LongPointData) otelExtension.getMetrics().stream()
+                .filter(d -> d.getName().equals(metricName))
+                .map(metricData -> metricData.getData().getPoints())
+                .flatMap(Collection::stream)
+                .findFirst().orElse(null);
+    }
+
     protected LongPointData getMetric(String metricName) {
         PointData pd = otelExtension.getMetrics().stream()
                 .filter(d -> d.getName().equals(metricName))
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 10f5a47c1b89..a0307ae504d6 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1007,6 +1007,14 @@ The generated Endpoint DSL header accessors on the Swift 
`HeaderNameBuilder` are
 `openstackSwiftVersionsLocation()`, `xContainerRead()` -> 
`openstackSwiftContainerRead()`, `xContainerWrite()` ->
 `openstackSwiftContainerWrite()`).
 
+=== camel-opentelemetry-metrics - a counter increment or decrement that does 
not give a number fails the exchange
+
+A `counter` with the `increment` or `decrement` option (such as 
`+increment=${body}+` or `+increment=${header.amount}+`)
+added 1 to the counter when the expression did not give a number, for example 
a `Map`, a POJO or a `null` body, or a
+missing header. It now fails the exchange with an `IllegalArgumentException`, 
and the counter is not updated. A
+counter with neither option still adds 1. The `CamelMetricsCounterIncrement` 
and `CamelMetricsCounterDecrement`
+headers are documented as `long`, which is how they are read.
+
 === camel-optaplanner - one SolverManager per endpoint, and consumers get the 
events of their own CamelContext
 
 With `configFile` and no `SolverManager` (neither the `solverManager` option 
nor the `CamelOptaPlannerSolverManager`
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenTelemetryEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenTelemetryEndpointBuilderFactory.java
index efe458b25c3b..557f759ecbda 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenTelemetryEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpenTelemetryEndpointBuilderFactory.java
@@ -349,7 +349,7 @@ public interface OpenTelemetryEndpointBuilderFactory {
         /**
          * Override decrement value in URI.
          * 
-         * The option is a: {@code Double} type.
+         * The option is a: {@code long} type.
          * 
          * Group: producer
          * 
@@ -361,7 +361,7 @@ public interface OpenTelemetryEndpointBuilderFactory {
         /**
          * Override increment value in URI.
          * 
-         * The option is a: {@code Double} type.
+         * The option is a: {@code long} type.
          * 
          * Group: producer
          * 

Reply via email to