-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simple instrumented gRPC example (#729)
- Loading branch information
1 parent
1c70f0e
commit 0f6199f
Showing
7 changed files
with
477 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
if(WITH_OTLP) | ||
add_subdirectory(otlp) | ||
add_subdirectory(grpc) | ||
endif() | ||
if(WITH_JAEGER) | ||
add_subdirectory(jaeger) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Proto file | ||
get_filename_component(proto_file "./protos/messages.proto" ABSOLUTE) | ||
get_filename_component(proto_file_path "${proto_file}" PATH) | ||
|
||
message("PATH:${proto_file_path}:${proto_file}") | ||
# Generated sources | ||
set(example_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/messages.pb.cc") | ||
set(example_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/messages.pb.h") | ||
set(example_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/messages.grpc.pb.cc") | ||
set(example_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/messages.grpc.pb.h") | ||
|
||
add_custom_command( | ||
OUTPUT "${example_proto_srcs}" "${example_proto_hdrs}" "${example_grpc_srcs}" | ||
"${example_grpc_hdrs}" | ||
COMMAND | ||
${PROTOBUF_PROTOC_EXECUTABLE} ARGS "--grpc_out=${CMAKE_CURRENT_BINARY_DIR}" | ||
"--cpp_out=${CMAKE_CURRENT_BINARY_DIR}" "--proto_path=${proto_file_path}" | ||
--plugin=protoc-gen-grpc="${gRPC_CPP_PLUGIN_EXECUTABLE}" "${proto_file}") | ||
# DEPENDS "${proto_file}") | ||
|
||
# hw_grpc_proto | ||
add_library(example_grpc_proto ${example_grpc_srcs} ${example_grpc_hdrs} | ||
${example_proto_srcs} ${example_proto_hdrs}) | ||
|
||
include_directories( | ||
${CMAKE_SOURCE_DIR}/exporters/ostream/include ${CMAKE_SOURCE_DIR}/ext/include | ||
${CMAKE_SOURCE_DIR}/api/include/ ${CMAKE_SOURCE_DIR/}) | ||
|
||
include_directories(${CMAKE_CURRENT_BINARY_DIR}) | ||
|
||
if(TARGET protobuf::libprotobuf) | ||
target_link_libraries(example_grpc_proto gRPC::grpc++ protobuf::libprotobuf) | ||
else() | ||
target_include_directories(example_grpc_proto ${Protobuf_INCLUDE_DIRS}) | ||
target_link_libraries(example_grpc_proto ${Protobuf_LIBRARIES}) | ||
endif() | ||
|
||
foreach(_target client server) | ||
add_executable(${_target} "${_target}.cpp") | ||
target_link_libraries( | ||
${_target} | ||
example_grpc_proto | ||
protobuf::libprotobuf | ||
gRPC::grpc++ | ||
gRPC::grpc++_reflection | ||
opentelemetry_trace | ||
opentelemetry_exporter_ostream_span) | ||
endforeach() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# OpenTelemetry C++ Example | ||
|
||
## gRPC | ||
|
||
This is a simple example that demonstrates tracing a gRPC request from client to server. There is an experimental directory in this example - the code within has been commented out to prevent any conflicts. The example shows several aspects of tracing such as: | ||
|
||
* Using the `TracerProvider` | ||
* Implementing the TextMapCarrier | ||
* Context injection/extraction | ||
* Span Attributes | ||
* Span Semantic Conventions | ||
* Using the ostream exporter | ||
* Nested spans | ||
* W3c Trace Context Propagation (Very soon!) | ||
|
||
### Running the example | ||
|
||
1. The example uses gRPC C++ as well as Google's protocol buffers. Make sure you have installed both | ||
of these packages on your system, in such a way that CMake would know how to find them with this command: | ||
|
||
``find_package(gRPC)`` | ||
|
||
2. Build and Deploy the opentelementry-cpp as described in [INSTALL.md](../../INSTALL.md). Building the project will build all of the examples | ||
and create new folders containing their executables within the 'build' directory NOT the 'examples' directory. | ||
|
||
3. Start the server from your `build/examples/grpc` directory. Both the server and client are configured to use 8800 as the default port, | ||
but if you would like to use another port, you can specify that as an argument. | ||
|
||
```console | ||
$ ./server [port_num] | ||
Server listening on port: 0.0.0.0:8800 | ||
``` | ||
|
||
4. In a separate terminal window, run the client to make a single request: | ||
|
||
```console | ||
$ ./client [port_num] | ||
... | ||
``` | ||
|
||
5. You should see console exporter output for both the client and server sessions. | ||
* Client console | ||
|
||
```console | ||
{ | ||
name : GreeterClient/Greet | ||
trace_id : f5d16f8399be0d2c6b39d992634ffdbb | ||
span_id : 9c79a2dd744d7d2d | ||
tracestate : | ||
parent_span_id: 0000000000000000 | ||
start : 1622603339918985700 | ||
duration : 4960500 | ||
description : | ||
span kind : Client | ||
status : Ok | ||
attributes : | ||
rpc.grpc.status_code: 0 | ||
net.peer.port: 8080 | ||
net.peer.ip: 0.0.0.0 | ||
rpc.method: Greet | ||
rpc.service: grpc-example.GreetService | ||
rpc.system: grpc | ||
events : | ||
} | ||
``` | ||
|
||
* Server console | ||
|
||
```console | ||
{ | ||
name : GreeterService/Greet | ||
trace_id : f5d16f8399be0d2c6b39d992634ffdbb | ||
span_id : 1e8a7d2d46e08573 | ||
tracestate : | ||
parent_span_id: 9c79a2dd744d7d2d | ||
start : 1622603339923163800 | ||
duration : 76400 | ||
description : | ||
span kind : Server | ||
status : Ok | ||
attributes : | ||
rpc.grpc.status_code: 0 | ||
rpc.method: Greet | ||
rpc.service: GreeterService | ||
rpc.system: grpc | ||
events : | ||
{ | ||
name : Processing client attributes | ||
timestamp : 1622603339923180800 | ||
attributes : | ||
} | ||
{ | ||
name : Response sent to client | ||
timestamp : 1622603339923233700 | ||
attributes : | ||
} | ||
links : | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
#include "tracer_common.h" | ||
#include <iostream> | ||
#include <memory> | ||
#include <string> | ||
|
||
#include <grpcpp/grpcpp.h> | ||
|
||
#include "messages.grpc.pb.h" | ||
|
||
using grpc::Channel; | ||
using grpc::ClientContext; | ||
using grpc::ClientReader; | ||
using grpc::Status; | ||
|
||
using grpc_example::Greeter; | ||
using grpc_example::GreetRequest; | ||
using grpc_example::GreetResponse; | ||
|
||
|
||
namespace | ||
{ | ||
|
||
class GreeterClient | ||
{ | ||
public: | ||
GreeterClient(std::shared_ptr<Channel> channel) : stub_(Greeter::NewStub(channel)) {} | ||
|
||
std::string Greet(std::string ip, uint16_t port) | ||
{ | ||
// Build gRPC Context objects and protobuf message containers | ||
GreetRequest request; | ||
GreetResponse response; | ||
ClientContext context; | ||
request.set_request("Nice to meet you!"); | ||
|
||
opentelemetry::trace::StartSpanOptions options; | ||
options.kind = opentelemetry::trace::SpanKind::kClient; | ||
|
||
std::string span_name = "GreeterClient/Greet"; | ||
auto span = get_tracer("grpc")->StartSpan(span_name, | ||
{{"rpc.system", "grpc"}, | ||
{"rpc.service", "grpc-example.GreetService"}, | ||
{"rpc.method", "Greet"}, | ||
{"net.peer.ip", ip}, | ||
{"net.peer.port", port}}, | ||
options); | ||
|
||
auto scope = get_tracer("grpc-client")->WithActiveSpan(span); | ||
|
||
// inject current context to grpc metadata | ||
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); | ||
GrpcClientCarrier carrier(&context); | ||
auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); | ||
prop->Inject(carrier, current_ctx); | ||
|
||
// Send request to server | ||
Status status = stub_->Greet(&context, request, &response); | ||
if (status.ok()) | ||
{ | ||
span->SetStatus(opentelemetry::trace::StatusCode::kOk); | ||
span->SetAttribute("rpc.grpc.status_code", status.error_code()); | ||
// Make sure to end your spans! | ||
span->End(); | ||
return response.response(); | ||
} | ||
else | ||
{ | ||
std::cout << status.error_code() << ": " << status.error_message() << std::endl; | ||
span->SetStatus(opentelemetry::trace::StatusCode::kError); | ||
span->SetAttribute("rpc.grpc.status_code", status.error_code()); | ||
// Make sure to end your spans! | ||
span->End(); | ||
return "RPC failed"; | ||
} | ||
} | ||
|
||
private: | ||
std::unique_ptr<Greeter::Stub> stub_; | ||
}; // GreeterClient class | ||
|
||
void RunClient(uint16_t port) | ||
{ | ||
GreeterClient greeter( | ||
grpc::CreateChannel("0.0.0.0:" + std::to_string(port), grpc::InsecureChannelCredentials())); | ||
std::string response = greeter.Greet("0.0.0.0", port); | ||
std::cout << "grpc_server says: " << response << std::endl; | ||
} | ||
} // namespace | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
initTracer(); | ||
// set global propagator | ||
opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator( | ||
nostd::shared_ptr<opentelemetry::context::propagation::TextMapPropagator>( | ||
new opentelemetry::trace::propagation::HttpTraceContext())); | ||
constexpr uint16_t default_port = 8800; | ||
uint16_t port; | ||
if (argc > 1) | ||
{ | ||
port = atoi(argv[1]); | ||
} | ||
else | ||
{ | ||
port = default_port; | ||
} | ||
RunClient(port); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
syntax = "proto3"; | ||
|
||
package grpc_example; | ||
|
||
service Greeter { | ||
rpc Greet(GreetRequest) returns (GreetResponse) {} | ||
} | ||
|
||
message GreetRequest { | ||
string request = 1; | ||
} | ||
|
||
message GreetResponse { | ||
string response = 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#include "messages.grpc.pb.h" | ||
#include "tracer_common.h" | ||
#include "opentelemetry/trace/span_context_kv_iterable_view.h" | ||
|
||
#include <grpcpp/grpcpp.h> | ||
#include <grpcpp/server.h> | ||
#include <grpcpp/server_builder.h> | ||
#include <grpcpp/server_context.h> | ||
|
||
#include <chrono> | ||
#include <fstream> | ||
#include <sstream> | ||
#include <string> | ||
#include <thread> | ||
#include <map> | ||
|
||
using grpc::Server; | ||
using grpc::ServerBuilder; | ||
using grpc::ServerContext; | ||
using grpc::ServerWriter; | ||
using grpc::Status; | ||
|
||
using grpc_example::Greeter; | ||
using grpc_example::GreetRequest; | ||
using grpc_example::GreetResponse; | ||
|
||
using Span = opentelemetry::trace::Span; | ||
using SpanContext = opentelemetry::trace::SpanContext; | ||
|
||
namespace | ||
{ | ||
class GreeterServer final : public Greeter::Service | ||
{ | ||
public: | ||
Status Greet(ServerContext *context, | ||
const GreetRequest *request, | ||
GreetResponse *response) override | ||
{ | ||
for( auto elem: context->client_metadata()) { | ||
std::cout << "ELEM: " << elem.first << " " << elem.second << "\n"; | ||
} | ||
|
||
// Create a SpanOptions object and set the kind to Server to inform OpenTel. | ||
opentelemetry::trace::StartSpanOptions options; | ||
options.kind = opentelemetry::trace::SpanKind::kServer; | ||
|
||
// extract context from grpc metadata | ||
GrpcServerCarrier carrier(context); | ||
|
||
auto prop = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator(); | ||
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent(); | ||
auto new_context = prop->Extract(carrier, current_ctx); | ||
options.parent = opentelemetry::trace::propagation::GetSpan(new_context)->GetContext(); | ||
|
||
std::string span_name = "GreeterService/Greet"; | ||
auto span = get_tracer("grpc") | ||
->StartSpan(span_name, | ||
{{"rpc.system", "grpc"}, | ||
{"rpc.service", "GreeterService"}, | ||
{"rpc.method", "Greet"}, | ||
{"rpc.grpc.status_code", 0}}, | ||
options); | ||
auto scope = get_tracer("grpc")->WithActiveSpan(span); | ||
|
||
// Fetch and parse whatever HTTP headers we can from the gRPC request. | ||
span->AddEvent("Processing client attributes"); | ||
|
||
std::string req = request->request(); | ||
std::cout << std::endl << "grpc_client says: " << req << std::endl; | ||
std::string message = "The pleasure is mine."; | ||
// Send response to client | ||
response->set_response(message); | ||
span->AddEvent("Response sent to client"); | ||
|
||
span->SetStatus(opentelemetry::trace::StatusCode::kOk); | ||
// Make sure to end your spans! | ||
span->End(); | ||
return Status::OK; | ||
} | ||
}; // GreeterServer class | ||
|
||
void RunServer(uint16_t port) | ||
{ | ||
std::string address("0.0.0.0:" + std::to_string(port)); | ||
GreeterServer service; | ||
ServerBuilder builder; | ||
|
||
builder.RegisterService(&service); | ||
builder.AddListeningPort(address, grpc::InsecureServerCredentials()); | ||
|
||
std::unique_ptr<Server> server(builder.BuildAndStart()); | ||
std::cout << "Server listening on port: " << address << std::endl; | ||
server->Wait(); | ||
server->Shutdown(); | ||
} | ||
} // namespace | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
initTracer(); | ||
constexpr uint16_t default_port = 8800; | ||
uint16_t port; | ||
if (argc > 1) | ||
{ | ||
port = atoi(argv[1]); | ||
} | ||
else | ||
{ | ||
port = default_port; | ||
} | ||
|
||
RunServer(port); | ||
return 0; | ||
} |
Oops, something went wrong.