|
1 |
| -# Setup Basic OpenTelemetry Plugin In gRPC C++ |
| 1 | +# Setup Basic OpenTelemetry Plugin in gRPC C++ |
2 | 2 |
|
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 |
4 | 4 |
|
| 5 | +Get hands-on with gRPC's OpenTelemetry plugin for C++ in this interactive |
| 6 | +codelab! <!-- TODO(arvindbr8): Insert link once codelab is published. --> |
5 | 7 |
|
6 |
| -Designed for developers already familiar with gRPC and wanting to learn how to instrument their gRPC usage with OpenTelemetry. |
| 8 | +### **Prerequisites** |
7 | 9 |
|
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++ |
12 | 11 |
|
13 |
| -## How to use this directory |
| 12 | +* Install the following prerequisites |
14 | 13 |
|
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