Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple instrumented gRPC example #729

Merged
merged 24 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2fce994
Add simple gRPC example
Hablapatabla May 8, 2021
6ebb25a
Restore errors introduced by format.sh. Explicitly include header fil…
Hablapatabla May 8, 2021
30d64d9
Merge branch 'main' into grpc-example
lalitb May 8, 2021
c3d5bfa
Implement Lalit's proposed changes in PR #729.
Hablapatabla May 8, 2021
baeb439
Merge branch 'open-telemetry:main' into grpc-example
Hablapatabla May 8, 2021
bb178cd
Implement client-server propagation, except it doesn't work. Commit t…
Hablapatabla May 10, 2021
bbfc584
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
Hablapatabla May 10, 2021
139273b
Saving files before refreshing line endings
Hablapatabla May 10, 2021
9496c3f
Normalize all the line endings
Hablapatabla May 10, 2021
1799f32
Format and comment out debugging output from trying to implement span…
Hablapatabla May 11, 2021
f3aabc0
Merge branch 'open-telemetry:main' into grpc-example
Hablapatabla May 11, 2021
00a707b
Merge branch 'grpc-example' of https://github.com/Hablapatabla/opente…
Hablapatabla May 11, 2021
25ff009
Fix code snippet markdown in README and try adding block around find_…
Hablapatabla May 11, 2021
d8569a2
Revert CMakeLists so it works again after bad commit (oof).
Hablapatabla May 11, 2021
2de0ea2
fix context propagation
lalitb Jun 2, 2021
464d52b
run only with WITH_OTLP
lalitb Jun 2, 2021
44dd750
revert
lalitb Jun 2, 2021
bc4311f
another revert try
lalitb Jun 2, 2021
2fb974a
fix readme
lalitb Jun 2, 2021
fadde9e
fix readme
lalitb Jun 2, 2021
0e3c346
format, and comments
lalitb Jun 2, 2021
bfaf2ce
Merge branch 'main' into grpc-example
lalitb Jun 2, 2021
8eb1dc8
Merge branch 'grpc-example' of github.com:Hablapatabla/opentelemetry-…
lalitb Jun 2, 2021
9404cf9
Merge branch 'main' into grpc-example
lalitb Jun 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
if(WITH_OTLP)
add_subdirectory(otlp)
add_subdirectory(grpc)
lalitb marked this conversation as resolved.
Show resolved Hide resolved
endif()
if(WITH_JAEGER)
add_subdirectory(jaeger)
Expand Down
48 changes: 48 additions & 0 deletions examples/grpc/CMakeLists.txt
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()
99 changes: 99 additions & 0 deletions examples/grpc/README.md
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 :
}
```
109 changes: 109 additions & 0 deletions examples/grpc/client.cpp
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;
}
15 changes: 15 additions & 0 deletions examples/grpc/protos/messages.proto
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;
}
114 changes: 114 additions & 0 deletions examples/grpc/server.cpp
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;
}
Loading