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 9ce7be6fb124 CAMEL-16866: add routepolicy 'long task' timer (#19933)
9ce7be6fb124 is described below
commit 9ce7be6fb12455b80185a2b0ea21af4f56dc6beb
Author: Jono Morris <[email protected]>
AuthorDate: Mon Nov 17 19:27:26 2025 +1300
CAMEL-16866: add routepolicy 'long task' timer (#19933)
* CAMEL-16866 add routepolicy long-task timer
* CAMEL-16866 add routepolicy to doc
---
.../main/docs/opentelemetry-metrics-component.adoc | 51 +++++++-
.../metrics/OpenTelemetryConstants.java | 2 +
.../camel/opentelemetry/metrics/TaskTimer.java | 9 +-
.../OpenTelemetryContextMetricsStatistics.java | 4 +-
.../routepolicy/OpenTelemetryLongTaskTimer.java | 92 ++++++++++++++
.../routepolicy/OpenTelemetryRoutePolicy.java | 29 ++++-
.../OpenTelemetryRoutePolicyConfiguration.java | 13 ++
.../OpenTelemetryRoutePolicyFactory.java | 12 +-
.../OpenTelemetryRoutePolicyNamingStrategy.java | 16 +++
.../OpenTelemetryContextMetricTest.java | 116 +++++++++++++++++
.../OpenTelemetryRoutePolicyLongTaskTest.java | 138 +++++++++++++++++++++
11 files changed, 465 insertions(+), 17 deletions(-)
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 1c434d896401..11eebee91981 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
@@ -340,11 +340,11 @@ Camel specific metrics that are available via the
`OpenTelemetryExchangeEventNot
[width="100%",options="header"]
|=====================================================
-|Default Name |Type |Unit |Description
-|camel.exchange.sent | LongHistogram | TimeUnit.MILLISECONDS | Time taken to
send a message to the endpoint
-|camel.exchange.elapsed | LongHistogram | TimeUnit.MILLISECONDS | Time taken
to complete exchange
-|camel.exchanges.last.time | ObservableLongGauge | TimeUnit.MILLISECONDS |
Last exchange processed time since the Unix epoch
-|camel.exchanges.inflight | ObservableLongGauge | Long | Number of in flight
messages per route
+|Default Name |Type |Description
+|camel.exchange.sent | LongHistogram | Time taken to send a message to the
endpoint; Default time unit is milliseconds
+|camel.exchange.elapsed | LongHistogram | Time taken to complete exchange;
Default time unit is milliseconds
+|camel.exchanges.last.time | ObservableLongGauge | Last exchange processed
time since the Unix epoch; ; Default time unit is milliseconds
+|camel.exchanges.inflight | ObservableLongGauge | Number of in flight messages
per route
|=====================================================
The following options are supported:
@@ -361,6 +361,47 @@ By default, static values are used. When using dynamic
attributes a dynamic to (
leading to high cardinality in metrics.
|=======================================================================
+=== OpenTelemetry Route Policy
+
+Route policy metrics can be enabled by adding the
`OpenTelemetryRoutePolicyFactory` to the `CamelContext`:
+
+[source,java]
+----
+context.addRoutePolicyFactory(new OpenTelemetryRoutePolicyFactory());
+----
+
+[NOTE]
+====
+You can define a dedicated `OpenTelemetryRoutePolicy` per route you want to
instrument
+in case you only want to instrument a few selected routes.
+====
+
+Camel specific metrics that are available via the `OpenTelemetryRoutePolicy`:
+[width="100%",options="header"]
+[width="100%",options="header"]
+|=====================================================
+|Default Name |Type |Description
+| camel.route.policy | LongHistogram | Time taken to complete processing of
exchanges for a route
+| camel.exchanges.succeeded | LongCounter | Number of successfully completed
exchanges
+| camel.exchanges.failed | LongCounter | Number of failed exchanges
+| camel.exchanges.total | LongCounter | Total number of exchanges processed
+| camel.exchanges.external.redeliveries | LongCounter | Number of externally
initiated redeliveries
+| camel.exchanges.failures.handled | LongCounter | Number of failures handled
+| camel.route.policy.tasks.active | ObservableLongUpDownCounter | Number of
tasks currently active
+| camel.route.policy.tasks.duration | ObservableLongUpDownCounter | Total
duration of all active tasks
+|=====================================================
+
+The following options are supported:
+
+[width="100%",options="header"]
+|=======================================================================
+|Name |Default |Description
+| namingStrategy | OpenTelemetryRoutePolicyNamingStrategy.DEFAULT | The
strategy to use for overriding default metric names.
+| policyConfiguration | - | Enables individual metrics to be enabled or
disabled.
+| timeUnit | TimeUnit.MILLISECONDS | The time unit for measuring the route
policy timer.
+| longTaskTimeUnit | TimeUnit.MILLISECONDS | The time unit for the 'long task'
timer that measures total duration of all active tasks.
+|=======================================================================
+
== OpenTelemetry Configuration
Applications can export collected metrics to various backends using different
exporters, including the OpenTelemetry Protocol (OTLP) exporter,
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 e3748d7b1d80..00071859099d 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
@@ -48,6 +48,8 @@ public class OpenTelemetryConstants {
public static final String
DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_EXTERNAL_REDELIVERIES_METER_NAME
= "camel.exchanges.external.redeliveries";
public static final String DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME =
"camel.route.policy";
+ public static final String DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE =
"camel.route.policy.tasks.active";
+ public static final String DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION =
"camel.route.policy.tasks.duration";
// Exchange-event metrics
public static final String DEFAULT_CAMEL_ROUTES_EXCHANGES_INFLIGHT =
"camel.exchanges.inflight";
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/TaskTimer.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/TaskTimer.java
index 89f69fb84a81..c29089b1b30c 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/TaskTimer.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/TaskTimer.java
@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
public class TaskTimer {
private long startTime;
+ private volatile boolean stopped;
public TaskTimer() {
startTime = System.nanoTime();
@@ -30,13 +31,13 @@ public class TaskTimer {
}
public long duration(TimeUnit unit) {
- if (startTime > 0) {
- return unit.convert(System.nanoTime() - startTime,
TimeUnit.NANOSECONDS);
+ if (stopped) {
+ return -1;
}
- return 0;
+ return unit.convert(System.nanoTime() - startTime,
TimeUnit.NANOSECONDS);
}
public void stop() {
- startTime = 0;
+ stopped = true;
}
}
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricsStatistics.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricsStatistics.java
index 206183707e36..8dc385feb0de 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricsStatistics.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricsStatistics.java
@@ -32,8 +32,8 @@ final class OpenTelemetryContextMetricsStatistics extends
OpenTelemetryRoutePoli
OpenTelemetryRoutePolicyNamingStrategy namingStrategy,
OpenTelemetryRoutePolicyConfiguration configuration,
boolean registerKamelets, boolean
registerTemplates,
- TimeUnit timeUnit) {
- super(meter, camelContext, null, namingStrategy, configuration,
timeUnit);
+ TimeUnit timeUnit, TimeUnit
longTaskTimeUnit) {
+ super(meter, camelContext, null, namingStrategy, configuration,
timeUnit, longTaskTimeUnit);
this.registerKamelets = registerKamelets;
this.registerTemplates = registerTemplates;
}
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryLongTaskTimer.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryLongTaskTimer.java
new file mode 100644
index 000000000000..e22737b23ca6
--- /dev/null
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryLongTaskTimer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.opentelemetry.metrics.routepolicy;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableLongUpDownCounter;
+import org.apache.camel.Route;
+import org.apache.camel.opentelemetry.metrics.TaskTimer;
+
+/**
+ * This timer is the equivalent of a 'long task' timer in other metrics
systems. It measures tasks that are still
+ * running, and publishes the number of active tasks and their total duration.
+ */
+public class OpenTelemetryLongTaskTimer {
+
+ // number of active tasks
+ private final ObservableLongUpDownCounter longTasksActive;
+ // total duration of all in progress tasks
+ private final ObservableLongUpDownCounter longTasksDuration;
+ private final Queue<LongTask> activeTasks = new ConcurrentLinkedQueue<>();
+
+ public OpenTelemetryLongTaskTimer(Route route, Meter meter, Attributes
attributes,
+ OpenTelemetryRoutePolicyConfiguration
configuration,
+ OpenTelemetryRoutePolicyNamingStrategy
namingStrategy, TimeUnit longTaskTimeUnit) {
+
+ this.longTasksActive = meter
+
.upDownCounterBuilder(namingStrategy.getLongTasksActiveName(route))
+ .setDescription(route != null ? "Route active long task
metric" : "CamelContext active long task metric")
+ .buildWithCallback(
+ observableMeasurement -> {
+ observableMeasurement.record(activeTasks.size(),
attributes);
+ });
+
+ this.longTasksDuration = meter
+
.upDownCounterBuilder(namingStrategy.getLongTasksDurationName(route))
+ .setDescription(route != null ? "Route long task duration
metric" : "CamelContext long task duration metric")
+ .setUnit(longTaskTimeUnit.name().toLowerCase())
+ .buildWithCallback(
+ observableMeasurement -> observableMeasurement.record(
+ allLongTaskDuration(longTaskTimeUnit),
attributes));
+ }
+
+ public void remove() {
+ if (longTasksActive != null) {
+ longTasksActive.close();
+ }
+ if (longTasksDuration != null) {
+ longTasksDuration.close();
+ }
+ }
+
+ public TaskTimer start() {
+ LongTask task = new LongTask();
+ activeTasks.add(task);
+ return task;
+ }
+
+ private long allLongTaskDuration(TimeUnit unit) {
+ long sum = 0L;
+ long now = System.nanoTime();
+ for (LongTask task : activeTasks) {
+ sum += now - task.getStartTime();
+ }
+ return unit.convert(sum, TimeUnit.NANOSECONDS);
+ }
+
+ private class LongTask extends TaskTimer {
+ public void stop() {
+ activeTasks.remove(this);
+ super.stop();
+ }
+ }
+}
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicy.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicy.java
index 6a4a4f575e95..886cf3fff69d 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicy.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicy.java
@@ -62,12 +62,11 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
boolean registerKamelets;
boolean registerTemplates = true;
- // options
private OpenTelemetryRoutePolicyNamingStrategy namingStrategy =
OpenTelemetryRoutePolicyNamingStrategy.DEFAULT;
private OpenTelemetryRoutePolicyConfiguration configuration = new
OpenTelemetryRoutePolicyConfiguration();
private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
+ private TimeUnit longTaskTimeUnit = TimeUnit.MILLISECONDS;
- // metrics
private final Map<Route, MetricsStatistics> statisticsMap = new
HashMap<>();
private RouteMetric contextStatistic;
@@ -107,6 +106,14 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
this.timeUnit = timeUnit;
}
+ public TimeUnit getLongTaskTimeUnit() {
+ return longTaskTimeUnit;
+ }
+
+ public void setLongTaskTimeUnit(TimeUnit longTaskTimeUnit) {
+ this.longTaskTimeUnit = longTaskTimeUnit;
+ }
+
boolean isRegisterKamelets() {
return registerKamelets;
}
@@ -159,7 +166,7 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
return null;
}
return new MetricsStatistics(
- meter, it.getCamelContext(), it,
getNamingStrategy(), configuration, timeUnit);
+ meter, it.getCamelContext(), it,
getNamingStrategy(), configuration, timeUnit, longTaskTimeUnit);
});
}
@@ -194,6 +201,7 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
private final OpenTelemetryRoutePolicyNamingStrategy namingStrategy;
private final OpenTelemetryRoutePolicyConfiguration configuration;
private final TimeUnit timeUnit;
+ private final TimeUnit longTaskTimeUnit;
// OpenTelemetry objects
private final Meter meter;
@@ -206,11 +214,12 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
private LongCounter exchangesTotal;
private LongCounter externalRedeliveries;
private LongCounter failuresHandled;
+ private OpenTelemetryLongTaskTimer longTaskTimer;
MetricsStatistics(Meter meter, CamelContext camelContext, Route route,
OpenTelemetryRoutePolicyNamingStrategy
namingStrategy,
OpenTelemetryRoutePolicyConfiguration configuration,
- TimeUnit timeUnit) {
+ TimeUnit timeUnit, TimeUnit longTaskTimeUnit) {
this.configuration = ObjectHelper.notNull(configuration,
"OpenTelemetryRoutePolicyConfiguration", this);
this.namingStrategy = ObjectHelper.notNull(namingStrategy,
"OpenTelemetryRoutePolicyNamingStrategy", this);
@@ -218,6 +227,7 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
this.camelContext = camelContext;
this.route = route;
this.timeUnit = timeUnit;
+ this.longTaskTimeUnit = longTaskTimeUnit;
this.attributes = Attributes.of(
AttributeKey.stringKey(CAMEL_CONTEXT_ATTRIBUTE),
route != null ? route.getCamelContext().getName() :
camelContext.getName(),
@@ -259,12 +269,19 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
=
createCounter(namingStrategy.getFailuresHandledName(route),
"Number of failures handled");
}
+ if (configuration.isLongTask()) {
+ longTaskTimer = new OpenTelemetryLongTaskTimer(
+ route, meter, attributes, configuration,
namingStrategy, longTaskTimeUnit);
+ }
}
@Override
public void onExchangeBegin(Exchange exchange) {
String propertyName = propertyName(exchange);
exchange.setProperty(propertyName, new TaskTimer());
+ if (longTaskTimer != null) {
+ exchange.setProperty(propertyName + "_long_task",
longTaskTimer.start());
+ }
}
@Override
@@ -285,7 +302,9 @@ public class OpenTelemetryRoutePolicy extends
RoutePolicySupport implements NonM
@Override
public void remove() {
- // no-op
+ if (longTaskTimer != null) {
+ longTaskTimer.remove();
+ }
}
private void updateAdditionalCounters(Exchange exchange) {
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyConfiguration.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyConfiguration.java
index 039331be07ab..f6c9fd766a72 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyConfiguration.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyConfiguration.java
@@ -30,6 +30,7 @@ public class OpenTelemetryRoutePolicyConfiguration {
private boolean exchangesTotal = true;
private boolean externalRedeliveries = true;
private boolean failuresHandled = true;
+ private boolean longTask;
public boolean isContextEnabled() {
return contextEnabled;
@@ -130,4 +131,16 @@ public class OpenTelemetryRoutePolicyConfiguration {
public void setFailuresHandled(boolean failuresHandled) {
this.failuresHandled = failuresHandled;
}
+
+ public boolean isLongTask() {
+ return longTask;
+ }
+
+ /**
+ * Enable 'long task timer' metrics that track the number of active tasks
and total duration of all tasks in
+ * progress.
+ */
+ public void setLongTask(boolean longTask) {
+ this.longTask = longTask;
+ }
}
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyFactory.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyFactory.java
index c31b11335a49..e3fbef79681e 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyFactory.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyFactory.java
@@ -40,6 +40,7 @@ public class OpenTelemetryRoutePolicyFactory extends
ServiceSupport
private OpenTelemetryRoutePolicyNamingStrategy namingStrategy =
OpenTelemetryRoutePolicyNamingStrategy.DEFAULT;
private OpenTelemetryRoutePolicyConfiguration policyConfiguration = new
OpenTelemetryRoutePolicyConfiguration();
private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
+ private TimeUnit longTaskTimeUnit = TimeUnit.MILLISECONDS;
@Override
public CamelContext getCamelContext() {
@@ -83,12 +84,20 @@ public class OpenTelemetryRoutePolicyFactory extends
ServiceSupport
this.timeUnit = timeUnit;
}
+ public TimeUnit getLongTaskTimeUnit() {
+ return longTaskTimeUnit;
+ }
+
+ public void setLongTaskTimeUnit(TimeUnit longTaskTimeUnit) {
+ this.longTaskTimeUnit = longTaskTimeUnit;
+ }
+
public synchronized RouteMetric
createOrGetContextMetric(OpenTelemetryRoutePolicy policy) {
if (contextMetric == null) {
contextMetric = new OpenTelemetryContextMetricsStatistics(
meter, camelContext, policy.getNamingStrategy(),
policy.getConfiguration(),
policy.isRegisterKamelets(), policy.isRegisterTemplates(),
- policy.getTimeUnit());
+ policy.getTimeUnit(), policy.getLongTaskTimeUnit());
}
return contextMetric;
}
@@ -99,6 +108,7 @@ public class OpenTelemetryRoutePolicyFactory extends
ServiceSupport
routePolicy.setNamingStrategy(getNamingStrategy());
routePolicy.setConfiguration(getPolicyConfiguration());
routePolicy.setTimeUnit(getTimeUnit());
+ routePolicy.setLongTaskTimeUnit(getLongTaskTimeUnit());
routePolicy.setMeter(meter);
return routePolicy;
}
diff --git
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyNamingStrategy.java
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyNamingStrategy.java
index 25f58bdb344a..b401411a0f66 100644
---
a/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyNamingStrategy.java
+++
b/components/camel-opentelemetry-metrics/src/main/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyNamingStrategy.java
@@ -24,6 +24,8 @@ import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFA
import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_SUCCEEDED_METER_NAME;
import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_TOTAL_METER_NAME;
import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_METER_NAME;
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE;
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION;
/**
* Provides a strategy to provide metric names for OpenTelemetry route policy
metrics.
@@ -68,6 +70,16 @@ public interface OpenTelemetryRoutePolicyNamingStrategy {
public String getExternalRedeliveriesName(Route route) {
return
formatName(DEFAULT_CAMEL_ROUTE_POLICY_EXCHANGES_EXTERNAL_REDELIVERIES_METER_NAME);
}
+
+ @Override
+ public String getLongTasksActiveName(Route route) {
+ return formatName(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE);
+ }
+
+ @Override
+ public String getLongTasksDurationName(Route route) {
+ return formatName(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION);
+ }
};
String getName(Route route);
@@ -83,4 +95,8 @@ public interface OpenTelemetryRoutePolicyNamingStrategy {
String getFailuresHandledName(Route route);
String getExternalRedeliveriesName(Route route);
+
+ String getLongTasksActiveName(Route route);
+
+ String getLongTasksDurationName(Route route);
}
diff --git
a/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricTest.java
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricTest.java
new file mode 100644
index 000000000000..67430fefacaf
--- /dev/null
+++
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryContextMetricTest.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.opentelemetry.metrics.routepolicy;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.sdk.metrics.data.LongPointData;
+import io.opentelemetry.sdk.metrics.data.PointData;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE;
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Verifies that there are point data for the context and route when context
metrics are enabled.
+ */
+public class OpenTelemetryContextMetricTest extends
AbstractOpenTelemetryRoutePolicyTest {
+
+ private static final long DELAY = 1500L;
+ private static final long TOLERANCE = 100L;
+
+ @Override
+ public OpenTelemetryRoutePolicyFactory
createOpenTelemetryRoutePolicyFactory() {
+ OpenTelemetryRoutePolicyFactory factory = new
OpenTelemetryRoutePolicyFactory();
+ factory.getPolicyConfiguration().setLongTask(true);
+ return factory;
+ }
+
+ // verify maximum duration of a long task
+ @Test
+ public void testLongTaskDuration() throws Exception {
+ MockEndpoint out = getMockEndpoint("mock:foo");
+ out.expectedMessageCount(1);
+
+ template.asyncSend("direct:foo", x -> {
+ });
+
+ long maxDuration =
pollLongTimer(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION);
+ assertTrue(maxDuration >= 0L && maxDuration < DELAY + TOLERANCE, "max
duration of long task");
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(
+ () -> MockEndpoint.assertIsSatisfied(context));
+ }
+
+ // verify maximum number of concurrent active long tasks
+ @Test
+ public void testLongTaskActive() throws Exception {
+ final int messageCnt = 2;
+ MockEndpoint out = getMockEndpoint("mock:foo");
+ out.expectedMessageCount(messageCnt);
+
+ for (int i = 0; i < messageCnt; i++) {
+ template.asyncSend("direct:foo", x -> {
+ });
+ }
+ long maxActive =
pollLongTimer(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE);
+ assertEquals(messageCnt, maxActive, "max active long tasks");
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(
+ () -> MockEndpoint.assertIsSatisfied(context));
+ }
+
+ // verifies that there are point data for the context and route
+ private long pollLongTimer(String meterName) throws Exception {
+ Thread.sleep(250L);
+ long max = 0L;
+ long curr = 0L;
+ for (int i = 0; i < 10; i++) {
+ Thread.sleep(250L);
+
+ List<PointData> ls = getAllPointData(meterName);
+ // counts from context and route statistics
+ assertEquals(2, ls.size(), "Expected two point data");
+
+ for (var pd : ls) {
+ assertInstanceOf(LongPointData.class, pd);
+ LongPointData lpd = (LongPointData) pd;
+ curr = lpd.getValue();
+ max = Math.max(max, curr);
+ }
+ if (curr == 0L) {
+ break;
+ }
+ }
+ return max;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:foo").routeId("foo").delay(DELAY).to("mock:foo");
+ }
+ };
+ }
+}
diff --git
a/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyLongTaskTest.java
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyLongTaskTest.java
new file mode 100644
index 000000000000..d75262441c25
--- /dev/null
+++
b/components/camel-opentelemetry-metrics/src/test/java/org/apache/camel/opentelemetry/metrics/routepolicy/OpenTelemetryRoutePolicyLongTaskTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.opentelemetry.metrics.routepolicy;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import io.opentelemetry.sdk.metrics.data.LongPointData;
+import io.opentelemetry.sdk.metrics.data.MetricData;
+import io.opentelemetry.sdk.metrics.data.PointData;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE;
+import static
org.apache.camel.opentelemetry.metrics.OpenTelemetryConstants.DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class OpenTelemetryRoutePolicyLongTaskTest extends
AbstractOpenTelemetryRoutePolicyTest {
+
+ private static final long DELAY = 1500L;
+ private static final long TOLERANCE = 100L;
+
+ @Override
+ public OpenTelemetryRoutePolicyFactory
createOpenTelemetryRoutePolicyFactory() {
+ OpenTelemetryRoutePolicyFactory factory = new
OpenTelemetryRoutePolicyFactory();
+ factory.getPolicyConfiguration().setContextEnabled(false);
+ factory.getPolicyConfiguration().setExchangesSucceeded(false);
+ factory.getPolicyConfiguration().setExchangesFailed(false);
+ factory.getPolicyConfiguration().setExchangesTotal(false);
+ factory.getPolicyConfiguration().setExternalRedeliveries(false);
+ factory.getPolicyConfiguration().setFailuresHandled(false);
+ factory.getPolicyConfiguration().setLongTask(true);
+ return factory;
+ }
+
+ // verify maximum duration of a long task using metric name
'camel.route.policy.long.task.duration'
+ @Test
+ public void testLongTaskDuration() throws Exception {
+ MockEndpoint out = getMockEndpoint("mock:foo");
+ out.expectedMessageCount(1);
+
+ template.asyncSend("direct:foo", x -> {
+ });
+
+ long maxDuration =
pollLongTimer(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION);
+ assertTrue(maxDuration >= 0L && maxDuration < DELAY + TOLERANCE, "max
duration of long task");
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(
+ () -> MockEndpoint.assertIsSatisfied(context));
+ }
+
+ // verify maximum number of concurrent active long tasks using metric name
'camel.route.policy.long.task.active'
+ @Test
+ public void testLongTaskActive() throws Exception {
+ final int messageCnt = 2;
+ MockEndpoint out = getMockEndpoint("mock:foo");
+ out.expectedMessageCount(messageCnt);
+
+ for (int i = 0; i < messageCnt; i++) {
+ template.asyncSend("direct:foo", x -> {
+ });
+ }
+ long maxActive =
pollLongTimer(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE);
+ assertEquals(messageCnt, maxActive, "max active long tasks");
+
+ MockEndpoint.assertIsSatisfied(context);
+ }
+
+ // verifies that there are metrics for the 'long task' timer for no
messaging
+ @Test
+ public void testLongTaskMetricData() {
+ List<MetricData> mdList =
getMetricData(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION);
+ assertFalse(mdList.isEmpty());
+ MetricData md = mdList.get(0);
+ assertEquals(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_DURATION, md.getName());
+ assertEquals("Route long task duration metric", md.getDescription());
+
+ mdList = getMetricData(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE);
+ assertFalse(mdList.isEmpty());
+ md = mdList.get(0);
+ assertEquals(DEFAULT_CAMEL_ROUTE_POLICY_TASKS_ACTIVE, md.getName());
+ assertEquals("Route active long task metric", md.getDescription());
+ }
+
+ // returns the maximum value recorded by the 'long-timer' until it goes
back to 0, i.e. messaging is done
+ private long pollLongTimer(String meterName) throws Exception {
+ Thread.sleep(250L);
+ long max = 0L;
+ long curr = 0L;
+
+ for (int i = 0; i < 10; i++) {
+ Thread.sleep(250L);
+
+ List<PointData> ls = getAllPointData(meterName);
+ // contextStatistic is not enabled, so only one point data from
route statistic
+ assertEquals(1, ls.size(), "Expected one point data");
+
+ for (var pd : ls) {
+ assertInstanceOf(LongPointData.class, pd);
+ LongPointData lpd = (LongPointData) pd;
+ curr = lpd.getValue();
+ max = Math.max(max, curr);
+ }
+ if (curr == 0L) {
+ break;
+ }
+ }
+ return max;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:foo").routeId("foo").delay(DELAY).to("mock:foo");
+ }
+ };
+ }
+}