Skip to content

Commit

Permalink
Add simple gRPC example
Browse files Browse the repository at this point in the history
  • Loading branch information
Hablapatabla committed May 8, 2021
1 parent 32f10c7 commit 2fce994
Show file tree
Hide file tree
Showing 11 changed files with 787 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ add_subdirectory(metrics_simple)
add_subdirectory(multithreaded)
add_subdirectory(multi_processor)
add_subdirectory(http)
add_subdirectory(grpc)
62 changes: 62 additions & 0 deletions examples/grpc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
find_package(gRPC)
find_package(Protobuf)

if(NOT Protobuf_FOUND)
message(WARNING "Aborting grpc example build, protobuf not found.")
elseif(NOT gRPC_FOUND)
message(WARNING "Aborting grpc example build, grpc not found.")
else()
set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>)
set(_GRPC_GRPCPP gRPC::grpc++)
set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)

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}/protos)

get_filename_component(
hw_proto ${CMAKE_CURRENT_SOURCE_DIR}/protos/messages.proto ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)

set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/messages.pb.cc")
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/messages.pb.h")
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/messages.grpc.pb.cc")
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/messages.grpc.pb.h")

add_custom_command(
OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}"
"${hw_grpc_hdrs}"
COMMAND
${_PROTOBUF_PROTOC} ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
--cpp_out "${CMAKE_CURRENT_BINARY_DIR}" -I "${hw_proto_path}"
--plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" "${hw_proto}"
DEPENDS "${hw_proto}")

include_directories("${CMAKE_CURRENT_BINARY_DIR}")

add_library(grpc_foo_library grpc_foo_lib/foo_split.cc)

add_library(hw_grpc_proto ${hw_grpc_srcs} ${hw_grpc_hdrs} ${hw_proto_srcs}
${hw_proto_hdrs})

target_link_libraries(hw_grpc_proto ${_REFLECTION} ${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})

foreach(_target client server)
add_executable(${_target} "${_target}.cc")
target_link_libraries(
${_target}
hw_grpc_proto
grpc_foo_library
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF}
${CMAKE_THREAD_LIBS_INIT}
${CORE_RUNTIME_LIBS}
opentelemetry_trace
opentelemetry_exporter_ostream_span)
endforeach()
endif()
122 changes: 122 additions & 0 deletions examples/grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# OpenTelemetry C++ Example

## gRPC

This is a simple example that demonstrates tracing a gRPC request from client to server. The example shows several aspects of tracing such as:

* Using the `TracerProvider`
* Implementing the TextMapCarrie
* Context injection/extraction
* Span Attributes
* Span Semantic Conventions
* Using the ostream exporte
* 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)'''

If you install these with a package manager like '''brew''' or '''apt''', you should not need to do extra work.

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 : f898b8bca93936f112a56b710bb888eb
span_id : 8354bf34f7de06a0
tracestate :
parent_span_id: 0000000000000000
start : 1620434679192923000
duration : 1571169
description :
span kind : Client
status : Ok
attributes :
rpc.grpc.status_code: 0
net.peer.port: 8800
rpc.system: grpc
net.peer.ip: 0.0.0.0
rpc.method: Greet
rpc.service: grpc-example.GreetService
events :
links :
}
```

* Server console

```console
{
name : splitfunc
trace_id : 0836f1469dac75fddb57df13bcf33420
span_id : 97b3f69fae5c0e15
tracestate :
parent_span_id: 3d5fcaccc5617153
start : 1620435268681957000
duration : 38026
description :
span kind : Internal
status : Unset
attributes :
events :
links :
}

{
name : splitlib
trace_id : 0836f1469dac75fddb57df13bcf33420
span_id : 3d5fcaccc5617153
tracestate :
parent_span_id: a6aa48220c511354
start : 1620435268681937000
duration : 99145
description :
span kind : Internal
status : Unset
attributes :
events :
links :
}

{
name : GreeterService/Greet
trace_id : 0836f1469dac75fddb57df13bcf33420
span_id : a6aa48220c511354
tracestate :
parent_span_id: 0000000000000000
start : 1620435268681893000
duration : 178531
description :
span kind : Serve
status : Ok
attributes :
rpc.grpc.status_code: 0
rpc.method: Greet
rpc.service: GreeterService
rpc.system: grpc
events :
links :
}
```
109 changes: 109 additions & 0 deletions examples/grpc/client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "grpc_foo_lib/foo_split.h"
#include "grpc_foo_lib/grpc_map_carrier.h"
#include "messages.grpc.pb.h"
#include "tracer_common.h"

#include <grpcpp/grpcpp.h>
#include <iostream>
#include <string>

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!");
// See tracer_common.h for this function
auto propagator = get_propagator();

opentelemetry::trace::StartSpanOptions options;
options.kind = opentelemetry::trace::SpanKind::kClient;
// Spans are only as useful as the information that you put in them.
// Even though this is a client service, it can still be useful to add
// as much data as possible. We add attributes for peer ip and port, both
// because it is required per OpenTelemetry's rpc semantic conventions,
// and because it could still be useful to a debugger in the future.
std::string span_name = "GreeterClient/Greet";
auto span = get_tracer("grpc-client")
->StartSpan(span_name,
{{"rpc.system", "grpc"},
{"rpc.service", "grpc-example.GreetService"},
{"rpc.method", "Greet"},
{"net.peer.ip", ip},
{"net.peer.port", port},
{"rpc.grpc.status_code", 0}},
options);
auto scope = get_tracer("grpc-client")->WithActiveSpan(span);

gRPCMapCarrier carrier;
carrier.gRPCMapCarrier::Set("http.header.stub", "temporarily-stubbed");

opentelemetry::context::Context ctx1 = opentelemetry::context::Context{"current-span", span};
opentelemetry::context::Context ctx2 = propagator->Extract(carrier, ctx1);
gRPCMapCarrier carrier2;
propagator->Inject(carrier2, ctx2);

Status status = stub_->Greet(&context, request, &response);
if (status.ok())
{
span->SetStatus(opentelemetry::trace::StatusCode::kOk);
// 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);
// 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();
constexpr uint16_t default_port = 8800;
uint16_t port;
if (argc > 1)
{
port = atoi(argv[1]);
}
else
{
port = default_port;
}
RunClient(port);
return 0;
}
Loading

0 comments on commit 2fce994

Please sign in to comment.