Skip to content

Commit 8e42fbe

Browse files
authored
update Setup Basic OpenTelemetry Plugin in gRPC C++ codelab to README.md (#31)
1 parent fb9c707 commit 8e42fbe

File tree

1 file changed

+293
-12
lines changed

1 file changed

+293
-12
lines changed
Lines changed: 293 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,299 @@
1-
# Setup Basic OpenTelemetry Plugin In gRPC C++
1+
# Setup Basic OpenTelemetry Plugin in gRPC C++
22

3-
Get hands-on with gRPC's OpenTelemetry plugin for C++ in this interactive codelab! <!-- TODO(arvindbr8): Insert link once codelab is published. -->
3+
## Before you Begin
44

5+
Get hands-on with gRPC's OpenTelemetry plugin for C++ in this interactive
6+
codelab! <!-- TODO(arvindbr8): Insert link once codelab is published. -->
57

6-
Designed for developers already familiar with gRPC and wanting to learn how to instrument their gRPC usage with OpenTelemetry.
8+
### **Prerequisites**
79

8-
#### You'll learn how to:
9-
- setup gRPC's OpenTelemetry plugin in C++.
10-
- setup Prometheus exporter with OpenTelemetry.
11-
- explore collected metrics using Prometheus.
10+
* Basic understanding of gRPC and gRPC C++
1211

13-
## How to use this directory
12+
* Install the following prerequisites
1413

15-
- [start_here](start_here/) directory serves as a starting point for the
16-
codelab.
17-
- [completed](completed/) directory showcases the finished code, giving you a
18-
peek of how the final implementation should look like.
14+
```console
15+
$ sudo apt-get update -y
16+
$ sudo apt-get upgrade -y
17+
$ sudo apt-get install -y git curl build-essential clang
18+
```
19+
20+
* This codelab will have you build examples using bazel. Install bazel using
21+
bazelisk. The latest version can found at https://github.com/bazelbuild/bazelisk/releases.
22+
* A simple way to set it up is to install it as the `bazel` binary in your `PATH`.
23+
24+
```console
25+
$ cp bazelisk-linux-amd64 /usr/local/bin/bazel
26+
```
27+
28+
* Alternatively, you can also use CMake. Instructions for using CMake can be
29+
found [here](https://github.com/grpc/grpc/tree/master/src/cpp\#cmake).
30+
31+
### **What you’ll learn**
32+
33+
* How to setup OpenTelemetry Plugin for existing gRPC C++ application
34+
* Running a local Prometheus instance
35+
* Exporting metrics to Prometheus
36+
* View metrics from Prometheus dashboard
37+
38+
### **What you’ll need**
39+
40+
* A computer with internet connection
41+
42+
<!-- TODO(yashkt/arvindbright) : Add some additional boilerplate stuff over here if needed. -->
43+
44+
## Building the example
45+
46+
```console
47+
$ git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
48+
$ cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
49+
$ bazel build start_here/…
50+
```
51+
52+
> [!NOTE]
53+
> Building gRPC might take a few minutes. We can move on to instrumenting our gRPC example with the gRPC OpenTelemetry plugin in the meantime.
54+
55+
## Instrumenting applications with gRPC OpenTelemetry Plugin
56+
57+
The example for this codelab is in the grpc/grpc github repo at [`grpc-codelabs/codelabs/grpc-cpp-opentelemetry/`](completed/).
58+
59+
The client and server uses a simple gRPC HelloWorld example that we will instrument with the gRPC OpenTelemetry plugin.
60+
61+
Open `codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc` with your favorite editor, and transform `main()` to look like this \-
62+
63+
```cpp
64+
int main(int argc, char** argv) {
65+
absl::ParseCommandLine(argc, argv);
66+
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
67+
// exporter.
68+
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
69+
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
70+
auto prometheus_exporter =
71+
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
72+
auto meter_provider =
73+
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
74+
// The default histogram boundaries are not granular enough for RPCs. Override
75+
// the "grpc.client.attempt.duration" view as recommended by
76+
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
77+
AddLatencyView(meter_provider.get(), "grpc.client.attempt.duration", "s");
78+
meter_provider->AddMetricReader(std::move(prometheus_exporter));
79+
auto status = grpc::OpenTelemetryPluginBuilder()
80+
.SetMeterProvider(std::move(meter_provider))
81+
.BuildAndRegisterGlobal();
82+
if (!status.ok()) {
83+
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
84+
<< status.ToString() << std::endl;
85+
return static_cast<int>(status.code());
86+
}
87+
88+
89+
// Continuously send RPCs.
90+
RunClient(absl::GetFlag(FLAGS_target));
91+
92+
93+
return 0;
94+
}
95+
```
96+
97+
> [!NOTE]
98+
> How a Prometheus Exporter is being set up on the OpenTelemetry Meter Provider.
99+
> (There are other ways to export the metrics as well. This codelab chooses the
100+
> prometheus exporter.) This MeterProvider is provided to gRPC’s OpenTelemetry
101+
> plugin as configuration. Once the OpenTelemetry plugin is registered globally
102+
> all gRPC clients and servers will be instrumented with OpenTelemetry.
103+
104+
Similarly, let’s add the OpenTelemetry plugin to the server as well. Open `codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc` and transform main to look like this
105+
106+
```cpp
107+
int main(int argc, char** argv) {
108+
absl::ParseCommandLine(argc, argv);
109+
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
110+
// exporter.
111+
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
112+
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
113+
auto prometheus_exporter =
114+
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
115+
auto meter_provider =
116+
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
117+
// The default histogram boundaries are not granular enough for RPCs. Override
118+
// the "grpc.server.call.duration" view as recommended by
119+
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
120+
AddLatencyView(meter_provider.get(), "grpc.server.call.duration", "s");
121+
meter_provider->AddMetricReader(std::move(prometheus_exporter));
122+
auto status = grpc::OpenTelemetryPluginBuilder()
123+
.SetMeterProvider(std::move(meter_provider))
124+
.BuildAndRegisterGlobal();
125+
if (!status.ok()) {
126+
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
127+
<< status.ToString() << std::endl;
128+
return static_cast<int>(status.code());
129+
}
130+
RunServer(absl::GetFlag(FLAGS_port));
131+
return 0;
132+
}
133+
```
134+
135+
> [!NOTE]
136+
> The required header files and build dependencies have already been added for convenience.
137+
138+
```cpp
139+
#include "opentelemetry/exporters/prometheus/exporter_factory.h"
140+
#include "opentelemetry/exporters/prometheus/exporter_options.h"
141+
#include "opentelemetry/sdk/metrics/meter_provider.h"
142+
143+
#include <grpcpp/ext/otel_plugin.h>
144+
```
145+
146+
Build dependencies added in `BUILD` file \-
147+
148+
```bazel
149+
cc_binary(
150+
name = "greeter_callback_client",
151+
srcs = ["greeter_callback_client.cc"],
152+
defines = ["BAZEL_BUILD"],
153+
deps = [
154+
"//util:util",
155+
"@com_github_grpc_grpc//:grpc++",
156+
"@com_github_grpc_grpc//:grpcpp_otel_plugin",
157+
"@com_google_absl//absl/flags:flag",
158+
"@com_google_absl//absl/flags:parse",
159+
"@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
160+
"@io_opentelemetry_cpp//sdk/src/metrics",
161+
],
162+
)
163+
```
164+
165+
# Running the example and viewing metrics
166+
167+
To run the server, run \-
168+
169+
```console
170+
$ bazel run start_here:greeter_callback_server
171+
```
172+
173+
With a successful setup, you will see the following output for the server \-
174+
175+
```console
176+
Server listening on 0.0.0.0:50051
177+
```
178+
179+
While, the server is running, on another terminal, run the client \-
180+
181+
```console
182+
$ bazel run start_here:greeter_callback_client
183+
```
184+
185+
A successful run will look like \-
186+
187+
```console
188+
Greeter received: Hello world
189+
Greeter received: Hello world
190+
Greeter received: Hello world
191+
Greeter received: Hello world
192+
Greeter received: Hello world
193+
Greeter received: Hello world
194+
Greeter received: Hello world
195+
Greeter received: Hello world
196+
Greeter received: Hello world
197+
Greeter received: Hello world
198+
Greeter received: Hello world
199+
```
200+
201+
Since we have set-up the gRPC OpenTelemetry plugin to export metrics using Prometheus. Those metrics will be available on localhost:9464 for server and localhost:9465 for client.
202+
203+
To see client metrics \-
204+
205+
```console
206+
$ curl localhost:9465/metrics
207+
```
208+
209+
The result would be of the form \-
210+
211+
```console
212+
# HELP exposer_transferred_bytes_total Transferred bytes to metrics services
213+
# TYPE exposer_transferred_bytes_total counter
214+
exposer_transferred_bytes_total 0
215+
# HELP exposer_scrapes_total Number of times metrics were scraped
216+
# TYPE exposer_scrapes_total counter
217+
exposer_scrapes_total 0
218+
# HELP exposer_request_latencies Latencies of serving scrape requests, in microseconds
219+
# TYPE exposer_request_latencies summary
220+
exposer_request_latencies_count 0
221+
exposer_request_latencies_sum 0
222+
exposer_request_latencies{quantile="0.5"} Nan
223+
exposer_request_latencies{quantile="0.9"} Nan
224+
exposer_request_latencies{quantile="0.99"} Nan
225+
# HELP target Target metadata
226+
# TYPE target gauge
227+
target_info{otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",service_name="unknown_service",telemetry_sdk_version="1.13.0",telemetry_sdk_name="opentelemetry",telemetry_sdk_language="cpp"} 1 1721958543107
228+
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
229+
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
230+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 96 1721958543107
231+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 1248 1721958543107
232+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="0"} 0 1721958543107
233+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="5"} 0 1721958543107
234+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="10"} 0 1721958543107
235+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="25"} 96 1721958543107
236+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="50"} 96 1721958543107
237+
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="75"} 96 1721958543107
238+
```
239+
240+
Similarly, for the server side metrics \-
241+
242+
```console
243+
$ curl localhost:9464/metrics
244+
```
245+
246+
# Viewing metrics on Prometheus
247+
248+
Here, we will setup a prometheus instance that will scrape our gRPC example client and server that are exporting metrics using prometheus.
249+
250+
[Download the latest release](https://prometheus.io/download) of Prometheus for your platform, then extract and run it:
251+
252+
```console
253+
$ tar xvfz prometheus-*.tar.gz
254+
$ cd prometheus-*
255+
```
256+
257+
Create a prometheus configuration file with the following \-
258+
259+
```console
260+
$ cat > grpc_otel_cpp_prometheus.yml <<EOF
261+
scrape_configs:
262+
- job_name: "prometheus"
263+
scrape_interval: 5s
264+
static_configs:
265+
- targets: ["localhost:9090"]
266+
- job_name: "grpc-otel-cpp"
267+
scrape_interval: 5s
268+
static_configs:
269+
- targets: ["localhost:9464", "localhost:9465"]
270+
EOF
271+
```
272+
273+
Start prometheus with the new configuration \-
274+
275+
```console
276+
$ ./prometheus --config.file=grpc_otel_cpp_prometheus.yml
277+
```
278+
279+
This will configure the metrics from the client and server codelab processes to be scraped every 5 seconds.
280+
281+
Go to [http://localhost:9090/graph](http://localhost:9090/graph) to view the metrics. For example, the query \-
282+
283+
```
284+
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
285+
```
286+
287+
will show a graph with the median attempt latency using 1minute as the time window for the quantile calculation.
288+
289+
Rate of queries \-
290+
291+
```
292+
increase(grpc_client_attempt_duration_seconds_bucket[1m])
293+
```
294+
295+
# (Optional) Exercise for User
296+
297+
In the prometheus dashboards, you’ll notice that the QPS is low. See if you can identify some suspicious code in the example that is limiting the QPS.
298+
299+
For the enthusiastic, the client code limits itself to only have a single pending RPC at a given moment. This can be modified so that the client sends more RPCs without waiting for the previous ones to complete. (The solution for this has not been provided.)

0 commit comments

Comments
 (0)