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 *
