Skip to content

Commit

Permalink
Add observability example
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesnetherton committed Oct 22, 2019
1 parent 876b52d commit ad39fe6
Show file tree
Hide file tree
Showing 10 changed files with 465 additions and 0 deletions.
66 changes: 66 additions & 0 deletions examples/observability/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
= Camel Quarkus Observability

This example project demonstrates how to add support for metrics, health checks and distributed tracing.

=== Running

[source]
----
$ mvn clean compile quarkus:dev -DnoDeps
----

==== Metrics endpoint

Metrics are exposed on an HTTP endpoint at `/metrics`. You can also browse application specific metrics from the `/metrics/application` endpoint.

To view all Camel metrics do:

[source]
----
$ curl localhost:8080/metrics/application
----

To pick out specific metrics you can either use `grep` or the `https://stedolan.github.io/jq/[jq]` library :

[source]
----
$ curl -s -H"Accept: application/json" localhost:8080/metrics/application | jq '.["camel.context.exchanges.completed.total;camelContext=camel-quarkus-observability"]'
----

==== Health endpoint

Camel provides some out of the box liveness and readiness checks. To see this working, interrogate the `/health/live` and `/health/ready` endpoints:

[source]
----
$ curl -s localhost:8080/health/live
----

[source]
----
$ curl -s localhost:8080/health/ready
----

The JSON output will contain a check named 'camel' for verifying whether the `CamelContext` is in the 'Started' state and another check to verify whether each individual route is in the 'Started' state.

This example project contains a custom liveness check class `CustomLivenessCheck` and custom readiness check class `CustomReadinessCheck` which leverage the Camel health API.
You'll see these listed in the health JSON as 'custom-liveness-check' and 'custom-readiness-check'. On every 5th invocation of these checks, the health status will be reported as DOWN.

You can also directly leverage MicroProfile Metrics APIs to create checks. Class `CamelUptimeHealthCheck` demonstrates how to register a rediness check.

==== Tracing

The tracing configuration for the application can be found within `application.properties`.

To view tracing events, start a Jaeger tracing server. A simple way of doing this is with Docker:

[source]
----
$ 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
----

With the server running, browse to http://localhost:16686. Then choose 'greetings-service' from the 'Service' drop down and click the 'Find Traces' button.

The netty-http consumer route introduces a random delay to simulate latency, hence the overall time of each trace should be different. When viewing a trace, you should see
a hierarchy of 3 spans showing the progression of the message exchange through each endpoint.

144 changes: 144 additions & 0 deletions examples/observability/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-bom</artifactId>
<version>0.2.1-SNAPSHOT</version>
<relativePath>../../poms/bom/pom.xml</relativePath>
</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>camel-quarkus-examples-observability</artifactId>
<name>Camel Quarkus :: Examples :: Observability</name>
<description>Camel Quarkus Example :: Observability</description>

<properties>
<native-image.docker-build>true</native-image.docker-build>
<native-image.container-runtime>docker</native-image.container-runtime>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-timer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-netty-http</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-microprofile-health</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-microprofile-metrics</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-opentracing</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<id>build</id>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>native-image</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemProperties>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<id>native-image</id>
<goals>
<goal>native-image</goal>
</goals>
<configuration>
<enableServer>false</enableServer>
<cleanupServer>true</cleanupServer>
<enableHttpUrlHandler>true</enableHttpUrlHandler>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.acme.observability;

import org.apache.camel.builder.RouteBuilder;

public class Routes extends RouteBuilder {

@Override
public void configure() throws Exception {
// Invokes a simple greeting endpoint every 10 seconds
from("timer:greeting?period=10s")
.to("netty-http:http://localhost:8099/greeting");

from("netty-http:0.0.0.0:8099/greeting")
// Random delay to simulate latency
.delay(simple("${random(1000, 5000)}"))
.setBody(constant("Hello From Camel Quarkus!"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.acme.observability.health.camel;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.camel.health.HealthCheckResultBuilder;
import org.apache.camel.microprofile.health.AbstractCamelMicroProfileLivenessCheck;

/**
* A simple custom liveness check which utilizes the Camel Health API.
*
* The check status is recorded as DOWN on every 5th invocation.
*/
public class CustomLivenessCheck extends AbstractCamelMicroProfileLivenessCheck {

AtomicInteger hitCount = new AtomicInteger();

public CustomLivenessCheck() {
super("custom-liveness-check");
getConfiguration().setEnabled(true);
}

@Override
protected void doCall(HealthCheckResultBuilder builder, Map<String, Object> options) {
int hits = hitCount.incrementAndGet();

// Flag the check as DOWN on every 5th invocation, else it is UP
if (hits %5 == 0) {
builder.down();
} else {
builder.up();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.acme.observability.health.camel;

import java.util.Map;

import org.apache.camel.health.HealthCheckResultBuilder;
import org.apache.camel.microprofile.health.AbstractCamelMicroProfileReadinessCheck;

/**
* A simple custom liveness check which utilizes the Camel Health API.
*/
public class CustomReadinessCheck extends AbstractCamelMicroProfileReadinessCheck {

public CustomReadinessCheck() {
super("custom-readiness-check");
getConfiguration().setEnabled(true);
}

@Override
protected void doCall(HealthCheckResultBuilder builder, Map<String, Object> options) {
builder.up();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.acme.observability.health.microprofile;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.apache.camel.CamelContext;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipse.microprofile.health.Readiness;

/**
* A simple CamelContext uptime readiness check which implements the MicroProfile Health API.
*/
@Readiness
@ApplicationScoped
public class CamelUptimeHealthCheck implements HealthCheck {

@Inject
CamelContext camelContext;

@Override
public HealthCheckResponse call() {
HealthCheckResponseBuilder builder = HealthCheckResponse.named("Uptime readiness check");

if (camelContext.getUptimeMillis() > 0) {
builder.up();
} else {
builder.down();
}

return builder.build();
}
}

0 comments on commit ad39fe6

Please sign in to comment.