Skip to content

Commit 9c3437a

Browse files
committed
Add OTEL Java extension demo
**What does this PR do?** This PR adds a PoC for how process context publishing can be integrated into an application using the OTEL Java SDK by using the OTEL extension mechanism. With the extension built, adding the OTEL process context to an application can be as simple as: ``` $ java -Dotel.javaagent.extensions=opentelemetry-java-instrumentation-extension-demo-1.0.jar -jar build/libs/dice-application.jar ... Published OTEL_CTX 2025-10-28T16:17:41.322Z INFO 'Starting DiceApplication using Java 23.0.1 with PID 398219 (dice-application.jar started by ivo.anjo)' : 00000000000000000000000000000000 0000000000000000 [scopeInfo: otel.DiceApplication:] {} ``` and we can look at it as usual: ``` $ sudo ./otel_process_ctx_dump.sh 398219 Found OTEL context for PID 398219 Start address: 75e620025000 00000000 4f 54 45 4c 5f 43 54 58 02 00 00 00 07 f2 73 e5 |OTEL_CTX......s.| 00000010 86 b5 72 18 59 00 00 00 b0 40 73 1c e6 75 00 00 |..r.Y....@s..u..| 00000020 Parsed struct: otel_process_ctx_signature : "OTEL_CTX" otel_process_ctx_version : 2 otel_process_ctx_published_at_ns : 1761669995235111431 (2025-10-28 16:46:35 GMT) otel_process_payload_size : 89 otel_process_payload : 0x000075e61c7340b0 Payload dump (89 bytes): 00000000 12 00 1a 24 39 66 66 33 63 62 31 64 2d 39 62 33 |...$9ff3cb1d-9b3| 00000010 65 2d 34 31 33 33 2d 62 38 34 32 2d 66 39 61 37 |e-4133-b842-f9a7| 00000020 31 62 37 32 61 39 61 63 22 10 64 69 63 65 2d 61 |1b72a9ac".dice-a| 00000030 70 70 6c 69 63 61 74 69 6f 6e 2a 00 32 04 6a 61 |pplication*.2.ja| 00000040 76 61 3a 06 31 2e 35 35 2e 30 42 0d 6f 70 65 6e |va:.1.55.0B.open| 00000050 74 65 6c 65 6d 65 74 72 79 |telemetry| 00000059 Protobuf decode: service_instance_id: "9ff3cb1d-9b3e-4133-b842-f9a71b72a9ac" service_name: "dice-application" telemetry_sdk_language: "java" telemetry_sdk_version: "1.55.0" telemetry_sdk_name: "opentelemetry" ``` **Motivation:** Trying this out was a nice suggestion we got from the OTEL Java SDK SIG as a way of showing off how this could fit in an OTEL app. **Additional Notes:** N/A **How to test the change?** The `README.md` includes instructions on how to build and run the example.
1 parent c73a814 commit 9c3437a

File tree

19 files changed

+1246
-0
lines changed

19 files changed

+1246
-0
lines changed

otel-java-extension-demo/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# OTEL Java Extension Demo
2+
3+
This is a demo for using the OTEL Java extension capability described in https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/examples/extension to automatically publish the OTEL process context for an example Java application. (The demo is also partially based on the code from that repo)
4+
5+
The Java application in use is the OTEL "Getting Started by Example" app from https://opentelemetry.io/docs/languages/java/getting-started/ .
6+
7+
This demo currently requires Linux AND Java 22+.
8+
9+
Here's how to run it:
10+
11+
1. Verify you're running the right Java version:
12+
13+
```
14+
$ java -version
15+
openjdk version "23.0.1" 2024-10-15
16+
OpenJDK Runtime Environment Corretto-23.0.1.8.1 (build 23.0.1+8-FR)
17+
OpenJDK 64-Bit Server VM Corretto-23.0.1.8.1 (build 23.0.1+8-FR, mixed mode, sharing)
18+
```
19+
20+
2. Build the extension:
21+
22+
```
23+
$ cd otel-process-ctx-extension/
24+
$ ./gradlew jar
25+
$ cd -
26+
```
27+
28+
3. Start the Java app:
29+
30+
```
31+
$ cd dice-application/
32+
$ ./gradlew assemble
33+
$ export JAVA_TOOL_OPTIONS="-javaagent:opentelemetry-javaagent.jar" \
34+
OTEL_TRACES_EXPORTER=logging \
35+
OTEL_METRICS_EXPORTER=logging \
36+
OTEL_LOGS_EXPORTER=logging \
37+
OTEL_METRIC_EXPORT_INTERVAL=15000
38+
$ java -Dotel.javaagent.extensions=../otel-process-ctx-extension/build/libs/opentelemetry-java-instrumentation-extension-demo-1.0.jar -jar build/libs/dice-application.jar
39+
```
40+
41+
4. Upon starting up, the app should flag if the process published successfully:
42+
43+
```
44+
Picked up JAVA_TOOL_OPTIONS: -javaagent:opentelemetry-javaagent.jar
45+
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
46+
[otel.javaagent 2025-10-28 16:17:38:511 +0000] [main] INFO io.opentelemetry.javaagent.tooling.VersionLogger - opentelemetry-javaagent - version: 2.21.0
47+
WARNING: A restricted method in java.lang.foreign.Linker has been called
48+
WARNING: java.lang.foreign.Linker::downcallHandle has been called by com.example.javaagent.OtelProcessCtx in an unnamed module
49+
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
50+
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
51+
52+
Published OTEL_CTX
53+
2025-10-28T16:17:41.322Z INFO 'Starting DiceApplication using Java 23.0.1 with PID 398219 (dice-application.jar started by ivo.anjo)' : 00000000000000000000000000000000 0000000000000000 [scopeInfo: otel.DiceApplication:] {}
54+
```
55+
56+
5. You can now take the PID and use the `otel_process_ctx_dump.sh` from the `anonmapping-clib` folder to print the application context:
57+
58+
```
59+
$ sudo ./otel_process_ctx_dump.sh 398219
60+
Found OTEL context for PID 398219
61+
Start address: 75e620025000
62+
00000000 4f 54 45 4c 5f 43 54 58 02 00 00 00 07 f2 73 e5 |OTEL_CTX......s.|
63+
00000010 86 b5 72 18 59 00 00 00 b0 40 73 1c e6 75 00 00 |..r.Y....@s..u..|
64+
00000020
65+
Parsed struct:
66+
otel_process_ctx_signature : "OTEL_CTX"
67+
otel_process_ctx_version : 2
68+
otel_process_ctx_published_at_ns : 1761669995235111431 (2025-10-28 16:46:35 GMT)
69+
otel_process_payload_size : 89
70+
otel_process_payload : 0x000075e61c7340b0
71+
Payload dump (89 bytes):
72+
00000000 12 00 1a 24 39 66 66 33 63 62 31 64 2d 39 62 33 |...$9ff3cb1d-9b3|
73+
00000010 65 2d 34 31 33 33 2d 62 38 34 32 2d 66 39 61 37 |e-4133-b842-f9a7|
74+
00000020 31 62 37 32 61 39 61 63 22 10 64 69 63 65 2d 61 |1b72a9ac".dice-a|
75+
00000030 70 70 6c 69 63 61 74 69 6f 6e 2a 00 32 04 6a 61 |pplication*.2.ja|
76+
00000040 76 61 3a 06 31 2e 35 35 2e 30 42 0d 6f 70 65 6e |va:.1.55.0B.open|
77+
00000050 74 65 6c 65 6d 65 74 72 79 |telemetry|
78+
00000059
79+
Protobuf decode:
80+
service_instance_id: "9ff3cb1d-9b3e-4133-b842-f9a71b72a9ac"
81+
service_name: "dice-application"
82+
telemetry_sdk_language: "java"
83+
telemetry_sdk_version: "1.55.0"
84+
telemetry_sdk_name: "opentelemetry"
85+
86+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package otel;
2+
3+
import org.springframework.boot.Banner;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
7+
@SpringBootApplication
8+
public class DiceApplication {
9+
public static void main(String[] args) {
10+
SpringApplication app = new SpringApplication(DiceApplication.class);
11+
app.setBannerMode(Banner.Mode.OFF);
12+
app.run(args);
13+
}
14+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package otel;
2+
3+
import java.util.Optional;
4+
import java.util.concurrent.ThreadLocalRandom;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.web.bind.annotation.GetMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
public class RollController {
13+
private static final Logger logger = LoggerFactory.getLogger(RollController.class);
14+
15+
@GetMapping("/rolldice")
16+
public String index(@RequestParam("player") Optional<String> player) {
17+
int result = this.getRandomNumber(1, 6);
18+
if (player.isPresent()) {
19+
logger.info("{} is rolling the dice: {}", player.get(), result);
20+
} else {
21+
logger.info("Anonymous player is rolling the dice: {}", result);
22+
}
23+
return Integer.toString(result);
24+
}
25+
26+
public int getRandomNumber(int min, int max) {
27+
return ThreadLocalRandom.current().nextInt(min, max + 1);
28+
}
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
plugins {
2+
id("java")
3+
id("org.springframework.boot") version "3.5.7"
4+
id("io.spring.dependency-management") version "1.1.7"
5+
}
6+
7+
sourceSets {
8+
main {
9+
java.setSrcDirs(setOf("."))
10+
}
11+
}
12+
13+
repositories {
14+
mavenCentral()
15+
}
16+
17+
dependencies {
18+
implementation("org.springframework.boot:spring-boot-starter-web")
19+
}
Binary file not shown.
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
5+
networkTimeout=10000
6+
validateDistributionUrl=true
7+
zipStoreBase=GRADLE_USER_HOME
8+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)