- Published on
Camel Quarkus Observability Support
- Authors
- Name
- James Netherton
Camel Quarkus 0.3.0 includes support for adding observability capabilities such as application metrics, health checks and distributed tracing. This post gives a brief explanation of how to configure and use these features.
Metrics
Camel Quarkus provides an extension which builds on top of Quarkus metrics so that Camel applications can produce metrics and have them processed by tools such as Prometheus.
To start working with metrics, add the camel-quarkus-microprofile-metrics
extension
<dependency>
<groupId>org.apache.quarkus</groupId>
<artifactId>camel-quarkus-microprofile-metrics</artifactId>
</dependency>
NOTE: There's no version specified. It's assumed that the project parent is
camel-quarkus-bom
. Refer to the Camel Quarkus getting started guide.
Provided metrics
When the extension is included, it automatically begins to collect Camel related metrics. Such as:
- The number of completed, failed or inflight exchanges
- The mean, minimum and maximum processing processing times for exchanges through each route
- The Camel context uptime
There's a more comprehensive list of metrics in the extension documentation.
Each metric is tagged with the name of the Camel context and where appropriate, the Camel route id.
You can review the generated metrics by executing curl -H"Accept: application/json" localhost:8080/metrics/application
. To view the output in the OpenMetrics format, remove the -H"Accept: application/json"
argument. The example output should look something like this.
{
"camel.route.exchanges.completed.total;camelContext=camel-quarkus-observability;routeId=route1": 4,
"camel.route.exchanges.completed.total;camelContext=camel-quarkus-observability;routeId=route2": 4,
"camel.context.uptime;camelContext=camel-quarkus-observability": 39799,
"camel.context.exchanges.completed.total;camelContext=camel-quarkus-observability": 8,
"camel.route.externalRedeliveries.total;camelContext=camel-quarkus-observability;routeId=route2": 0,
"camel.context.exchanges.total;camelContext=camel-quarkus-observability": 8,
"camel.exchange.processing": {
"max;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 4760254484,
"p75;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 3905296516,
"stddev;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 1190768315.9025462,
"min;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 1884000000,
"p95;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 4760254484,
"p50;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 1968567546,
"p99;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 4760254484,
"p98;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 4760254484,
"mean;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 2941856018.3989124,
"p999;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 4760254484,
"meanRate;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 0.2068157217537497,
"fiveMinRate;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 0.38112608423917144,
"count;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 8,
"oneMinRate;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 0.32458314691034595,
"fifteenMinRate;camelContext=camel-quarkus-observability;endpointName=timer://greeting?period=10s;eventType=ExchangeCreatedEvent": 0.3934614333270665,
},
"camel.route.externalRedeliveries.total;camelContext=camel-quarkus-observability;routeId=route1": 0,
"camel.route.exchanges.inflight.count;camelContext=camel-quarkus-observability;routeId=route2": 0,
"camel.route.count;camelContext=camel-quarkus-observability": 2,
"camel.context.exchanges.inflight.count;camelContext=camel-quarkus-observability": 0,
"camel.route.exchanges.failed.total;camelContext=camel-quarkus-observability;routeId=route1": 0,
"camel.route.exchanges.failed.total;camelContext=camel-quarkus-observability;routeId=route2": 0,
"camel.context.failuresHandled.total;camelContext=camel-quarkus-observability": 0,
"camel.context.exchanges.failed.total;camelContext=camel-quarkus-observability": 0,
"camel.route.running.count;camelContext=camel-quarkus-observability": 2,
"camel.route.exchanges.total;camelContext=camel-quarkus-observability;routeId=route2": 4,
"camel.route.exchanges.total;camelContext=camel-quarkus-observability;routeId=route1": 4,
"camel.route.failuresHandled.total;camelContext=camel-quarkus-observability;routeId=route2": 0,
"camel.route.failuresHandled.total;camelContext=camel-quarkus-observability;routeId=route1": 0,
"camel.context.externalRedeliveries.total;camelContext=camel-quarkus-observability": 0,
"camel.route.processing": {
"p999;camelContext=camel-quarkus-observability;routeId=route1": 4759773844,
"p95;camelContext=camel-quarkus-observability;routeId=route1": 4759773844,
"max;camelContext=camel-quarkus-observability;routeId=route1": 4759773844,
"p50;camelContext=camel-quarkus-observability;routeId=route1": 1966499655,
"p99;camelContext=camel-quarkus-observability;routeId=route1": 4759773844,
"p75;camelContext=camel-quarkus-observability;routeId=route1": 3903591192,
"min;camelContext=camel-quarkus-observability;routeId=route1": 1887061052,
"mean;camelContext=camel-quarkus-observability;routeId=route1": 2957773658.2611957,
"p98;camelContext=camel-quarkus-observability;routeId=route1": 4759773844,
"stddev;camelContext=camel-quarkus-observability;routeId=route1": 1212814015.7063947,
"oneMinRate;camelContext=camel-quarkus-observability;routeId=route1": 0.16229157345517298,
"meanRate;camelContext=camel-quarkus-observability;routeId=route1": 0.1033277281226765,
"count;camelContext=camel-quarkus-observability;routeId=route1": 4,
"fifteenMinRate;camelContext=camel-quarkus-observability;routeId=route1": 0.19673071666353326,
"fiveMinRate;camelContext=camel-quarkus-observability;routeId=route1": 0.19056304211958572
},
"camel.context.status;camelContext=camel-quarkus-observability": 1
}
Custom metrics
You can generate your own custom metrics by configuring microprofile-metrics endpoints in your Camel routes. For example, to increment a counter metric every 10 seconds:
from("timer:incrementMetric?period=10s")
.to("microprofile-metrics:counter:simple.counter")
To see the full set of available endpoint options, refer to the Camel MicroProfile Metrics documentation.
Alternatively, you can leverage the MicroProfile metrics API in your code. Refer to the Quarkus metrics documentation for more information about that.
Health
Support for health checks with Camel Quarkus is provided by the camel-quarkus-microprofile-health
extension. It builds on top of Quarkus health.
To start working with health checks add the camel-quarkus-microprofile-health
extension
<dependency>
<groupId>org.apache.quarkus</groupId>
<artifactId>camel-quarkus-microprofile-health</artifactId>
</dependency>
Provided health checks
Camel provides some simple liveness and readiness checks out of the box. To see these in action, execute curl localhost:8080/health/live
and curl localhost:8080/health/ready
. You should see an output similar to below.
{
"status": "UP",
"checks": [
{
"name": "camel",
"status": "UP",
"data": {
"contextStatus": "Started",
"name": "camel-quarkus-observability"
}
}
]
}
The first status field indicates that the overall liveness or readiness of the application is "UP". The checks field contains a summary of all of the configured liveness or readiness checks.
In this case there is a check named 'camel' which verifies whether the Camel context status is 'Started'. In this case this condition is true so the health check status is reported as "UP". You'd see it as "DOWN" if the context status was any other value.
Custom health checks
You can write your application health checks using the Camel Health API. There are two convenience classes that can be extended:
AbstractCamelMicroProfileLivenessCheck
AbstractCamelMicroProfileReadinessCheck
For example:
public class CustomLivenessCheck extends AbstractCamelMicroProfileLivenessCheck {
public CustomLivenessCheck() {
super("my-liveness-check-name");
getConfiguration().setEnabled(true);
}
@Override
protected void doCall(HealthCheckResultBuilder builder, Map<String, Object> options) {
if (someSuccessCondition) {
builder.up();
} else {
builder.down();
}
}
}
Alternatively you can leverage MicroProfile Health APIs as outlined in the Quarkus health guide.
Tracing
Support for tracing with Camel Quarkus is provided by the camel-quarkus-opentracing
extension.
To enable tracing support, add the camel-quarkus-opentracing
extension
<dependency>
<groupId>org.apache.quarkus</groupId>
<artifactId>camel-quarkus-opentracing</artifactId>
</dependency>
The extension uses the Camel OpenTracing component and automatically configures it in order to start tracing your routes.
Traces can be sent to a tracing server by adding some configuration options to application.properties
.
quarkus.jaeger.service-name = awesome-service
quarkus.jaeger.sampler-type = const
quarkus.jaeger.sampler-param = 1
quarkus.jaeger.endpoint = http://localhost:14268/api/traces
For local development you can start a local tracing server with Docker.
docker run -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:latest
Example application
There's an example application featuring all of the extensions mentioned in this post here.
Conclusion
Hopefully this post was a useful introduction to the observability features of Camel Quarkus. As ever, feedback and contributions are welcome. Feel free to file an issue for any improvements.