Skip to content

Commit 36b81da

Browse files
authored
Telemetry configuration example (#202)
* Telemetry configuration example
1 parent cc133aa commit 36b81da

File tree

18 files changed

+685
-0
lines changed

18 files changed

+685
-0
lines changed

examples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<module>webserver</module>
6565
<module>coherence</module>
6666
<module>declarative</module>
67+
<module>telemetry</module>
6768
</modules>
6869

6970
<build>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Helidon OpenTelemetry SE Example
2+
3+
This project implements a simple Hello World REST service using Helidon SE and using OpenTelemetry tracing prepared using Helidon configuration.
4+
5+
## Download and start a telemetry back-end
6+
One easy way to see telemetry in action is to run a back-end server that can collect OpenTelemetry OTLP telemetry data and display it. This example Helidon service by default uses OpenTelemetry to transmit its data using OTLP.
7+
8+
One option is to download the [Jaeger back-end](https://www.jaegertracing.io/download/), install it, and run it, but most modern backends support OTLP. Later steps in this example use Jaeger as an example back-end.
9+
10+
## View the telemetry configuration
11+
Look at the `src/main/resources/application.yaml` file. It contains configuration for OpenTelemetry. Note these settings under `telemetry`:
12+
* `service` - Assigns the service name by which all telemetry from this application is identified. You will use this later when using the telemetry back-end UI to browse traces.
13+
* `tracing.attributes` - Declares settings applied to all traces transmitted to the back-end.
14+
* `processors` - Specifies a single span processor, `simple`, which emits each span as soon as it is ended. This makes sure that OpenTelemetry sends span data to the back-end as soon as possible.
15+
16+
**NOTE**: In production systems, you should use the default `batch` processor type (with its additional settings if you wish) for better network performance.
17+
18+
## Build and run
19+
20+
With JDK21
21+
```bash
22+
mvn package
23+
java -jar target/helidon-examples-telemetry-config.jar
24+
```
25+
26+
## Exercise the application
27+
28+
Basic:
29+
```
30+
curl -X GET http://localhost:8080/simple-greet
31+
Hello World!
32+
```
33+
34+
35+
JSON:
36+
```
37+
curl -X GET http://localhost:8080/greet
38+
{"message":"Hello World!"}
39+
40+
curl -X GET http://localhost:8080/greet/Joe
41+
{"message":"Hello Joe!"}
42+
43+
curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
44+
45+
curl -X GET http://localhost:8080/greet/Jose
46+
{"message":"Hola Jose!"}
47+
```
48+
49+
## Use the telemetry back-end to view tracing information
50+
Use a browser to access the back-end UI and view the spans. For example, with Jaeger:
51+
1. Access `http://localhost:16686`.
52+
2. Expand the upper-left "Service" drop list and select "otel-config-example" and then click the "Find Traces" button. ![Service Selection](images/service-selection.png "Service Selection")
53+
54+
Recall that the configuration assigns "otel-config-example" as the `telemetry.service`, so that is the service name the back-end displays.
55+
3. The UI displays separate traces for each of the requests you made to the Helidon service.
56+
4. Click on one of the traces.
57+
5. The back-end shows two or more spans, depending on which trace you clicked. ![Example GET trace](images/get-trace.png "GET trace")
58+
6. Click on any of the spans. ![Example GET span](images/get-span.png "GET span") Note that the "Process" tags include values for `x` and `y` from the `attributes` settings in the `application.yaml` config file.
59+
89 KB
Loading
60.9 KB
Loading
120 KB
Loading
46 KB
Loading

examples/telemetry/config/pom.xml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright (c) 2025 Oracle and/or its affiliates.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0"
20+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
22+
<modelVersion>4.0.0</modelVersion>
23+
<parent>
24+
<groupId>io.helidon.applications</groupId>
25+
<artifactId>helidon-se</artifactId>
26+
<version>4.3.0-SNAPSHOT</version>
27+
<relativePath/>
28+
</parent>
29+
<groupId>io.helidon.examples.telemetry</groupId>
30+
<artifactId>helidon-examples-telemetry-config</artifactId>
31+
<version>1.0-SNAPSHOT</version>
32+
<name>Helidon Examples Telemetry Configuration</name>
33+
34+
<properties>
35+
<mainClass>io.helidon.examples.telemetry.otelconfig.Main</mainClass>
36+
</properties>
37+
38+
<dependencies>
39+
<dependency>
40+
<groupId>io.helidon.webserver</groupId>
41+
<artifactId>helidon-webserver</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>io.helidon.telemetry</groupId>
45+
<artifactId>helidon-telemetry-opentelemetry-config</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>io.helidon.webserver.observe</groupId>
49+
<artifactId>helidon-webserver-observe-tracing</artifactId>
50+
</dependency>
51+
<dependency>
52+
<groupId>io.helidon.http.media</groupId>
53+
<artifactId>helidon-http-media-jsonp</artifactId>
54+
</dependency>
55+
<dependency>
56+
<groupId>io.helidon.config</groupId>
57+
<artifactId>helidon-config-yaml</artifactId>
58+
</dependency>
59+
<dependency>
60+
<groupId>io.opentelemetry</groupId>
61+
<artifactId>opentelemetry-api</artifactId>
62+
</dependency>
63+
<dependency>
64+
<groupId>io.helidon.tracing.providers</groupId>
65+
<artifactId>helidon-tracing-providers-opentelemetry</artifactId>
66+
<scope>runtime</scope>
67+
</dependency>
68+
<dependency>
69+
<groupId>io.helidon.webserver.observe</groupId>
70+
<artifactId>helidon-webserver-observe-telemetry-tracing</artifactId>
71+
<scope>runtime</scope>
72+
</dependency>
73+
<dependency>
74+
<groupId>io.opentelemetry</groupId>
75+
<artifactId>opentelemetry-exporter-otlp</artifactId>
76+
<scope>runtime</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>io.helidon.logging</groupId>
80+
<artifactId>helidon-logging-jul</artifactId>
81+
<scope>runtime</scope>
82+
</dependency>
83+
<dependency>
84+
<groupId>io.helidon.telemetry.testing</groupId>
85+
<artifactId>helidon-telemetry-testing-tracing</artifactId>
86+
<scope>test</scope>
87+
</dependency>
88+
<dependency>
89+
<groupId>io.helidon.webclient</groupId>
90+
<artifactId>helidon-webclient</artifactId>
91+
<scope>test</scope>
92+
</dependency>
93+
<dependency>
94+
<groupId>org.junit.jupiter</groupId>
95+
<artifactId>junit-jupiter-api</artifactId>
96+
<scope>test</scope>
97+
</dependency>
98+
<dependency>
99+
<groupId>org.hamcrest</groupId>
100+
<artifactId>hamcrest-all</artifactId>
101+
<scope>test</scope>
102+
</dependency>
103+
<dependency>
104+
<groupId>io.helidon.webserver.testing.junit5</groupId>
105+
<artifactId>helidon-webserver-testing-junit5</artifactId>
106+
<scope>test</scope>
107+
</dependency>
108+
</dependencies>
109+
110+
<build>
111+
<plugins>
112+
<plugin>
113+
<groupId>org.apache.maven.plugins</groupId>
114+
<artifactId>maven-dependency-plugin</artifactId>
115+
<executions>
116+
<execution>
117+
<id>copy-libs</id>
118+
</execution>
119+
</executions>
120+
</plugin>
121+
</plugins>
122+
</build>
123+
</project>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.examples.telemetry.otelconfig;
18+
19+
import java.util.Collections;
20+
import java.util.concurrent.atomic.AtomicReference;
21+
22+
import io.helidon.config.Config;
23+
import io.helidon.http.Status;
24+
import io.helidon.webserver.http.HttpRules;
25+
import io.helidon.webserver.http.HttpService;
26+
import io.helidon.webserver.http.ServerRequest;
27+
import io.helidon.webserver.http.ServerResponse;
28+
29+
import jakarta.json.Json;
30+
import jakarta.json.JsonBuilderFactory;
31+
import jakarta.json.JsonObject;
32+
33+
/**
34+
* A simple service to greet you. Examples:
35+
* <p>
36+
* Get default greeting message:
37+
* {@code curl -X GET http://localhost:8080/greet}
38+
* <p>
39+
* Get greeting message for Joe:
40+
* {@code curl -X GET http://localhost:8080/greet/Joe}
41+
* <p>
42+
* Change greeting
43+
* {@code curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting}
44+
* <p>
45+
* The message is returned as a JSON object
46+
*/
47+
class GreetService implements HttpService {
48+
49+
50+
private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
51+
52+
/**
53+
* The config value for the key {@code greeting}.
54+
*/
55+
private final AtomicReference<String> greeting = new AtomicReference<>();
56+
57+
GreetService() {
58+
this(Config.global().get("app"));
59+
}
60+
61+
GreetService(Config appConfig) {
62+
greeting.set(appConfig.get("greeting").asString().orElse("Ciao"));
63+
}
64+
65+
/**
66+
* A service registers itself by updating the routing rules.
67+
*
68+
* @param rules the routing rules.
69+
*/
70+
@Override
71+
public void routing(HttpRules rules) {
72+
rules
73+
.get("/", this::getDefaultMessageHandler)
74+
.get("/{name}", this::getMessageHandler)
75+
.put("/greeting", this::updateGreetingHandler);
76+
}
77+
78+
/**
79+
* Return a worldly greeting message.
80+
*
81+
* @param request the server request
82+
* @param response the server response
83+
*/
84+
private void getDefaultMessageHandler(ServerRequest request,
85+
ServerResponse response) {
86+
sendResponse(response, "World");
87+
}
88+
89+
/**
90+
* Return a greeting message using the name that was provided.
91+
*
92+
* @param request the server request
93+
* @param response the server response
94+
*/
95+
private void getMessageHandler(ServerRequest request,
96+
ServerResponse response) {
97+
String name = request.path().pathParameters().get("name");
98+
sendResponse(response, name);
99+
}
100+
101+
private void sendResponse(ServerResponse response, String name) {
102+
String msg = String.format("%s %s!", greeting.get(), name);
103+
104+
JsonObject returnObject = JSON.createObjectBuilder()
105+
.add("message", msg)
106+
.build();
107+
response.send(returnObject);
108+
}
109+
110+
private void updateGreetingFromJson(JsonObject jo, ServerResponse response) {
111+
112+
if (!jo.containsKey("greeting")) {
113+
JsonObject jsonErrorObject = JSON.createObjectBuilder()
114+
.add("error", "No greeting provided")
115+
.build();
116+
response.status(Status.BAD_REQUEST_400)
117+
.send(jsonErrorObject);
118+
return;
119+
}
120+
121+
greeting.set(jo.getString("greeting"));
122+
response.status(Status.NO_CONTENT_204).send();
123+
}
124+
125+
/**
126+
* Set the greeting to use in future messages.
127+
*
128+
* @param request the server request
129+
* @param response the server response
130+
*/
131+
private void updateGreetingHandler(ServerRequest request,
132+
ServerResponse response) {
133+
updateGreetingFromJson(request.content().as(JsonObject.class), response);
134+
}
135+
136+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.examples.telemetry.otelconfig;
18+
19+
import io.helidon.config.Config;
20+
import io.helidon.logging.common.LogConfig;
21+
import io.helidon.service.registry.Services;
22+
import io.helidon.webserver.WebServer;
23+
import io.helidon.webserver.http.HttpRouting;
24+
25+
/**
26+
* The application main class.
27+
*/
28+
public class Main {
29+
30+
/**
31+
* Cannot be instantiated.
32+
*/
33+
private Main() {
34+
}
35+
36+
/**
37+
* Application main entry point.
38+
* @param args command line arguments.
39+
*/
40+
public static void main(String[] args) {
41+
42+
// load logging configuration
43+
LogConfig.configureRuntime();
44+
45+
// initialize config from default configuration
46+
Config config = Services.get(Config.class);
47+
48+
WebServer server = WebServer.builder()
49+
.config(config.get("server"))
50+
.routing(Main::routing)
51+
.build()
52+
.start();
53+
54+
System.out.println("WEB server is up! http://localhost:" + server.port() + "/simple-greet");
55+
56+
}
57+
58+
/**
59+
* Updates HTTP Routing.
60+
*/
61+
static void routing(HttpRouting.Builder routing) {
62+
routing
63+
.register("/greet", new GreetService())
64+
.get("/simple-greet", (req, res) -> res.send("Hello World!"));
65+
}
66+
}

0 commit comments

Comments
 (0)