Skip to content

Commit 17554cc

Browse files
authored
add: gRPC Java OpenTelemetry codelab (#12)
This PR adds codelab demonstrating use of gRPC Java OpenTelemetry API along with the solution.
1 parent 7118c09 commit 17554cc

File tree

17 files changed

+1172
-0
lines changed

17 files changed

+1172
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Gradle
2+
build
3+
gradle.properties
4+
.gradle
5+
local.properties
6+
out
7+
8+
# IntelliJ IDEA
9+
.idea
10+
*.iml
11+
*.ipr
12+
*.iws
13+
.ijwb
14+
15+
# OS X
16+
.DS_Store
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# gRPC Java OpenTelemetry
2+
3+
Get hands-on with gRPC's OpenTelemetry API for Java in this interactive codelab! <!-- TODO(arvindbr8): Insert link once codelab is published. -->
4+
5+
Designed for developers already familiar with gRPC and wanting to learn how to instrument their gRPC usage with OpenTelemetry.
6+
7+
#### You'll learn how to:
8+
9+
- setup gRPC's OpenTelemetry API in gRPC Java.
10+
- setup Prometheus exporter with OpenTelemetry.
11+
- explore collected metrics using Prometheus.
12+
13+
## How to use this directory
14+
15+
- [start_here](start_here/) directory serves as a starting point for the
16+
codelab.
17+
- [complete](complete/) directory showcases the finished code, giving you a
18+
peek of how the final implementation should look like.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
plugins {
2+
// Provide convenience executables for trying out the codelabs.
3+
id 'application'
4+
id 'com.google.protobuf' version '0.9.4'
5+
// Generate IntelliJ IDEA's .idea & .iml project files
6+
id 'idea'
7+
}
8+
9+
repositories {
10+
maven { // The google mirror is less flaky than mavenCentral()
11+
url "https://maven-central.storage-download.googleapis.com/maven2/"
12+
}
13+
mavenCentral()
14+
mavenLocal()
15+
}
16+
17+
java {
18+
sourceCompatibility = JavaVersion.VERSION_1_8
19+
targetCompatibility = JavaVersion.VERSION_1_8
20+
}
21+
22+
def grpcVersion = '1.66.0' // LATEST_GRPC_VERSION
23+
def protocVersion = '3.25.3'
24+
def openTelemetryVersion = '1.40.0'
25+
def openTelemetryPrometheusVersion = '1.40.0-alpha'
26+
27+
dependencies {
28+
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
29+
implementation "io.grpc:grpc-stub:${grpcVersion}"
30+
implementation "io.grpc:grpc-opentelemetry:${grpcVersion}"
31+
implementation "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}"
32+
implementation "io.opentelemetry:opentelemetry-sdk-metrics:${openTelemetryVersion}"
33+
implementation "io.opentelemetry:opentelemetry-exporter-logging:${openTelemetryVersion}"
34+
implementation "io.opentelemetry:opentelemetry-exporter-prometheus:${openTelemetryPrometheusVersion}"
35+
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
36+
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
37+
}
38+
39+
protobuf {
40+
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
41+
plugins {
42+
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
43+
}
44+
generateProtoTasks {
45+
all()*.plugins { grpc {} }
46+
}
47+
}
48+
49+
startScripts.enabled = false
50+
51+
task OpenTelemetryHelloWorldServer(type: CreateStartScripts) {
52+
mainClass = 'io.grpc.codelabs.opentelemetry.OpenTelemetryServer'
53+
applicationName = 'opentelemetry-server'
54+
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
55+
classpath = startScripts.classpath
56+
}
57+
58+
task OpenTelemetryHelloWorldClient(type: CreateStartScripts) {
59+
mainClass = 'io.grpc.codelabs.opentelemetry.OpenTelemetryClient'
60+
applicationName = 'opentelemetry-client'
61+
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
62+
classpath = startScripts.classpath
63+
}
64+
65+
application {
66+
applicationDistribution.into('bin') {
67+
from(OpenTelemetryHelloWorldServer)
68+
from(OpenTelemetryHelloWorldClient)
69+
filePermissions {
70+
unix(0755)
71+
}
72+
}
73+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'complete'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
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.grpc.codelabs.opentelemetry;
18+
19+
import io.grpc.Channel;
20+
import io.grpc.Grpc;
21+
import io.grpc.InsecureChannelCredentials;
22+
import io.grpc.ManagedChannel;
23+
import io.grpc.ManagedChannelBuilder;
24+
import io.grpc.StatusRuntimeException;
25+
import io.grpc.codelabs.helloworld.GreeterGrpc;
26+
import io.grpc.codelabs.helloworld.HelloReply;
27+
import io.grpc.codelabs.helloworld.HelloRequest;
28+
import io.grpc.opentelemetry.GrpcOpenTelemetry;
29+
import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
30+
import io.opentelemetry.sdk.OpenTelemetrySdk;
31+
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
32+
import java.util.concurrent.TimeUnit;
33+
import java.util.concurrent.atomic.AtomicBoolean;
34+
import java.util.logging.Level;
35+
import java.util.logging.Logger;
36+
37+
/**
38+
* A simple gRPC client that requests a greeting from the {@link HelloWorldServer} and
39+
* generates gRPC OpenTelmetry metrics data based on the configuration.
40+
*/
41+
public class OpenTelemetryClient {
42+
private static final Logger logger = Logger.getLogger(OpenTelemetryClient.class.getName());
43+
44+
private final GreeterGrpc.GreeterBlockingStub blockingStub;
45+
46+
/** Construct client for accessing HelloWorld server using the existing channel. */
47+
public OpenTelemetryClient(Channel channel) {
48+
blockingStub = GreeterGrpc.newBlockingStub(channel);
49+
}
50+
51+
/** Say hello to server. */
52+
public void greet(String name) {
53+
logger.info("Will try to greet " + name + " ...");
54+
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
55+
HelloReply response;
56+
try {
57+
response = blockingStub.sayHello(request);
58+
} catch (StatusRuntimeException e) {
59+
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
60+
return;
61+
}
62+
logger.info("Greeting: " + response.getMessage());
63+
}
64+
65+
/**
66+
* Greet server. If provided, the first element of {@code args} is the name to use in the
67+
* greeting. The second argument is the target server.
68+
*/
69+
public static void main(String[] args) throws Exception {
70+
String user = "world";
71+
// Access a service running on the local machine on port 50051
72+
String target = "localhost:50051";
73+
// The port on which prometheus metrics are exposed.
74+
int prometheusPort = 9465;
75+
AtomicBoolean sendRpcs = new AtomicBoolean(true);
76+
if (args.length > 0) {
77+
if ("--help".equals(args[0])) {
78+
System.err.println("Usage: [name [target [prometheusPort]]]");
79+
System.err.println("");
80+
System.err.println(" name The name you wish to be greeted by. Defaults to " + user);
81+
System.err.println(" target The server to connect to. Defaults to " + target);
82+
System.err.println(" prometheusPort The port to expose prometheus metrics. Defaults to " + prometheusPort);
83+
System.exit(1);
84+
}
85+
user = args[0];
86+
}
87+
if (args.length > 1) {
88+
target = args[1];
89+
}
90+
if (args.length > 2) {
91+
prometheusPort = Integer.parseInt(args[2]);
92+
}
93+
94+
Thread mainThread = Thread.currentThread();
95+
96+
Runtime.getRuntime().addShutdownHook(new Thread() {
97+
@Override
98+
public void run() {
99+
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
100+
System.err.println("*** shutting down gRPC client since JVM is shutting down");
101+
102+
sendRpcs.set(false);
103+
try {
104+
mainThread.join();
105+
} catch (InterruptedException e) {
106+
e.printStackTrace(System.err);
107+
}
108+
System.err.println("*** client shut down");
109+
}
110+
});
111+
112+
///////////////////////////////////////////////////////////////////////////
113+
// CODELAB SOLUTION : Register GrpcOpenTelemetry.
114+
///////////////////////////////////////////////////////////////////////////
115+
116+
// Adds a PrometheusHttpServer to convert OpenTelemetry metrics to Prometheus format and
117+
// expose these via a HttpServer exporter to the SdkMeterProvider.
118+
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
119+
.setPort(prometheusPort)
120+
.build();
121+
122+
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
123+
.registerMetricReader(prometheusExporter)
124+
.build();
125+
126+
// Initialize OpenTelemetry SDK with MeterProvider configured with Prometeheus.
127+
OpenTelemetrySdk openTelemetrySdk =
128+
OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();
129+
130+
// Initialize gRPC OpenTelemetry.
131+
// Following client metrics are enabled by default :
132+
// 1. grpc.client.attempt.started
133+
// 2. grpc.client.attempt.sent_total_compressed_message_size
134+
// 3. grpc.client.attempt.rcvd_total_compressed_message_size
135+
// 4. grpc.client.attempt.duration
136+
// 5. grpc.client.call.duration
137+
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
138+
.sdk(openTelemetrySdk)
139+
.build();
140+
// Registers gRPC OpenTelemetry globally.
141+
grpcOpenTelmetry.registerGlobal();
142+
143+
// Create a communication channel to the server, known as a Channel.
144+
ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
145+
.build();
146+
OpenTelemetryClient client = new OpenTelemetryClient(channel);
147+
148+
try {
149+
// Run RPCs every second.
150+
while (sendRpcs.get()) {
151+
client.greet(user);
152+
// Sleep for a bit before sending the next RPC.
153+
Thread.sleep(1000);
154+
}
155+
} finally {
156+
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
157+
///////////////////////////////////////////////////////////////////////////
158+
// CODELAB SOLUTION : Add code to shutdown OpenTelemetry SDK.
159+
///////////////////////////////////////////////////////////////////////////
160+
openTelemetrySdk.close();
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)