Skip to content

Commit

Permalink
examples: Add gRPC OpenTelemetry example (v1.65.x backport) (#11309)
Browse files Browse the repository at this point in the history
  • Loading branch information
DNVindhya committed Jun 23, 2024
1 parent b9927b0 commit d6ce8c5
Show file tree
Hide file tree
Showing 8 changed files with 780 additions and 0 deletions.
54 changes: 54 additions & 0 deletions examples/example-opentelemetry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
gRPC OpenTelemetry Example
================

The example extends the gRPC "hello world" example by modifying the client and server to
showcase a sample configuration for gRPC OpenTelemetry with a Prometheus exporter.

The example requires grpc-java to be pre-built. Using a release tag will download the relevant binaries
from a maven repository. But if you need the latest SNAPSHOT binaries you will need to follow
[COMPILING](../../COMPILING.md) to build these.

### Build the example

The source code is [here](src/main/java/io/grpc/examples/opentelemetry).
To build the example, run in this directory:
```
$ ../gradlew installDist
```
The build creates scripts `opentelemetry-server` and `opentelemetry-client` in the `build/install/example-opentelemetry/bin/` directory
which can be used to run this example. The example requires the server to be running before starting the
client.

### Run the example

**opentelemetry-server**:

The opentelemetry-server accepts optional arguments for server-port and prometheus-port:

```text
USAGE: opentelemetry-server [server-port [prometheus-port]]
```

**opentelemetry-client**:

The opentelemetry-client accepts optional arguments for user-name, target and prometheus-port:

```text
USAGE: opentelemetry-client-client [user-name [target [prometheus-port]]]
```

The opentelemetry-client continuously sends an RPC to the server every second.

To make sure that the server and client metrics are being exported properly, in
a separate terminal, run the following:

```
$ curl localhost:9464/metrics
```

```
$ curl localhost:9465/metrics
```

> ***NOTE:*** If the prometheus endpoint configured is overridden, please update the target in the
> above curl command.
92 changes: 92 additions & 0 deletions examples/example-opentelemetry/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
plugins {
// Provide convenience executables for trying out the examples.
id 'application'
id 'com.google.protobuf' version '0.9.4'
// Generate IntelliJ IDEA's .idea & .iml project files
id 'idea'
}

repositories {
maven { // The google mirror is less flaky than mavenCentral()
url "https://maven-central.storage-download.googleapis.com/maven2/"
}
mavenCentral()
mavenLocal()
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

// IMPORTANT: You probably want the non-SNAPSHOT version of gRPC. Make sure you
// are looking at a tagged version of the example and not "master"!

// Feel free to delete the comment at the next line. It is just for safely
// updating the version in our release process.
def grpcVersion = '1.65.0-SNAPSHOT' // CURRENT_GRPC_VERSION
def protocVersion = '3.25.1'
def openTelemetryVersion = '1.39.0'
def openTelemetryPrometheusVersion = '1.39.0-alpha'

dependencies {
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
implementation "io.grpc:grpc-opentelemetry:${grpcVersion}"
implementation "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-sdk-metrics:${openTelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-exporter-logging:${openTelemetryVersion}"
implementation "io.opentelemetry:opentelemetry-exporter-prometheus:${openTelemetryPrometheusVersion}"
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
}

protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}

startScripts.enabled = false

task OpenTelemetryHelloWorldServer(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.opentelemetry.OpenTelemetryServer'
applicationName = 'opentelemetry-server'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

task OpenTelemetryHelloWorldClient(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.opentelemetry.OpenTelemetryClient'
applicationName = 'opentelemetry-client'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

task LoggingOpenTelemetryHelloWorldServer(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.opentelemetry.logging.LoggingOpenTelemetryServer'
applicationName = 'logging-opentelemetry-server'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

task LoggingOpenTelemetryHelloWorldClient(type: CreateStartScripts) {
mainClass = 'io.grpc.examples.opentelemetry.logging.LoggingOpenTelemetryClient'
applicationName = 'logging-opentelemetry-client'
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
classpath = startScripts.classpath
}

application {
applicationDistribution.into('bin') {
from(OpenTelemetryHelloWorldServer)
from(OpenTelemetryHelloWorldClient)
from(LoggingOpenTelemetryHelloWorldServer)
from(LoggingOpenTelemetryHelloWorldClient)
fileMode = 0755
}
}
1 change: 1 addition & 0 deletions examples/example-opentelemetry/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'example-opentelemetry'
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright 2024 The gRPC Authors
*
* Licensed 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 io.grpc.examples.opentelemetry;

import io.grpc.Channel;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.opentelemetry.GrpcOpenTelemetry;
import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* A simple gRPC client that requests a greeting from the {@link HelloWorldServer} and
* generates gRPC OpenTelmetry metrics data based on the configuration.
*/
public class OpenTelemetryClient {
private static final Logger logger = Logger.getLogger(OpenTelemetryClient.class.getName());

private final GreeterGrpc.GreeterBlockingStub blockingStub;

/** Construct client for accessing HelloWorld server using the existing channel. */
public OpenTelemetryClient(Channel channel) {
blockingStub = GreeterGrpc.newBlockingStub(channel);
}

/** Say hello to server. */
public void greet(String name) {
logger.info("Will try to greet " + name + " ...");
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}
logger.info("Greeting: " + response.getMessage());
}

/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting. The second argument is the target server.
*/
public static void main(String[] args) throws Exception {
String user = "world";
// Access a service running on the local machine on port 50051
String target = "localhost:50051";
// The port on which prometheus metrics are exposed.
int prometheusPort = 9465;
AtomicBoolean sendRpcs = new AtomicBoolean(true);
if (args.length > 0) {
if ("--help".equals(args[0])) {
System.err.println("Usage: [name [target [prometheusPort]]]");
System.err.println("");
System.err.println(" name The name you wish to be greeted by. Defaults to " + user);
System.err.println(" target The server to connect to. Defaults to " + target);
System.err.println(" prometheusPort The port to expose prometheus metrics. Defaults to " + prometheusPort);
System.exit(1);
}
user = args[0];
}
if (args.length > 1) {
target = args[1];
}
if (args.length > 2) {
prometheusPort = Integer.parseInt(args[2]);
}

Thread mainThread = Thread.currentThread();

Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC client since JVM is shutting down");

sendRpcs.set(false);
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** client shut down");
}
});

// Adds a PrometheusHttpServer to convert OpenTelemetry metrics to Prometheus format and
// expose these via a HttpServer exporter to the SdkMeterProvider.
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
.registerMetricReader(
PrometheusHttpServer.builder().setPort(prometheusPort).build())
.build();

// Initialize OpenTelemetry SDK with MeterProvider configured with Prometeheus.
OpenTelemetrySdk openTelemetrySdk =
OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();

// Initialize gRPC OpenTelemetry.
// Following client metrics are enabled by default :
// 1. grpc.client.attempt.started
// 2. grpc.client.attempt.sent_total_compressed_message_size
// 3. grpc.client.attempt.rcvd_total_compressed_message_size
// 4. grpc.client.attempt.duration
// 5. grpc.client.call.duration
GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
.sdk(openTelemetrySdk)
.build();
// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

// Create a communication channel to the server, known as a Channel.
ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
.build();
OpenTelemetryClient client = new OpenTelemetryClient(channel);

try {
// Run RPCs every second.
while (sendRpcs.get()) {
client.greet(user);
// Sleep for a bit before sending the next RPC.
Thread.sleep(1000);
}
} finally {
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
// Shut down OpenTelemetry SDK.
openTelemetrySdk.close();
}
}
}
Loading

0 comments on commit d6ce8c5

Please sign in to comment.