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 49a0b3fed1af CAMEL-26611: camel-micrometer,
camel-opentelemetry-metrics - use tags and attributes from headers as given
(#29223)
49a0b3fed1af is described below
commit 49a0b3fed1af723c00a147ec63cf4cb7c94ac50d
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 5 13:05:35 2026 +0100
CAMEL-26611: camel-micrometer, camel-opentelemetry-metrics - use tags and
attributes from headers as given (#29223)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../camel/catalog/components/micrometer.json | 2 +-
.../catalog/components/opentelemetry-metrics.json | 2 +-
.../camel/catalog/docs/micrometer-component.adoc | 5 +++
.../docs/opentelemetry-metrics-component.adoc | 5 +++
.../camel/component/micrometer/micrometer.json | 2 +-
.../src/main/docs/micrometer-component.adoc | 5 +++
.../micrometer/AbstractMicrometerProducer.java | 4 ++-
.../component/micrometer/MicrometerConstants.java | 4 ++-
.../component/micrometer/CounterRouteTest.java | 38 ++++++++++++++++++++++
.../metrics/opentelemetry-metrics.json | 2 +-
.../main/docs/opentelemetry-metrics-component.adoc | 5 +++
.../metrics/AbstractOpenTelemetryProducer.java | 17 ++++------
.../metrics/OpenTelemetryConstants.java | 3 +-
.../opentelemetry/metrics/CounterRouteTest.java | 38 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 11 +++++++
.../dsl/MicrometerEndpointBuilderFactory.java | 4 ++-
.../dsl/OpenTelemetryEndpointBuilderFactory.java | 4 ++-
17 files changed, 131 insertions(+), 20 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json
index 53e540c3a179..85b226b5cf3d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json
@@ -35,7 +35,7 @@
"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.component.micrometer.MicrometerConstants#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.component.micrometer.MicrometerConstants#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.component.micrometer.MicrometerConstants#HEADER_METRIC_DESCRIPTION"
},
- "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"java.lang.Iterable<Tag>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "To augment meter tags
defined as URI parameters", "constantName":
"org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_TAGS" }
+ "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"java.lang.Iterable<Tag>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "To augment meter tags
defined as URI parameters. The tags from the header are used as given; use the
tags URI parameters for Simple expressions.", "constantName":
"org.apache.camel.component.micrometer.MicrometerConstants#HEADER [...]
},
"bodyTypes": {
"producerIn": { "carriers": [ "any" ], "description": "Not used, unless an
option such as increment, decrement or value is a simple expression that reads
it, which must then give a number. The CamelMetrics headers override the
options" },
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 63b6574d4142..0dfaf8b6c9f4 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
@@ -35,7 +35,7 @@
"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"
}
+ "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. The attributes from the
header are used as given; use the attributes URI parameters for Simple
expressions.", "constantName": "org.apache.camel.opentele [...]
},
"bodyTypes": {
"producerIn": { "carriers": [ "any" ], "description": "Not used, unless an
option such as increment, decrement or value is a simple expression that reads
it, which must then give a number. The CamelMetrics headers override the
options" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc
index 1149d43c4d2a..e699f1bdcd1e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc
@@ -148,6 +148,11 @@ from("direct:in")
will update a counter with name `new.name` instead of `name.not.used` using the
tag `dynamic-key` with value `dynamic-value` in addition to the tag `key` with
value `value`.
+The tags from the `CamelMetricsTags` header are used as given: unlike the
`tags` URI parameters,
+their keys and values are not evaluated as `Simple` expressions. A tag from
the header overrides a tag
+from the URI with the same key. To tag a meter with a value from the message,
use a `tags` URI parameter
+such as `tags.customer=${header.customerId}`, or compute the value in the
route before setting the header.
+
All Metrics specific headers are removed from the message once the Micrometer
endpoint finishes processing of exchange.
If the Micrometer endpoint cannot update the meter, then the exchange fails
with the exception, which the
error handler of the route handles as any other failure. For example, a
`value`, `increment` or `decrement` that is
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 4c30d69c9a03..da424ad67647 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
@@ -90,6 +90,11 @@ from("direct:in")
will update a counter with name `new.name` (instead of `name.not.used`) using
the attribute `dynamic-key` with value `dynamic-value` in addition to the
attribute `key` with value `value`.
+The attributes from the `CamelMetricsAttributes` header are used as given:
unlike the `attributes` URI parameters,
+their keys and values are not evaluated as `Simple` expressions. An attribute
from the header overrides an attribute
+from the URI with the same key. To add an attribute with a value from the
message, use an `attributes` URI parameter
+such as `attributes.customer=${header.customerId}`, or compute the value in
the route before setting the header.
+
All Metrics specific headers are removed from the message once the
OpenTelemetry endpoint finishes processing the exchange. While processing the
exchange
the OpenTelemetry endpoint will catch all exceptions and write log entry using
level `warn`.
diff --git
a/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json
b/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json
index 53e540c3a179..85b226b5cf3d 100644
---
a/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json
+++
b/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json
@@ -35,7 +35,7 @@
"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.component.micrometer.MicrometerConstants#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.component.micrometer.MicrometerConstants#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.component.micrometer.MicrometerConstants#HEADER_METRIC_DESCRIPTION"
},
- "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"java.lang.Iterable<Tag>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "To augment meter tags
defined as URI parameters", "constantName":
"org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_TAGS" }
+ "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType":
"java.lang.Iterable<Tag>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "To augment meter tags
defined as URI parameters. The tags from the header are used as given; use the
tags URI parameters for Simple expressions.", "constantName":
"org.apache.camel.component.micrometer.MicrometerConstants#HEADER [...]
},
"bodyTypes": {
"producerIn": { "carriers": [ "any" ], "description": "Not used, unless an
option such as increment, decrement or value is a simple expression that reads
it, which must then give a number. The CamelMetrics headers override the
options" },
diff --git
a/components/camel-micrometer/src/main/docs/micrometer-component.adoc
b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
index 1149d43c4d2a..e699f1bdcd1e 100644
--- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc
+++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc
@@ -148,6 +148,11 @@ from("direct:in")
will update a counter with name `new.name` instead of `name.not.used` using the
tag `dynamic-key` with value `dynamic-value` in addition to the tag `key` with
value `value`.
+The tags from the `CamelMetricsTags` header are used as given: unlike the
`tags` URI parameters,
+their keys and values are not evaluated as `Simple` expressions. A tag from
the header overrides a tag
+from the URI with the same key. To tag a meter with a value from the message,
use a `tags` URI parameter
+such as `tags.customer=${header.customerId}`, or compute the value in the
route before setting the header.
+
All Metrics specific headers are removed from the message once the Micrometer
endpoint finishes processing of exchange.
If the Micrometer endpoint cannot update the meter, then the exchange fails
with the exception, which the
error handler of the route handles as any other failure. For example, a
`value`, `increment` or `decrement` that is
diff --git
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
index c89b6b7234d9..ba1175a296b8 100644
---
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
+++
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/AbstractMicrometerProducer.java
@@ -64,11 +64,13 @@ public abstract class AbstractMicrometerProducer<T extends
Meter> extends Defaul
String defaultMetricsDescription = simple(exchange,
getEndpoint().getMetricsDescription(), String.class);
String finalMetricsDescription = getStringHeader(in,
HEADER_METRIC_DESCRIPTION, defaultMetricsDescription);
Iterable<Tag> headerTags = getTagHeader(in, HEADER_METRIC_TAGS,
Tags.empty());
- Iterable<Tag> finalTags = Tags.concat(tags, headerTags).stream()
+ // only the tags from the endpoint are simple expressions, the tags
from the header are used as given
+ Iterable<Tag> finalTags = Tags.of(tags).stream()
.map(tag -> Tag.of(
simple(exchange, tag.getKey(), String.class),
simple(exchange, tag.getValue(), String.class)))
.reduce(Tags.empty(), Tags::and, Tags::and)
+ .and(headerTags)
.and(Tags.of(
CAMEL_CONTEXT_TAG,
getEndpoint().getCamelContext().getName()));
try {
diff --git
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
index c2f2c4e1fcaf..ea8a7affc388 100644
---
a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
+++
b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java
@@ -37,7 +37,9 @@ public final class MicrometerConstants {
public static final String HEADER_METRIC_NAME = HEADER_PREFIX + "Name";
@Metadata(description = "Override description value in URI", javaType =
"String")
public static final String HEADER_METRIC_DESCRIPTION = HEADER_PREFIX +
"Description";
- @Metadata(description = "To augment meter tags defined as URI parameters",
javaType = "java.lang.Iterable<Tag>")
+ @Metadata(description = "To augment meter tags defined as URI parameters.
The tags from the header are used as given;"
+ + " use the tags URI parameters for Simple
expressions.",
+ javaType = "java.lang.Iterable<Tag>")
public static final String HEADER_METRIC_TAGS = HEADER_PREFIX + "Tags";
public static final String DEFAULT_CAMEL_MESSAGE_HISTORY_METER_NAME =
"camel.message.history";
diff --git
a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
index 60ed279d235b..8af9842b9dbc 100644
---
a/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
+++
b/components/camel-micrometer/src/test/java/org/apache/camel/component/micrometer/CounterRouteTest.java
@@ -21,6 +21,7 @@ import java.util.Map;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.apache.camel.BindToRegistry;
import org.apache.camel.CamelExecutionException;
@@ -39,9 +40,11 @@ import
org.springframework.context.support.AbstractApplicationContext;
import static
org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_DECREMENT;
import static
org.apache.camel.component.micrometer.MicrometerConstants.HEADER_COUNTER_INCREMENT;
import static
org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_NAME;
+import static
org.apache.camel.component.micrometer.MicrometerConstants.HEADER_METRIC_TAGS;
import static
org.apache.camel.component.micrometer.MicrometerConstants.METRICS_REGISTRY_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;
@CamelSpringTest
@@ -74,6 +77,9 @@ public class CounterRouteTest extends CamelSpringTestSupport {
@Produce("direct:in-8")
private ProducerTemplate producer8;
+ @Produce("direct:in-9")
+ private ProducerTemplate producer9;
+
@BindToRegistry(METRICS_REGISTRY_NAME)
private MeterRegistry registry = new SimpleMeterRegistry();
@@ -119,6 +125,10 @@ public class CounterRouteTest extends
CamelSpringTestSupport {
from("direct:in-8")
.to("micrometer:counter:H?decrement=${body}")
.to("mock:out");
+
+ from("direct:in-9")
+
.to("micrometer:counter:I?tags.a=${body.length}&tags.b=uri")
+ .to("mock:out");
}
};
}
@@ -239,6 +249,34 @@ public class CounterRouteTest extends
CamelSpringTestSupport {
endpoint.assertIsSatisfied();
}
+ @Test
+ public void testHeaderTagsAreUsedAsGiven() throws Exception {
+ endpoint.expectedMessageCount(1);
+ producer9.sendBodyAndHeaders("Hello", Map.of(
+ "k", "key",
+ HEADER_METRIC_TAGS, Tags.of("c", "${body}", "${header.k}",
"d")));
+ Counter counter = registry.find("I").counter();
+ assertEquals(1.0D, counter.count(), 0.01D);
+ // the tags from the uri are resolved per exchange
+ assertEquals("5", counter.getId().getTag("a"));
+ assertEquals("uri", counter.getId().getTag("b"));
+ // the tags from the header are not
+ assertEquals("${body}", counter.getId().getTag("c"));
+ assertEquals("d", counter.getId().getTag("${header.k}"));
+ assertNull(counter.getId().getTag("key"));
+ endpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void testHeaderTagOverridesUriTagWithSameKey() throws Exception {
+ endpoint.expectedMessageCount(1);
+ producer9.sendBodyAndHeader("Hello", HEADER_METRIC_TAGS, Tags.of("b",
"header"));
+ Counter counter = registry.find("I").counter();
+ assertEquals("5", counter.getId().getTag("a"));
+ assertEquals("header", counter.getId().getTag("b"));
+ endpoint.assertIsSatisfied();
+ }
+
private void assertIncrementFails(Object body) {
CamelExecutionException e = assertThrows(CamelExecutionException.class,
() -> producer5.sendBody(body));
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 63b6574d4142..0dfaf8b6c9f4 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
@@ -35,7 +35,7 @@
"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"
}
+ "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. The attributes from the
header are used as given; use the attributes URI parameters for Simple
expressions.", "constantName": "org.apache.camel.opentele [...]
},
"bodyTypes": {
"producerIn": { "carriers": [ "any" ], "description": "Not used, unless an
option such as increment, decrement or value is a simple expression that reads
it, which must then give a number. The CamelMetrics headers override the
options" },
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 4c30d69c9a03..da424ad67647 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
@@ -90,6 +90,11 @@ from("direct:in")
will update a counter with name `new.name` (instead of `name.not.used`) using
the attribute `dynamic-key` with value `dynamic-value` in addition to the
attribute `key` with value `value`.
+The attributes from the `CamelMetricsAttributes` header are used as given:
unlike the `attributes` URI parameters,
+their keys and values are not evaluated as `Simple` expressions. An attribute
from the header overrides an attribute
+from the URI with the same key. To add an attribute with a value from the
message, use an `attributes` URI parameter
+such as `attributes.customer=${header.customerId}`, or compute the value in
the route before setting the header.
+
All Metrics specific headers are removed from the message once the
OpenTelemetry endpoint finishes processing the exchange. While processing the
exchange
the OpenTelemetry endpoint will catch all exceptions and write log entry using
level `warn`.
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/AbstractOpenTelemetryProducer.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/AbstractOpenTelemetryProducer.java
index f033094546e4..db469ee5a212 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/AbstractOpenTelemetryProducer.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/AbstractOpenTelemetryProducer.java
@@ -16,10 +16,6 @@
*/
package org.apache.camel.opentelemetry.metrics;
-import java.util.HashMap;
-import java.util.Map;
-
-import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import org.apache.camel.Exchange;
@@ -63,14 +59,13 @@ abstract public class AbstractOpenTelemetryProducer<T>
extends DefaultProducer {
String defaultMetricsDescription = simple(exchange,
getEndpoint().getMetricsDescription(), String.class);
String finalMetricsDescription = getStringHeader(in,
HEADER_METRIC_DESCRIPTION, defaultMetricsDescription);
- Map<AttributeKey<?>, Object> map = new HashMap<>(attributes.asMap());
- map.putAll(getAttributesHeader(in, HEADER_METRIC_ATTRIBUTES,
Attributes.empty()).asMap());
-
+ // only the attributes from the endpoint are simple expressions, the
attributes from the header are used as given
AttributesBuilder ab = Attributes.builder();
- for (Map.Entry<AttributeKey<?>, Object> entry : map.entrySet()) {
- ab.put(simple(exchange, entry.getKey().toString(), String.class),
- simple(exchange, entry.getValue().toString(),
String.class));
- }
+ attributes.forEach((key, value) -> ab.put(
+ simple(exchange, key.getKey(), String.class),
+ simple(exchange, value.toString(), String.class)));
+ getAttributesHeader(in, HEADER_METRIC_ATTRIBUTES, Attributes.empty())
+ .forEach((key, value) -> ab.put(key.getKey(),
value.toString()));
ab.put(CAMEL_CONTEXT_ATTRIBUTE,
getEndpoint().getCamelContext().getName());
try {
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 8c21da2361b1..c5e34fe5ab57 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
@@ -34,7 +34,8 @@ public class OpenTelemetryConstants {
public static final String HEADER_METRIC_NAME = HEADER_PREFIX + "Name";
@Metadata(description = "Override description value in URI", javaType =
"String")
public static final String HEADER_METRIC_DESCRIPTION = HEADER_PREFIX +
"Description";
- @Metadata(description = "To augment meter attributes defined as URI
parameters",
+ @Metadata(description = "To augment meter attributes defined as URI
parameters. The attributes from the header are used"
+ + " as given; use the attributes URI parameters
for Simple expressions.",
javaType = "io.opentelemetry.api.common.Attributes")
public static final String HEADER_METRIC_ATTRIBUTES = HEADER_PREFIX +
"Attributes";
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 f6c0522151db..e18b1cc8cae8 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
@@ -93,6 +93,10 @@ public class CounterRouteTest extends CamelTestSupport {
from("direct:in9")
.to("opentelemetry-metrics:counter:I?decrement=${body}")
.to("mock:result");
+
+ from("direct:in10")
+
.to("opentelemetry-metrics:counter:J?attributes.a=${body.length}&attributes.b=uri")
+ .to("mock:result");
}
};
}
@@ -307,6 +311,40 @@ public class CounterRouteTest extends CamelTestSupport {
MockEndpoint.assertIsSatisfied(context);
}
+ @Test
+ public void testHeaderAttributesAreUsedAsGiven() throws Exception {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedMessageCount(1);
+ template.sendBodyAndHeaders("direct:in10", "Hello", Map.of(
+ "k", "key",
+ HEADER_METRIC_ATTRIBUTES, Attributes.of(
+ AttributeKey.stringKey("c"), "${body}",
+ AttributeKey.stringKey("${header.k}"), "d")));
+
+ Attributes attributes = getMetric("J").getAttributes();
+ // the attributes from the uri are resolved per exchange
+ assertEquals("5", attributes.get(AttributeKey.stringKey("a")));
+ assertEquals("uri", attributes.get(AttributeKey.stringKey("b")));
+ // the attributes from the header are not
+ assertEquals("${body}", attributes.get(AttributeKey.stringKey("c")));
+ assertEquals("d",
attributes.get(AttributeKey.stringKey("${header.k}")));
+ assertNull(attributes.get(AttributeKey.stringKey("key")));
+ MockEndpoint.assertIsSatisfied(context);
+ }
+
+ @Test
+ public void testHeaderAttributeOverridesUriAttributeWithSameKey() throws
Exception {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedMessageCount(1);
+ template.sendBodyAndHeader("direct:in10", "Hello",
HEADER_METRIC_ATTRIBUTES,
+ Attributes.of(AttributeKey.stringKey("b"), "header"));
+
+ Attributes attributes = getMetric("J").getAttributes();
+ assertEquals("5", attributes.get(AttributeKey.stringKey("a")));
+ assertEquals("header", attributes.get(AttributeKey.stringKey("b")));
+ MockEndpoint.assertIsSatisfied(context);
+ }
+
private void assertIncrementFails(Object body) {
CamelExecutionException e = assertThrows(CamelExecutionException.class,
() -> template.sendBody("direct:in7", body));
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 3400e6748ca9..7f2031673993 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
@@ -61,6 +61,17 @@ Starting in 4.24, parameter values provided via the
`CamelJpaParameters` header
Routes that previously relied on evaluating Simple expressions within the
`CamelJpaParameters` header should evaluate those expressions in the route
before setting the header value.
+=== camel-micrometer, camel-opentelemetry-metrics - tags and attributes from
headers are used as given
+
+The `micrometer` and `opentelemetry-metrics` producers now use the tags of the
`CamelMetricsTags` header and the
+attributes of the `CamelMetricsAttributes` header as given. Before, their keys
and values were evaluated as Simple
+expressions, in the same way as the `tags` and `attributes` URI options. The
URI options are still evaluated per
+exchange, and a tag or attribute from the header still overrides one from the
URI with the same key.
+
+A route that relied on a Simple expression in a header tag or attribute should
use the `tags` / `attributes` URI
+option instead (for example `tags.customer=${header.customerId}`), or compute
the value in the route before
+setting the header.
+
=== camel-a2a - consumer context is separate from producer headers
An A2A consumer no longer populates producer control headers with the incoming
operation and
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java
index 4b88b09c8454..c91e10c20b68 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java
@@ -367,7 +367,9 @@ public interface MicrometerEndpointBuilderFactory {
return "CamelMetricsDescription";
}
/**
- * To augment meter tags defined as URI parameters.
+ * To augment meter tags defined as URI parameters. The tags from the
+ * header are used as given; use the tags URI parameters for Simple
+ * expressions.
*
* The option is a: {@code java.lang.Iterable<Tag>} type.
*
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 557f759ecbda..34f28914140f 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
@@ -395,7 +395,9 @@ public interface OpenTelemetryEndpointBuilderFactory {
return "CamelMetricsDescription";
}
/**
- * To augment meter attributes defined as URI parameters.
+ * To augment meter attributes defined as URI parameters. The
attributes
+ * from the header are used as given; use the attributes URI parameters
+ * for Simple expressions.
*
* The option is a: {@code io.opentelemetry.api.common.Attributes}
type.
*