[OTel] Generate pkg-config file for grpcpp_otel_plugin (#36686)
Public Changes - * Add a pkgconfig installer for `grpcpp_otel_plugin` Example Changes - * Add example of how to use the pkgconfig for `grpcpp_otel_plugin` with the existing OpenTelemetry example. * Add another OpenTelemetry example that uses OTel's OStream exporter. This makes it easier to test the pkgconfig file for `grpcpp_otel_plugin` since the OStream exporter does not require any additional dependencies, as opposed to the Prometheus exporter. Test changes - * Modify `run_distrib_test_cmake_pkgconfig.sh` test to install opentelemetry and build the example with the OStream exporter. Closes #36686 PiperOrigin-RevId: 636965475
This commit is contained in:
parent
3df8e0787f
commit
ac303a09f6
|
|
@ -37015,3 +37015,14 @@ generate_pkgconfig(
|
||||||
"-lgrpc++_unsecure"
|
"-lgrpc++_unsecure"
|
||||||
"-laddress_sorting -lupb_message_lib -lupb_mem_lib -lupb_base_lib -lutf8_range_lib"
|
"-laddress_sorting -lupb_message_lib -lupb_mem_lib -lupb_base_lib -lutf8_range_lib"
|
||||||
"grpc++_unsecure.pc")
|
"grpc++_unsecure.pc")
|
||||||
|
|
||||||
|
# grpcpp_otel_plugin .pc file
|
||||||
|
generate_pkgconfig(
|
||||||
|
"gRPC++ OpenTelemetry Plugin"
|
||||||
|
"OpenTelemetry Plugin for gRPC C++"
|
||||||
|
"${gRPC_CPP_VERSION}"
|
||||||
|
"absl_algorithm_container absl_any_invocable absl_base absl_bind_front absl_check absl_cleanup absl_config absl_cord absl_core_headers absl_flags absl_flags_marshalling absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_inlined_vector absl_log absl_log_globals absl_log_severity absl_memory absl_no_destructor absl_optional absl_random_bit_gen_ref absl_random_distributions absl_random_random absl_span absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant gpr grpc grpc++ opentelemetry_api"
|
||||||
|
"libcares openssl re2 zlib"
|
||||||
|
"-lgrpcpp_otel_plugin"
|
||||||
|
"-laddress_sorting -lupb_textformat_lib -lupb_json_lib -lupb_message_lib -lupb_mem_lib -lupb_base_lib -lutf8_range_lib"
|
||||||
|
"grpcpp_otel_plugin.pc")
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,19 @@
|
||||||
|
|
||||||
licenses(["notice"])
|
licenses(["notice"])
|
||||||
|
|
||||||
|
package(
|
||||||
|
default_visibility = ["//examples/cpp/otel:__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "util",
|
name = "util",
|
||||||
srcs = ["util.cc"],
|
srcs = ["util.cc"],
|
||||||
hdrs = ["util.h"],
|
hdrs = ["util.h"],
|
||||||
|
defines = ["BAZEL_BUILD"],
|
||||||
deps = [
|
deps = [
|
||||||
"//:grpc++",
|
"//:grpc++",
|
||||||
|
"//:grpc++_reflection",
|
||||||
|
"//examples/protos:helloworld_cc_grpc",
|
||||||
"@io_opentelemetry_cpp//sdk/src/metrics",
|
"@io_opentelemetry_cpp//sdk/src/metrics",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -32,7 +39,6 @@ cc_binary(
|
||||||
"util",
|
"util",
|
||||||
"//:grpc++",
|
"//:grpc++",
|
||||||
"//:grpcpp_otel_plugin",
|
"//:grpcpp_otel_plugin",
|
||||||
"//examples/protos:helloworld_cc_grpc",
|
|
||||||
"@com_google_absl//absl/flags:flag",
|
"@com_google_absl//absl/flags:flag",
|
||||||
"@com_google_absl//absl/flags:parse",
|
"@com_google_absl//absl/flags:parse",
|
||||||
"@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
|
"@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
|
||||||
|
|
@ -47,9 +53,7 @@ cc_binary(
|
||||||
deps = [
|
deps = [
|
||||||
"util",
|
"util",
|
||||||
"//:grpc++",
|
"//:grpc++",
|
||||||
"//:grpc++_reflection",
|
|
||||||
"//:grpcpp_otel_plugin",
|
"//:grpcpp_otel_plugin",
|
||||||
"//examples/protos:helloworld_cc_grpc",
|
|
||||||
"@com_google_absl//absl/flags:flag",
|
"@com_google_absl//absl/flags:flag",
|
||||||
"@com_google_absl//absl/flags:parse",
|
"@com_google_absl//absl/flags:parse",
|
||||||
"@com_google_absl//absl/strings:str_format",
|
"@com_google_absl//absl/strings:str_format",
|
||||||
|
|
|
||||||
|
|
@ -67,22 +67,21 @@ target_link_libraries(hw_grpc_proto
|
||||||
add_library(util
|
add_library(util
|
||||||
"util.cc")
|
"util.cc")
|
||||||
target_link_libraries(util
|
target_link_libraries(util
|
||||||
|
hw_grpc_proto
|
||||||
opentelemetry-cpp::metrics
|
opentelemetry-cpp::metrics
|
||||||
${_GRPC_GRPCPP})
|
${_GRPC_GRPCPP}
|
||||||
|
${_REFLECTION}
|
||||||
|
${_PROTOBUF_LIBPROTOBUF})
|
||||||
|
|
||||||
# Targets greeter_callback_(client|server)
|
# Targets greeter_callback_(client|server)
|
||||||
foreach(_target
|
foreach(_target
|
||||||
greeter_callback_client greeter_callback_server)
|
greeter_callback_client greeter_callback_server)
|
||||||
add_executable(${_target} "${_target}.cc")
|
add_executable(${_target} "${_target}.cc")
|
||||||
target_link_libraries(${_target}
|
target_link_libraries(${_target}
|
||||||
hw_grpc_proto
|
|
||||||
absl::flags
|
absl::flags
|
||||||
absl::flags_parse
|
absl::flags_parse
|
||||||
opentelemetry-cpp::metrics
|
opentelemetry-cpp::metrics
|
||||||
opentelemetry-cpp::prometheus_exporter
|
opentelemetry-cpp::prometheus_exporter
|
||||||
${_REFLECTION}
|
|
||||||
${_GRPC_GRPCPP}
|
|
||||||
gRPC::grpcpp_otel_plugin
|
gRPC::grpcpp_otel_plugin
|
||||||
util
|
util)
|
||||||
${_PROTOBUF_LIBPROTOBUF})
|
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
#
|
||||||
|
# Copyright 2015 gRPC authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
# TODO(jtattermusch): Remove the hack to workaround protobuf bug. See https://github.com/protocolbuffers/protobuf/issues/12439
|
||||||
|
# Hack: protobuf currently doesn't declare it's absl dependencies when protobuf.pc pkgconfig file is used.
|
||||||
|
PROTOBUF_ABSL_DEPS = absl_absl_check absl_absl_log absl_algorithm absl_base absl_bind_front absl_bits absl_btree absl_cleanup absl_cord absl_core_headers absl_debugging absl_die_if_null absl_dynamic_annotations absl_flags absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_layout absl_log_initialize absl_log_severity absl_memory absl_node_hash_map absl_node_hash_set absl_optional absl_span absl_status absl_statusor absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant
|
||||||
|
# TODO(jtattermusch): Remove the hack to workaround protobuf/utf8_range bug. See https://github.com/protocolbuffers/utf8_range/issues/20
|
||||||
|
# Hack: utf8_range (which is protobuf's dependency) currently doesn't have a pkgconfig file, so we need to explicitly
|
||||||
|
# tweak the list of libraries to link against to fix the build.
|
||||||
|
PROTOBUF_UTF8_RANGE_LINK_LIBS = -lutf8_validity
|
||||||
|
OPENTELEMETRY_LINK_LIBS = -lopentelemetry_metrics -lopentelemetry_exporter_prometheus -lopentelemetry_common -lopentelemetry_resources -lprometheus-cpp-pull -lprometheus-cpp-core
|
||||||
|
|
||||||
|
HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
|
||||||
|
SYSTEM ?= $(HOST_SYSTEM)
|
||||||
|
CXX = g++
|
||||||
|
CPPFLAGS += `pkg-config --cflags protobuf grpc absl_flags absl_flags_parse`
|
||||||
|
ifeq ($(SYSTEM),Darwin)
|
||||||
|
LDFLAGS += -L/usr/local/lib `pkg-config --libs --static protobuf grpc++ grpcpp_otel_plugin absl_flags absl_flags_parse $(PROTOBUF_ABSL_DEPS)` \
|
||||||
|
$(PROTOBUF_UTF8_RANGE_LINK_LIBS) \
|
||||||
|
$(OPENTELEMETRY_LINK_LIBS) \
|
||||||
|
-pthread \
|
||||||
|
-lgrpc++_reflection \
|
||||||
|
-ldl
|
||||||
|
else
|
||||||
|
LDFLAGS += -L/usr/local/lib `pkg-config --libs --static protobuf grpc++ grpcpp_otel_plugin absl_flags absl_flags_parse $(PROTOBUF_ABSL_DEPS)` \
|
||||||
|
$(PROTOBUF_UTF8_RANGE_LINK_LIBS) \
|
||||||
|
$(OPENTELEMETRY_LINK_LIBS) \
|
||||||
|
-pthread \
|
||||||
|
-Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed \
|
||||||
|
-ldl
|
||||||
|
endif
|
||||||
|
PROTOC = protoc
|
||||||
|
GRPC_CPP_PLUGIN = grpc_cpp_plugin
|
||||||
|
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
|
||||||
|
|
||||||
|
PROTOS_PATH = ../../protos
|
||||||
|
|
||||||
|
vpath %.proto $(PROTOS_PATH)
|
||||||
|
|
||||||
|
all: system-check greeter_callback_client greeter_callback_server
|
||||||
|
|
||||||
|
greeter_callback_client: util.o helloworld.pb.o helloworld.grpc.pb.o greeter_callback_client.o
|
||||||
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
greeter_callback_server: util.o helloworld.pb.o helloworld.grpc.pb.o greeter_callback_server.o
|
||||||
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
.PRECIOUS: %.grpc.pb.cc
|
||||||
|
%.grpc.pb.cc: %.proto
|
||||||
|
$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
|
||||||
|
|
||||||
|
.PRECIOUS: %.pb.cc
|
||||||
|
%.pb.cc: %.proto
|
||||||
|
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.pb.cc *.pb.h greeter_callback_client greeter_callback_server
|
||||||
|
|
||||||
|
|
||||||
|
# The following is to test your system and ensure a smoother experience.
|
||||||
|
# They are by no means necessary to actually compile a grpc-enabled software.
|
||||||
|
|
||||||
|
PROTOC_CMD = which $(PROTOC)
|
||||||
|
PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q 'libprotoc.3\|libprotoc [0-9][0-9]\.'
|
||||||
|
PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN)
|
||||||
|
HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false)
|
||||||
|
ifeq ($(HAS_PROTOC),true)
|
||||||
|
HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
|
||||||
|
endif
|
||||||
|
HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false)
|
||||||
|
|
||||||
|
SYSTEM_OK = false
|
||||||
|
ifeq ($(HAS_VALID_PROTOC),true)
|
||||||
|
ifeq ($(HAS_PLUGIN),true)
|
||||||
|
SYSTEM_OK = true
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
system-check:
|
||||||
|
ifneq ($(HAS_VALID_PROTOC),true)
|
||||||
|
@echo " DEPENDENCY ERROR"
|
||||||
|
@echo
|
||||||
|
@echo "You don't have protoc 3.0.0 or newer installed in your path."
|
||||||
|
@echo "Please install an up-to-date version of Google protocol buffers."
|
||||||
|
@echo "You can find it here:"
|
||||||
|
@echo
|
||||||
|
@echo " https://github.com/protocolbuffers/protobuf/releases"
|
||||||
|
@echo
|
||||||
|
@echo "Here is what I get when trying to evaluate your version of protoc:"
|
||||||
|
@echo
|
||||||
|
-$(PROTOC) --version
|
||||||
|
@echo
|
||||||
|
@echo
|
||||||
|
endif
|
||||||
|
ifneq ($(HAS_PLUGIN),true)
|
||||||
|
@echo " DEPENDENCY ERROR"
|
||||||
|
@echo
|
||||||
|
@echo "You don't have the grpc c++ protobuf plugin installed in your path."
|
||||||
|
@echo "Please install grpc. You can find it here:"
|
||||||
|
@echo
|
||||||
|
@echo " https://github.com/grpc/grpc"
|
||||||
|
@echo
|
||||||
|
@echo "Here is what I get when trying to detect if you have the plugin:"
|
||||||
|
@echo
|
||||||
|
-which $(GRPC_CPP_PLUGIN)
|
||||||
|
@echo
|
||||||
|
@echo
|
||||||
|
endif
|
||||||
|
ifneq ($(SYSTEM_OK),true)
|
||||||
|
@false
|
||||||
|
endif
|
||||||
|
|
@ -16,12 +16,14 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <condition_variable>
|
// Explicitly define HAVE_ABSEIL to avoid conflict with OTel's Abseil
|
||||||
#include <iostream>
|
// version. Refer
|
||||||
#include <memory>
|
// https://github.com/open-telemetry/opentelemetry-cpp/issues/1042.
|
||||||
#include <mutex>
|
#ifndef HAVE_ABSEIL
|
||||||
|
#define HAVE_ABSEIL
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "absl/flags/flag.h"
|
#include "absl/flags/flag.h"
|
||||||
#include "absl/flags/parse.h"
|
#include "absl/flags/parse.h"
|
||||||
|
|
@ -30,13 +32,10 @@
|
||||||
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
||||||
|
|
||||||
#include <grpcpp/ext/otel_plugin.h>
|
#include <grpcpp/ext/otel_plugin.h>
|
||||||
#include <grpcpp/grpcpp.h>
|
|
||||||
|
|
||||||
#ifdef BAZEL_BUILD
|
#ifdef BAZEL_BUILD
|
||||||
#include "examples/cpp/otel/util.h"
|
#include "examples/cpp/otel/util.h"
|
||||||
#include "examples/protos/helloworld.grpc.pb.h"
|
|
||||||
#else
|
#else
|
||||||
#include "helloworld.grpc.pb.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -44,64 +43,6 @@ ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
|
||||||
ABSL_FLAG(std::string, prometheus_endpoint, "localhost:9465",
|
ABSL_FLAG(std::string, prometheus_endpoint, "localhost:9465",
|
||||||
"Prometheus exporter endpoint");
|
"Prometheus exporter endpoint");
|
||||||
|
|
||||||
using grpc::Channel;
|
|
||||||
using grpc::ClientContext;
|
|
||||||
using grpc::Status;
|
|
||||||
using helloworld::Greeter;
|
|
||||||
using helloworld::HelloReply;
|
|
||||||
using helloworld::HelloRequest;
|
|
||||||
|
|
||||||
class GreeterClient {
|
|
||||||
public:
|
|
||||||
GreeterClient(std::shared_ptr<Channel> channel)
|
|
||||||
: stub_(Greeter::NewStub(channel)) {}
|
|
||||||
|
|
||||||
// Assembles the client's payload, sends it and presents the response back
|
|
||||||
// from the server.
|
|
||||||
std::string SayHello(const std::string& user) {
|
|
||||||
// Data we are sending to the server.
|
|
||||||
HelloRequest request;
|
|
||||||
request.set_name(user);
|
|
||||||
|
|
||||||
// Container for the data we expect from the server.
|
|
||||||
HelloReply reply;
|
|
||||||
|
|
||||||
// Context for the client. It could be used to convey extra information to
|
|
||||||
// the server and/or tweak certain RPC behaviors.
|
|
||||||
ClientContext context;
|
|
||||||
|
|
||||||
// The actual RPC.
|
|
||||||
std::mutex mu;
|
|
||||||
std::condition_variable cv;
|
|
||||||
bool done = false;
|
|
||||||
Status status;
|
|
||||||
stub_->async()->SayHello(&context, &request, &reply,
|
|
||||||
[&mu, &cv, &done, &status](Status s) {
|
|
||||||
status = std::move(s);
|
|
||||||
std::lock_guard<std::mutex> lock(mu);
|
|
||||||
done = true;
|
|
||||||
cv.notify_one();
|
|
||||||
});
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mu);
|
|
||||||
while (!done) {
|
|
||||||
cv.wait(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Act upon its status.
|
|
||||||
if (status.ok()) {
|
|
||||||
return reply.message();
|
|
||||||
} else {
|
|
||||||
std::cout << status.error_code() << ": " << status.error_message()
|
|
||||||
<< std::endl;
|
|
||||||
return "RPC failed";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Greeter::Stub> stub_;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
absl::ParseCommandLine(argc, argv);
|
absl::ParseCommandLine(argc, argv);
|
||||||
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
|
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
|
||||||
|
|
@ -125,20 +66,9 @@ int main(int argc, char** argv) {
|
||||||
<< status.ToString() << std::endl;
|
<< status.ToString() << std::endl;
|
||||||
return static_cast<int>(status.code());
|
return static_cast<int>(status.code());
|
||||||
}
|
}
|
||||||
// Instantiate the client. It requires a channel, out of which the actual RPCs
|
|
||||||
// are created. This channel models a connection to an endpoint specified by
|
|
||||||
// the argument "--target=" which is the only expected argument.
|
|
||||||
std::string target_str = absl::GetFlag(FLAGS_target);
|
|
||||||
grpc::ChannelArguments args;
|
|
||||||
// Continuously send RPCs every second.
|
// Continuously send RPCs every second.
|
||||||
while (true) {
|
RunClient(absl::GetFlag(FLAGS_target));
|
||||||
GreeterClient greeter(grpc::CreateCustomChannel(
|
|
||||||
target_str, grpc::InsecureChannelCredentials(), args));
|
|
||||||
std::string user("world");
|
|
||||||
std::string reply = greeter.SayHello(user);
|
|
||||||
std::cout << "Greeter received: " << reply << std::endl;
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Explicitly define HAVE_ABSEIL to avoid conflict with OTel's Abseil
|
||||||
|
// version. Refer
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-cpp/issues/1042.
|
||||||
|
#ifndef HAVE_ABSEIL
|
||||||
|
#define HAVE_ABSEIL
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -28,15 +35,10 @@
|
||||||
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
||||||
|
|
||||||
#include <grpcpp/ext/otel_plugin.h>
|
#include <grpcpp/ext/otel_plugin.h>
|
||||||
#include <grpcpp/ext/proto_server_reflection_plugin.h>
|
|
||||||
#include <grpcpp/grpcpp.h>
|
|
||||||
#include <grpcpp/health_check_service_interface.h>
|
|
||||||
|
|
||||||
#ifdef BAZEL_BUILD
|
#ifdef BAZEL_BUILD
|
||||||
#include "examples/cpp/otel/util.h"
|
#include "examples/cpp/otel/util.h"
|
||||||
#include "examples/protos/helloworld.grpc.pb.h"
|
|
||||||
#else
|
#else
|
||||||
#include "helloworld.grpc.pb.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -44,50 +46,6 @@ ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
|
||||||
ABSL_FLAG(std::string, prometheus_endpoint, "localhost:9464",
|
ABSL_FLAG(std::string, prometheus_endpoint, "localhost:9464",
|
||||||
"Prometheus exporter endpoint");
|
"Prometheus exporter endpoint");
|
||||||
|
|
||||||
using grpc::CallbackServerContext;
|
|
||||||
using grpc::Server;
|
|
||||||
using grpc::ServerBuilder;
|
|
||||||
using grpc::ServerUnaryReactor;
|
|
||||||
using grpc::Status;
|
|
||||||
using helloworld::Greeter;
|
|
||||||
using helloworld::HelloReply;
|
|
||||||
using helloworld::HelloRequest;
|
|
||||||
|
|
||||||
// Logic and data behind the server's behavior.
|
|
||||||
class GreeterServiceImpl final : public Greeter::CallbackService {
|
|
||||||
ServerUnaryReactor* SayHello(CallbackServerContext* context,
|
|
||||||
const HelloRequest* request,
|
|
||||||
HelloReply* reply) override {
|
|
||||||
std::string prefix("Hello ");
|
|
||||||
reply->set_message(prefix + request->name());
|
|
||||||
|
|
||||||
ServerUnaryReactor* reactor = context->DefaultReactor();
|
|
||||||
reactor->Finish(Status::OK);
|
|
||||||
return reactor;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void RunServer(uint16_t port) {
|
|
||||||
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
|
|
||||||
GreeterServiceImpl service;
|
|
||||||
|
|
||||||
grpc::EnableDefaultHealthCheckService(true);
|
|
||||||
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
|
|
||||||
ServerBuilder builder;
|
|
||||||
// Listen on the given address without any authentication mechanism.
|
|
||||||
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
|
||||||
// Register "service" as the instance through which we'll communicate with
|
|
||||||
// clients. In this case it corresponds to an *synchronous* service.
|
|
||||||
builder.RegisterService(&service);
|
|
||||||
// Finally assemble the server.
|
|
||||||
std::unique_ptr<Server> server(builder.BuildAndStart());
|
|
||||||
std::cout << "Server listening on " << server_address << std::endl;
|
|
||||||
|
|
||||||
// Wait for the server to shutdown. Note that some other thread must be
|
|
||||||
// responsible for shutting down the server for this call to ever return.
|
|
||||||
server->Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
absl::ParseCommandLine(argc, argv);
|
absl::ParseCommandLine(argc, argv);
|
||||||
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
|
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright 2023 the gRPC authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "greeter_callback_client",
|
||||||
|
srcs = ["greeter_callback_client.cc"],
|
||||||
|
defines = ["BAZEL_BUILD"],
|
||||||
|
deps = [
|
||||||
|
"//:grpcpp_otel_plugin",
|
||||||
|
"//examples/cpp/otel:util",
|
||||||
|
"@com_google_absl//absl/flags:flag",
|
||||||
|
"@com_google_absl//absl/flags:parse",
|
||||||
|
"@io_opentelemetry_cpp//exporters/ostream:ostream_metric_exporter",
|
||||||
|
"@io_opentelemetry_cpp//sdk/src/metrics",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "greeter_callback_server",
|
||||||
|
srcs = ["greeter_callback_server.cc"],
|
||||||
|
defines = ["BAZEL_BUILD"],
|
||||||
|
deps = [
|
||||||
|
"//:grpcpp_otel_plugin",
|
||||||
|
"//examples/cpp/otel:util",
|
||||||
|
"@com_google_absl//absl/flags:flag",
|
||||||
|
"@com_google_absl//absl/flags:parse",
|
||||||
|
"@com_google_absl//absl/strings:str_format",
|
||||||
|
"@io_opentelemetry_cpp//exporters/ostream:ostream_metric_exporter",
|
||||||
|
"@io_opentelemetry_cpp//sdk/src/metrics",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Copyright 2018 gRPC authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# cmake build file for C++ gRPC OpenTelemetry example.
|
||||||
|
# Assumes absl, protobuf, prometheus-cpp, opentelemetry-cpp and gRPC (with -DgRPC_BUILD_OPENTELEMETRY_PLUGIN=ON) have been installed using cmake.
|
||||||
|
# See cmake_externalproject/CMakeLists.txt for all-in-one cmake build
|
||||||
|
# that automatically builds all the dependencies before building helloworld.
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
project(grpc_opentelemetry_example C CXX)
|
||||||
|
|
||||||
|
include(../../cmake/common.cmake)
|
||||||
|
|
||||||
|
# Find opentelemetry-cpp package
|
||||||
|
find_package(opentelemetry-cpp CONFIG REQUIRED)
|
||||||
|
|
||||||
|
# Proto file
|
||||||
|
get_filename_component(hw_proto "../../../protos/helloworld.proto" ABSOLUTE)
|
||||||
|
get_filename_component(hw_proto_path "${hw_proto}" PATH)
|
||||||
|
|
||||||
|
# Generated sources
|
||||||
|
set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.cc")
|
||||||
|
set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.pb.h")
|
||||||
|
set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.grpc.pb.cc")
|
||||||
|
set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/helloworld.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 generated *.pb.h files
|
||||||
|
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
include_directories("${CMAKE_SOURCE_DIR}")
|
||||||
|
|
||||||
|
# hw_grpc_proto
|
||||||
|
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})
|
||||||
|
|
||||||
|
# util
|
||||||
|
add_library(util
|
||||||
|
"../util.cc")
|
||||||
|
target_link_libraries(util
|
||||||
|
hw_grpc_proto
|
||||||
|
opentelemetry-cpp::metrics
|
||||||
|
${_GRPC_GRPCPP}
|
||||||
|
${_REFLECTION}
|
||||||
|
${_PROTOBUF_LIBPROTOBUF})
|
||||||
|
|
||||||
|
# Targets greeter_callback_(client|server)
|
||||||
|
foreach(_target
|
||||||
|
greeter_callback_client greeter_callback_server)
|
||||||
|
add_executable(${_target} "${_target}.cc")
|
||||||
|
target_link_libraries(${_target}
|
||||||
|
absl::flags
|
||||||
|
absl::flags_parse
|
||||||
|
opentelemetry-cpp::metrics
|
||||||
|
opentelemetry-cpp::ostream_metrics_exporter
|
||||||
|
gRPC::grpcpp_otel_plugin
|
||||||
|
util
|
||||||
|
${_PROTOBUF_LIBPROTOBUF})
|
||||||
|
endforeach()
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
#
|
||||||
|
# Copyright 2015 gRPC authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
# TODO(jtattermusch): Remove the hack to workaround protobuf bug. See https://github.com/protocolbuffers/protobuf/issues/12439
|
||||||
|
# Hack: protobuf currently doesn't declare it's absl dependencies when protobuf.pc pkgconfig file is used.
|
||||||
|
PROTOBUF_ABSL_DEPS = absl_absl_check absl_absl_log absl_algorithm absl_base absl_bind_front absl_bits absl_btree absl_cleanup absl_cord absl_core_headers absl_debugging absl_die_if_null absl_dynamic_annotations absl_flags absl_flat_hash_map absl_flat_hash_set absl_function_ref absl_hash absl_layout absl_log_initialize absl_log_severity absl_memory absl_node_hash_map absl_node_hash_set absl_optional absl_span absl_status absl_statusor absl_strings absl_synchronization absl_time absl_type_traits absl_utility absl_variant
|
||||||
|
# TODO(jtattermusch): Remove the hack to workaround protobuf/utf8_range bug. See https://github.com/protocolbuffers/utf8_range/issues/20
|
||||||
|
# Hack: utf8_range (which is protobuf's dependency) currently doesn't have a pkgconfig file, so we need to explicitly
|
||||||
|
# tweak the list of libraries to link against to fix the build.
|
||||||
|
PROTOBUF_UTF8_RANGE_LINK_LIBS = -lutf8_validity
|
||||||
|
OPENTELEMETRY_LINK_LIBS = -lopentelemetry_metrics -lopentelemetry_exporter_ostream_metrics -lopentelemetry_resources -lopentelemetry_common
|
||||||
|
|
||||||
|
HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
|
||||||
|
SYSTEM ?= $(HOST_SYSTEM)
|
||||||
|
CXX = g++
|
||||||
|
CPPFLAGS += `pkg-config --cflags protobuf grpc absl_flags absl_flags_parse`
|
||||||
|
ifeq ($(SYSTEM),Darwin)
|
||||||
|
LDFLAGS += -L/usr/local/lib `pkg-config --libs --static protobuf grpc++ grpcpp_otel_plugin absl_flags absl_flags_parse $(PROTOBUF_ABSL_DEPS)` \
|
||||||
|
$(PROTOBUF_UTF8_RANGE_LINK_LIBS) \
|
||||||
|
$(OPENTELEMETRY_LINK_LIBS) \
|
||||||
|
-pthread \
|
||||||
|
-lgrpc++_reflection \
|
||||||
|
-ldl
|
||||||
|
else
|
||||||
|
LDFLAGS += -L/usr/local/lib `pkg-config --libs --static protobuf grpc++ grpcpp_otel_plugin absl_flags absl_flags_parse $(PROTOBUF_ABSL_DEPS)` \
|
||||||
|
$(PROTOBUF_UTF8_RANGE_LINK_LIBS) \
|
||||||
|
$(OPENTELEMETRY_LINK_LIBS) \
|
||||||
|
-pthread \
|
||||||
|
-Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed \
|
||||||
|
-ldl
|
||||||
|
endif
|
||||||
|
PROTOC = protoc
|
||||||
|
GRPC_CPP_PLUGIN = grpc_cpp_plugin
|
||||||
|
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
|
||||||
|
|
||||||
|
PROTOS_PATH = ../../../protos
|
||||||
|
|
||||||
|
vpath %.proto $(PROTOS_PATH)
|
||||||
|
|
||||||
|
all: system-check greeter_callback_client greeter_callback_server
|
||||||
|
|
||||||
|
greeter_callback_client: util.o helloworld.pb.o helloworld.grpc.pb.o greeter_callback_client.o
|
||||||
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
greeter_callback_server: util.o helloworld.pb.o helloworld.grpc.pb.o greeter_callback_server.o
|
||||||
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
.PRECIOUS: %.grpc.pb.cc
|
||||||
|
%.grpc.pb.cc: %.proto
|
||||||
|
$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
|
||||||
|
|
||||||
|
.PRECIOUS: %.pb.cc
|
||||||
|
%.pb.cc: %.proto
|
||||||
|
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
|
||||||
|
|
||||||
|
util.o: ../util.cc
|
||||||
|
$(CXX) $^ -I . -I $(CPPFLAGS) -c -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.pb.cc *.pb.h greeter_callback_client greeter_callback_server
|
||||||
|
|
||||||
|
|
||||||
|
# The following is to test your system and ensure a smoother experience.
|
||||||
|
# They are by no means necessary to actually compile a grpc-enabled software.
|
||||||
|
|
||||||
|
PROTOC_CMD = which $(PROTOC)
|
||||||
|
PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q 'libprotoc.3\|libprotoc [0-9][0-9]\.'
|
||||||
|
PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN)
|
||||||
|
HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false)
|
||||||
|
ifeq ($(HAS_PROTOC),true)
|
||||||
|
HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
|
||||||
|
endif
|
||||||
|
HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false)
|
||||||
|
|
||||||
|
SYSTEM_OK = false
|
||||||
|
ifeq ($(HAS_VALID_PROTOC),true)
|
||||||
|
ifeq ($(HAS_PLUGIN),true)
|
||||||
|
SYSTEM_OK = true
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
system-check:
|
||||||
|
ifneq ($(HAS_VALID_PROTOC),true)
|
||||||
|
@echo " DEPENDENCY ERROR"
|
||||||
|
@echo
|
||||||
|
@echo "You don't have protoc 3.0.0 or newer installed in your path."
|
||||||
|
@echo "Please install an up-to-date version of Google protocol buffers."
|
||||||
|
@echo "You can find it here:"
|
||||||
|
@echo
|
||||||
|
@echo " https://github.com/protocolbuffers/protobuf/releases"
|
||||||
|
@echo
|
||||||
|
@echo "Here is what I get when trying to evaluate your version of protoc:"
|
||||||
|
@echo
|
||||||
|
-$(PROTOC) --version
|
||||||
|
@echo
|
||||||
|
@echo
|
||||||
|
endif
|
||||||
|
ifneq ($(HAS_PLUGIN),true)
|
||||||
|
@echo " DEPENDENCY ERROR"
|
||||||
|
@echo
|
||||||
|
@echo "You don't have the grpc c++ protobuf plugin installed in your path."
|
||||||
|
@echo "Please install grpc. You can find it here:"
|
||||||
|
@echo
|
||||||
|
@echo " https://github.com/grpc/grpc"
|
||||||
|
@echo
|
||||||
|
@echo "Here is what I get when trying to detect if you have the plugin:"
|
||||||
|
@echo
|
||||||
|
-which $(GRPC_CPP_PLUGIN)
|
||||||
|
@echo
|
||||||
|
@echo
|
||||||
|
endif
|
||||||
|
ifneq ($(SYSTEM_OK),true)
|
||||||
|
@false
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2021 gRPC authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Explicitly define HAVE_ABSEIL to avoid conflict with OTel's Abseil
|
||||||
|
// version. Refer
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-cpp/issues/1042.
|
||||||
|
#ifndef HAVE_ABSEIL
|
||||||
|
#define HAVE_ABSEIL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "absl/flags/flag.h"
|
||||||
|
#include "absl/flags/parse.h"
|
||||||
|
#include "opentelemetry/exporters/ostream/metric_exporter.h"
|
||||||
|
#include "opentelemetry/exporters/ostream/metric_exporter_factory.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
||||||
|
|
||||||
|
#include <grpcpp/ext/otel_plugin.h>
|
||||||
|
|
||||||
|
#ifdef BAZEL_BUILD
|
||||||
|
#include "examples/cpp/otel/util.h"
|
||||||
|
#else
|
||||||
|
#include "../util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
absl::ParseCommandLine(argc, argv);
|
||||||
|
// Register a global gRPC OpenTelemetry plugin configured with an ostream
|
||||||
|
// exporter.
|
||||||
|
auto ostream_exporter =
|
||||||
|
opentelemetry::exporter::metrics::OStreamMetricExporterFactory::Create();
|
||||||
|
opentelemetry::sdk::metrics::PeriodicExportingMetricReaderOptions
|
||||||
|
reader_options;
|
||||||
|
reader_options.export_interval_millis = std::chrono::milliseconds(1000);
|
||||||
|
reader_options.export_timeout_millis = std::chrono::milliseconds(500);
|
||||||
|
auto reader =
|
||||||
|
opentelemetry::sdk::metrics::PeriodicExportingMetricReaderFactory::Create(
|
||||||
|
std::move(ostream_exporter), reader_options);
|
||||||
|
auto meter_provider =
|
||||||
|
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
|
||||||
|
// The default histogram boundaries are not granular enough for RPCs. Override
|
||||||
|
// the "grpc.client.attempt.duration" view as recommended by
|
||||||
|
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
|
||||||
|
AddLatencyView(meter_provider.get(), "grpc.client.attempt.duration", "s");
|
||||||
|
meter_provider->AddMetricReader(std::move(reader));
|
||||||
|
auto status = grpc::OpenTelemetryPluginBuilder()
|
||||||
|
.SetMeterProvider(std::move(meter_provider))
|
||||||
|
.BuildAndRegisterGlobal();
|
||||||
|
if (!status.ok()) {
|
||||||
|
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
|
||||||
|
<< status.ToString() << std::endl;
|
||||||
|
return static_cast<int>(status.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continuously send RPCs every second.
|
||||||
|
RunClient(absl::GetFlag(FLAGS_target));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2021 gRPC authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Explicitly define HAVE_ABSEIL to avoid conflict with OTel's Abseil
|
||||||
|
// version. Refer
|
||||||
|
// https://github.com/open-telemetry/opentelemetry-cpp/issues/1042.
|
||||||
|
#ifndef HAVE_ABSEIL
|
||||||
|
#define HAVE_ABSEIL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "absl/flags/flag.h"
|
||||||
|
#include "absl/flags/parse.h"
|
||||||
|
#include "opentelemetry/exporters/ostream/metric_exporter.h"
|
||||||
|
#include "opentelemetry/exporters/ostream/metric_exporter_factory.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h"
|
||||||
|
#include "opentelemetry/sdk/metrics/meter_provider.h"
|
||||||
|
|
||||||
|
#include <grpcpp/ext/otel_plugin.h>
|
||||||
|
|
||||||
|
#ifdef BAZEL_BUILD
|
||||||
|
#include "examples/cpp/otel/util.h"
|
||||||
|
#else
|
||||||
|
#include "../util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
absl::ParseCommandLine(argc, argv);
|
||||||
|
// Register a global gRPC OpenTelemetry plugin configured with an ostream
|
||||||
|
// exporter.
|
||||||
|
auto ostream_exporter =
|
||||||
|
opentelemetry::exporter::metrics::OStreamMetricExporterFactory::Create();
|
||||||
|
opentelemetry::sdk::metrics::PeriodicExportingMetricReaderOptions
|
||||||
|
reader_options;
|
||||||
|
reader_options.export_interval_millis = std::chrono::milliseconds(1000);
|
||||||
|
reader_options.export_timeout_millis = std::chrono::milliseconds(500);
|
||||||
|
auto reader =
|
||||||
|
opentelemetry::sdk::metrics::PeriodicExportingMetricReaderFactory::Create(
|
||||||
|
std::move(ostream_exporter), reader_options);
|
||||||
|
auto meter_provider =
|
||||||
|
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
|
||||||
|
// The default histogram boundaries are not granular enough for RPCs. Override
|
||||||
|
// the "grpc.server.call.duration" view as recommended by
|
||||||
|
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
|
||||||
|
AddLatencyView(meter_provider.get(), "grpc.server.call.duration", "s");
|
||||||
|
meter_provider->AddMetricReader(std::move(reader));
|
||||||
|
auto status = grpc::OpenTelemetryPluginBuilder()
|
||||||
|
.SetMeterProvider(std::move(meter_provider))
|
||||||
|
.BuildAndRegisterGlobal();
|
||||||
|
if (!status.ok()) {
|
||||||
|
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
|
||||||
|
<< status.ToString() << std::endl;
|
||||||
|
return static_cast<int>(status.code());
|
||||||
|
}
|
||||||
|
RunServer(absl::GetFlag(FLAGS_port));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -16,17 +16,42 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifdef BAZEL_BUILD
|
// Explicitly define HAVE_ABSEIL to avoid conflict with OTel's Abseil
|
||||||
#include "examples/cpp/otel/util.h"
|
// version. Refer
|
||||||
#else
|
// https://github.com/open-telemetry/opentelemetry-cpp/issues/1042.
|
||||||
#include "util.h"
|
#ifndef HAVE_ABSEIL
|
||||||
|
#define HAVE_ABSEIL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "opentelemetry/sdk/metrics/view/instrument_selector_factory.h"
|
#include "opentelemetry/sdk/metrics/view/instrument_selector_factory.h"
|
||||||
#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h"
|
#include "opentelemetry/sdk/metrics/view/meter_selector_factory.h"
|
||||||
#include "opentelemetry/sdk/metrics/view/view_factory.h"
|
#include "opentelemetry/sdk/metrics/view/view_factory.h"
|
||||||
|
|
||||||
|
#include <grpcpp/ext/proto_server_reflection_plugin.h>
|
||||||
#include <grpcpp/grpcpp.h>
|
#include <grpcpp/grpcpp.h>
|
||||||
|
#include <grpcpp/health_check_service_interface.h>
|
||||||
|
|
||||||
|
#ifdef BAZEL_BUILD
|
||||||
|
#include "examples/cpp/otel/util.h"
|
||||||
|
#include "examples/protos/helloworld.grpc.pb.h"
|
||||||
|
#else
|
||||||
|
#include "helloworld.grpc.pb.h"
|
||||||
|
#include "util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using grpc::CallbackServerContext;
|
||||||
|
using grpc::Channel;
|
||||||
|
using grpc::ClientContext;
|
||||||
|
using grpc::Server;
|
||||||
|
using grpc::ServerBuilder;
|
||||||
|
using grpc::ServerUnaryReactor;
|
||||||
|
using grpc::Status;
|
||||||
|
using helloworld::Greeter;
|
||||||
|
using helloworld::HelloReply;
|
||||||
|
using helloworld::HelloRequest;
|
||||||
|
|
||||||
void AddLatencyView(opentelemetry::sdk::metrics::MeterProvider* provider,
|
void AddLatencyView(opentelemetry::sdk::metrics::MeterProvider* provider,
|
||||||
const std::string& name, const std::string& unit) {
|
const std::string& name, const std::string& unit) {
|
||||||
|
|
@ -48,3 +73,109 @@ void AddLatencyView(opentelemetry::sdk::metrics::MeterProvider* provider,
|
||||||
opentelemetry::sdk::metrics::AggregationType::kHistogram,
|
opentelemetry::sdk::metrics::AggregationType::kHistogram,
|
||||||
std::move(histogram_config)));
|
std::move(histogram_config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class GreeterClient {
|
||||||
|
public:
|
||||||
|
GreeterClient(std::shared_ptr<Channel> channel)
|
||||||
|
: stub_(Greeter::NewStub(channel)) {}
|
||||||
|
|
||||||
|
// Assembles the client's payload, sends it and presents the response back
|
||||||
|
// from the server.
|
||||||
|
std::string SayHello(const std::string& user) {
|
||||||
|
// Data we are sending to the server.
|
||||||
|
HelloRequest request;
|
||||||
|
request.set_name(user);
|
||||||
|
|
||||||
|
// Container for the data we expect from the server.
|
||||||
|
HelloReply reply;
|
||||||
|
|
||||||
|
// Context for the client. It could be used to convey extra information to
|
||||||
|
// the server and/or tweak certain RPC behaviors.
|
||||||
|
ClientContext context;
|
||||||
|
|
||||||
|
// The actual RPC.
|
||||||
|
std::mutex mu;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool done = false;
|
||||||
|
Status status;
|
||||||
|
stub_->async()->SayHello(&context, &request, &reply,
|
||||||
|
[&mu, &cv, &done, &status](Status s) {
|
||||||
|
status = std::move(s);
|
||||||
|
std::lock_guard<std::mutex> lock(mu);
|
||||||
|
done = true;
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(mu);
|
||||||
|
while (!done) {
|
||||||
|
cv.wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act upon its status.
|
||||||
|
if (status.ok()) {
|
||||||
|
return reply.message();
|
||||||
|
} else {
|
||||||
|
std::cout << status.error_code() << ": " << status.error_message()
|
||||||
|
<< std::endl;
|
||||||
|
return "RPC failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Greeter::Stub> stub_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Logic and data behind the server's behavior.
|
||||||
|
class GreeterServiceImpl final : public Greeter::CallbackService {
|
||||||
|
ServerUnaryReactor* SayHello(CallbackServerContext* context,
|
||||||
|
const HelloRequest* request,
|
||||||
|
HelloReply* reply) override {
|
||||||
|
std::string prefix("Hello ");
|
||||||
|
reply->set_message(prefix + request->name());
|
||||||
|
|
||||||
|
ServerUnaryReactor* reactor = context->DefaultReactor();
|
||||||
|
reactor->Finish(Status::OK);
|
||||||
|
return reactor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void RunServer(uint16_t port) {
|
||||||
|
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
|
||||||
|
GreeterServiceImpl service;
|
||||||
|
|
||||||
|
grpc::EnableDefaultHealthCheckService(true);
|
||||||
|
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
|
||||||
|
ServerBuilder builder;
|
||||||
|
// Listen on the given address without any authentication mechanism.
|
||||||
|
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
||||||
|
// Register "service" as the instance through which we'll communicate with
|
||||||
|
// clients. In this case it corresponds to an *synchronous* service.
|
||||||
|
builder.RegisterService(&service);
|
||||||
|
// Finally assemble the server.
|
||||||
|
std::unique_ptr<Server> server(builder.BuildAndStart());
|
||||||
|
std::cout << "Server listening on " << server_address << std::endl;
|
||||||
|
|
||||||
|
// Wait for the server to shutdown. Note that some other thread must be
|
||||||
|
// responsible for shutting down the server for this call to ever return.
|
||||||
|
server->Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunClient(const std::string& target_str) {
|
||||||
|
// Instantiate the client. It requires a channel, out of which the actual RPCs
|
||||||
|
// are created. This channel models a connection to an endpoint specified by
|
||||||
|
// the argument "--target=" which is the only expected argument.
|
||||||
|
grpc::ChannelArguments args;
|
||||||
|
// Continuously send RPCs every second.
|
||||||
|
while (true) {
|
||||||
|
GreeterClient greeter(grpc::CreateCustomChannel(
|
||||||
|
target_str, grpc::InsecureChannelCredentials(), args));
|
||||||
|
std::string user("world");
|
||||||
|
std::string reply = greeter.SayHello(user);
|
||||||
|
std::cout << "Greeter received: " << reply << std::endl;
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,6 @@
|
||||||
void AddLatencyView(opentelemetry::sdk::metrics::MeterProvider* provider,
|
void AddLatencyView(opentelemetry::sdk::metrics::MeterProvider* provider,
|
||||||
const std::string& name, const std::string& unit);
|
const std::string& name, const std::string& unit);
|
||||||
|
|
||||||
|
void RunServer(uint16_t port);
|
||||||
|
void RunClient(const std::string& target_str);
|
||||||
#endif // GRPCPP_EXAMPLES_CPP_OTEL_UTIL_H
|
#endif // GRPCPP_EXAMPLES_CPP_OTEL_UTIL_H
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@
|
||||||
if lib_name == 'protobuf':
|
if lib_name == 'protobuf':
|
||||||
# TODO(jtattermusch): add better way of excluding explicit protobuf dependency.
|
# TODO(jtattermusch): add better way of excluding explicit protobuf dependency.
|
||||||
continue
|
continue
|
||||||
requires.add(lib_name)
|
if lib_name == 'opentelemetry-cpp::api':
|
||||||
|
requires.add('opentelemetry_api')
|
||||||
|
else:
|
||||||
|
requires.add(lib_name)
|
||||||
return list(sorted(requires))
|
return list(sorted(requires))
|
||||||
|
|
||||||
def get_pkgconfig_requires_private(lib):
|
def get_pkgconfig_requires_private(lib):
|
||||||
|
|
@ -1142,3 +1145,14 @@
|
||||||
"${" ".join(get_pkgconfig_libs("grpc++_unsecure"))}"
|
"${" ".join(get_pkgconfig_libs("grpc++_unsecure"))}"
|
||||||
"${" ".join(get_pkgconfig_libs_private("grpc++_unsecure"))}"
|
"${" ".join(get_pkgconfig_libs_private("grpc++_unsecure"))}"
|
||||||
"grpc++_unsecure.pc")
|
"grpc++_unsecure.pc")
|
||||||
|
|
||||||
|
# grpcpp_otel_plugin .pc file
|
||||||
|
generate_pkgconfig(
|
||||||
|
"gRPC++ OpenTelemetry Plugin"
|
||||||
|
"OpenTelemetry Plugin for gRPC C++"
|
||||||
|
"<%text>${gRPC_CPP_VERSION}</%text>"
|
||||||
|
"${" ".join(get_pkgconfig_requires("grpcpp_otel_plugin"))}"
|
||||||
|
"${" ".join(get_pkgconfig_requires_private("grpcpp_otel_plugin"))}"
|
||||||
|
"${" ".join(get_pkgconfig_libs("grpcpp_otel_plugin"))}"
|
||||||
|
"${" ".join(get_pkgconfig_libs_private("grpcpp_otel_plugin"))}"
|
||||||
|
"grpcpp_otel_plugin.pc")
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,13 @@ cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||||
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}" install
|
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}" install
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
# Install OpenTelemetry
|
||||||
|
mkdir -p "third_party/opentelemetry-cpp/cmake/build"
|
||||||
|
pushd "third_party/opentelemetry-cpp/cmake/build"
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_ABSEIL=ON -DBUILD_TESTING=OFF -DWITH_BENCHMARK=OFF ../..
|
||||||
|
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}" install
|
||||||
|
popd
|
||||||
|
|
||||||
# Just before installing gRPC, wipe out contents of all the submodules to simulate
|
# Just before installing gRPC, wipe out contents of all the submodules to simulate
|
||||||
# a standalone build from an archive.
|
# a standalone build from an archive.
|
||||||
# Get list of submodules from the .gitmodules file since for "git submodule foreach"
|
# Get list of submodules from the .gitmodules file since for "git submodule foreach"
|
||||||
|
|
@ -88,6 +95,7 @@ cmake \
|
||||||
-DgRPC_RE2_PROVIDER=package \
|
-DgRPC_RE2_PROVIDER=package \
|
||||||
-DgRPC_SSL_PROVIDER=package \
|
-DgRPC_SSL_PROVIDER=package \
|
||||||
-DgRPC_ZLIB_PROVIDER=package \
|
-DgRPC_ZLIB_PROVIDER=package \
|
||||||
|
-DgRPC_BUILD_GRPCPP_OTEL_PLUGIN=ON \
|
||||||
../..
|
../..
|
||||||
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}" install
|
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}" install
|
||||||
popd
|
popd
|
||||||
|
|
@ -111,3 +119,8 @@ popd
|
||||||
pushd examples/cpp/route_guide
|
pushd examples/cpp/route_guide
|
||||||
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}"
|
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
# Build otel example using Makefile and pkg-config
|
||||||
|
pushd examples/cpp/otel/ostream
|
||||||
|
make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}"
|
||||||
|
popd
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue