From d49025655dd1c88c784ccce6a8e35208a8c11f69 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Tue, 5 Mar 2024 23:22:13 +0000 Subject: [PATCH] [OTel C++] Add CMake build support --- CMakeLists.txt | 151 ++++++++++++++++++ bazel/grpc_build_system.bzl | 1 - build_autogenerated.yaml | 46 ++++++ cmake/opentelemetry-cpp.cmake | 44 +++++ grpc.gyp | 13 ++ templates/CMakeLists.txt.template | 10 ++ test/cpp/ext/otel/otel_plugin_test.cc | 2 +- test/cpp/ext/otel/otel_test_library.cc | 2 +- test/cpp/ext/otel/otel_test_library.h | 2 +- .../extract_metadata_from_bazel_xml.py | 8 +- tools/run_tests/generated/tests.json | 24 +++ 11 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 cmake/opentelemetry-cpp.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index cf8697a597f..bb0ea23e302 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ set(gRPC_BUILD_MSVC_MP_COUNT 0 CACHE STRING "The maximum number of processes for option(gRPC_BUILD_TESTS "Build tests" OFF) option(gRPC_BUILD_CODEGEN "Build codegen" ON) option(gRPC_DOWNLOAD_ARCHIVES "Download archives for empty 3rd party directories" ON) +option(gRPC_BUILD_OPENTELEMETRY_PLUGIN "Build gRPC OpenTelemetry Plugin" OFF) set(gRPC_INSTALL_default ON) if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -224,6 +225,11 @@ set(gRPC_ABSL_USED_TARGETS absl_meta ) +# The OpenTelemetry plugin support "package" build only at present. +set(gRPC_OPENTELEMETRY_PROVIDER "package") +# set(gRPC_OPENTELEMETRY_PROVIDER "module" CACHE STRING "Provider of opentelemetry library") +# set_property(CACHE gRPC_OPENTELEMETRY_PROVIDER PROPERTY STRINGS "module" "package") + set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library") if(UNIX) @@ -348,6 +354,10 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) set(_gRPC_ALLTARGETS_LIBRARIES ${_gRPC_ALLTARGETS_LIBRARIES} ${_gRPC_SYSTEMD_LIBRARIES}) endif() +if(gRPC_BUILD_OPENTELEMETRY_PLUGIN) + include(cmake/opentelemetry-cpp.cmake) +endif() + # Setup external proto library at third_party/envoy-api with 2 download URLs if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/envoy-api AND gRPC_DOWNLOAD_ARCHIVES) # Download the archive via HTTP, validate the checksum, and extract to third_party/envoy-api. @@ -1201,6 +1211,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx orca_service_end2end_test) add_dependencies(buildtests_cxx orphanable_test) add_dependencies(buildtests_cxx osa_distance_test) + add_dependencies(buildtests_cxx otel_plugin_test) add_dependencies(buildtests_cxx out_of_bounds_bad_client_test) add_dependencies(buildtests_cxx outlier_detection_lb_config_parser_test) add_dependencies(buildtests_cxx outlier_detection_test) @@ -5680,6 +5691,80 @@ endif() endif() + +add_library(grpcpp_otel_plugin + src/cpp/ext/otel/otel_client_filter.cc + src/cpp/ext/otel/otel_plugin.cc + src/cpp/ext/otel/otel_server_call_tracer.cc +) + +target_compile_features(grpcpp_otel_plugin PUBLIC cxx_std_14) + +set_target_properties(grpcpp_otel_plugin PROPERTIES + VERSION ${gRPC_CPP_VERSION} + SOVERSION ${gRPC_CPP_SOVERSION} +) + +if(WIN32 AND MSVC) + set_target_properties(grpcpp_otel_plugin PROPERTIES COMPILE_PDB_NAME "grpcpp_otel_plugin" + COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" + ) + if(BUILD_SHARED_LIBS) + target_compile_definitions(grpcpp_otel_plugin + PRIVATE + "GPR_DLL_IMPORTS" + "GRPC_DLL_IMPORTS" + "GRPCXX_DLL_IMPORTS" + ) + endif() + if(gRPC_INSTALL) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/grpcpp_otel_plugin.pdb + DESTINATION ${gRPC_INSTALL_LIBDIR} OPTIONAL + ) + endif() +endif() + +target_include_directories(grpcpp_otel_plugin + PUBLIC $ $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + ${_gRPC_PROTO_GENS_DIR} +) +target_link_libraries(grpcpp_otel_plugin + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc++ + opentelemetry-cpp::api +) + +foreach(_hdr + include/grpcpp/ext/otel_plugin.h +) + string(REPLACE "include/" "" _path ${_hdr}) + get_filename_component(_path ${_path} PATH) + install(FILES ${_hdr} + DESTINATION "${gRPC_INSTALL_INCLUDEDIR}/${_path}" + ) +endforeach() + + +if(gRPC_INSTALL) + install(TARGETS grpcpp_otel_plugin EXPORT gRPCTargets + RUNTIME DESTINATION ${gRPC_INSTALL_BINDIR} + BUNDLE DESTINATION ${gRPC_INSTALL_BINDIR} + LIBRARY DESTINATION ${gRPC_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${gRPC_INSTALL_LIBDIR} + ) +endif() + + if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -20520,6 +20605,72 @@ target_link_libraries(osa_distance_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(otel_plugin_test + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/echo_messages.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/simple_messages.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h + src/cpp/ext/otel/otel_client_filter.cc + src/cpp/ext/otel/otel_plugin.cc + src/cpp/ext/otel/otel_server_call_tracer.cc + test/cpp/end2end/test_service_impl.cc + test/cpp/ext/otel/otel_plugin_test.cc + test/cpp/ext/otel/otel_test_library.cc +) +if(WIN32 AND MSVC) + if(BUILD_SHARED_LIBS) + target_compile_definitions(otel_plugin_test + PRIVATE + "GPR_DLL_IMPORTS" + "GRPC_DLL_IMPORTS" + "GRPCXX_DLL_IMPORTS" + ) + endif() +endif() +target_compile_features(otel_plugin_test PUBLIC cxx_std_14) +target_include_directories(otel_plugin_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(otel_plugin_test + ${_gRPC_ALLTARGETS_LIBRARIES} + gtest + opentelemetry-cpp::api + opentelemetry-cpp::metrics + grpc++_test_util +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/bazel/grpc_build_system.bzl b/bazel/grpc_build_system.bzl index 6e09896b4ed..bf241fb8685 100644 --- a/bazel/grpc_build_system.bzl +++ b/bazel/grpc_build_system.bzl @@ -204,7 +204,6 @@ def grpc_cc_library( testonly = testonly, linkopts = linkopts, includes = [ - "api/include", "include", "src/core/ext/upb-gen", # Once upb code-gen issue is resolved, remove this. "src/core/ext/upbdefs-gen", # Once upb code-gen issue is resolved, remove this. diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index daafa1d41bd..199f7ed0af5 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -5097,6 +5097,24 @@ libs: - src/cpp/server/channelz/channelz_service_plugin.cc deps: - grpc++ +- name: grpcpp_otel_plugin + build: all + language: c++ + public_headers: + - include/grpcpp/ext/otel_plugin.h + headers: + - src/cpp/ext/otel/key_value_iterable.h + - src/cpp/ext/otel/otel_call_tracer.h + - src/cpp/ext/otel/otel_client_filter.h + - src/cpp/ext/otel/otel_plugin.h + - src/cpp/ext/otel/otel_server_call_tracer.h + src: + - src/cpp/ext/otel/otel_client_filter.cc + - src/cpp/ext/otel/otel_plugin.cc + - src/cpp/ext/otel/otel_server_call_tracer.cc + deps: + - grpc++ + - opentelemetry-cpp::api targets: - name: fd_conservation_posix_test build: test @@ -13171,6 +13189,34 @@ targets: - test/core/util/osa_distance_test.cc deps: - gtest +- name: otel_plugin_test + gtest: true + build: test + language: c++ + headers: + - src/cpp/ext/otel/key_value_iterable.h + - src/cpp/ext/otel/otel_call_tracer.h + - src/cpp/ext/otel/otel_client_filter.h + - src/cpp/ext/otel/otel_plugin.h + - src/cpp/ext/otel/otel_server_call_tracer.h + - test/cpp/end2end/test_service_impl.h + - test/cpp/ext/otel/otel_test_library.h + src: + - src/proto/grpc/testing/echo.proto + - src/proto/grpc/testing/echo_messages.proto + - src/proto/grpc/testing/simple_messages.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto + - src/cpp/ext/otel/otel_client_filter.cc + - src/cpp/ext/otel/otel_plugin.cc + - src/cpp/ext/otel/otel_server_call_tracer.cc + - test/cpp/end2end/test_service_impl.cc + - test/cpp/ext/otel/otel_plugin_test.cc + - test/cpp/ext/otel/otel_test_library.cc + deps: + - gtest + - opentelemetry-cpp::api + - opentelemetry-cpp::metrics + - grpc++_test_util - name: out_of_bounds_bad_client_test gtest: true build: test diff --git a/cmake/opentelemetry-cpp.cmake b/cmake/opentelemetry-cpp.cmake new file mode 100644 index 00000000000..51a02a39e57 --- /dev/null +++ b/cmake/opentelemetry-cpp.cmake @@ -0,0 +1,44 @@ +# Copyright 2024 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. + +if(TARGET opentelemetry-cpp::api) + # If opentelemetry is included already, skip including it. +# OpenTelemetry does not work with "module" mode at present. +# elseif(gRPC_OPENTELEMETRY_PROVIDER STREQUAL "module") +# if(NOT OPENTELEMETRY_ROOT_DIR) +# set(OPENTELEMETRY_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/opentelemetry-cpp) +# endif() +# set(BUILD_TESTING OFF) +# if(NOT gRPC_BUILD_TESTS) +# set(WITH_API_ONLY ON) +# endif() +# set(WITH_ABSEIL ON) +# include_directories(${OPENTELEMETRY_ROOT_DIR} "${OPENTELEMETRY_ROOT_DIR}/api/include") +# add_subdirectory(${OPENTELEMETRY_ROOT_DIR} third_party/opentelemetry-cpp) +# if(EXISTS "${OPENTELEMETRY_ROOT_DIR}/CMakeLists.txt") + # Unclear whether we should install OpenTelemetry along with gRPC + # if(gRPC_INSTALL AND _gRPC_INSTALL_SUPPORTED_FROM_MODULE) + # set(OPENTELEMETRY_INSTALL ON) + # endif() +# else() +# message(WARNING "gRPC_OPENTELEMETRY_PROVIDER is \"module\" but OPENTELEMETRY_ROOT_DIR is wrong") +# endif() +# if(gRPC_INSTALL AND NOT _gRPC_INSTALL_SUPPORTED_FROM_MODULE) +# message(WARNING "gRPC_INSTALL will be forced to FALSE because gRPC_OPENTELEMETRY_PROVIDER is \"module\" and CMake version (${CMAKE_VERSION}) is less than 3.13.") +# set(gRPC_INSTALL FALSE) +# endif() +elseif(gRPC_OPENTELEMETRY_PROVIDER STREQUAL "package") + find_package(opentelemetry-cpp CONFIG REQUIRED) +endif() +set(_gRPC_FIND_OPENTELEMETRY "if(NOT TARGET opentelemetry-cpp::opentelemetry_api)\n find_package(opentelemetry-cpp)\nendif()") diff --git a/grpc.gyp b/grpc.gyp index bb844663ee0..568b996a9d7 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -2351,6 +2351,19 @@ 'src/cpp/server/channelz/channelz_service_plugin.cc', ], }, + { + 'target_name': 'grpcpp_otel_plugin', + 'type': 'static_library', + 'dependencies': [ + 'grpc++', + 'opentelemetry-cpp::api', + ], + 'sources': [ + 'src/cpp/ext/otel/otel_client_filter.cc', + 'src/cpp/ext/otel/otel_plugin.cc', + 'src/cpp/ext/otel/otel_server_call_tracer.cc', + ], + }, { 'target_name': 'boringssl', 'type': 'static_library', diff --git a/templates/CMakeLists.txt.template b/templates/CMakeLists.txt.template index 08cab68e7f4..efe75ace319 100644 --- a/templates/CMakeLists.txt.template +++ b/templates/CMakeLists.txt.template @@ -313,6 +313,7 @@ option(gRPC_BUILD_TESTS "Build tests" OFF) option(gRPC_BUILD_CODEGEN "Build codegen" ON) option(gRPC_DOWNLOAD_ARCHIVES "Download archives for empty 3rd party directories" ON) + option(gRPC_BUILD_OPENTELEMETRY_PLUGIN "Build gRPC OpenTelemetry Plugin" OFF) set(gRPC_INSTALL_default ON) if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -374,6 +375,11 @@ absl_meta ) + # The OpenTelemetry plugin support "package" build only at present. + set(gRPC_OPENTELEMETRY_PROVIDER "package") + # set(gRPC_OPENTELEMETRY_PROVIDER "module" CACHE STRING "Provider of opentelemetry library") + # set_property(CACHE gRPC_OPENTELEMETRY_PROVIDER PROPERTY STRINGS "module" "package") + set(gRPC_USE_PROTO_LITE OFF CACHE BOOL "Use the protobuf-lite library") if(UNIX) @@ -499,6 +505,10 @@ set(_gRPC_ALLTARGETS_LIBRARIES <%text>${_gRPC_ALLTARGETS_LIBRARIES} <%text>${_gRPC_SYSTEMD_LIBRARIES}) endif() + if(gRPC_BUILD_OPENTELEMETRY_PLUGIN) + include(cmake/opentelemetry-cpp.cmake) + endif() + % for external_proto_library in external_proto_libraries: % if len(external_proto_library.urls) > 1: # Setup external proto library at ${external_proto_library.destination} with ${len(external_proto_library.urls)} download URLs diff --git a/test/cpp/ext/otel/otel_plugin_test.cc b/test/cpp/ext/otel/otel_plugin_test.cc index 20aa27f64d1..3cb5cb318ef 100644 --- a/test/cpp/ext/otel/otel_plugin_test.cc +++ b/test/cpp/ext/otel/otel_plugin_test.cc @@ -19,9 +19,9 @@ #include "src/cpp/ext/otel/otel_plugin.h" #include "absl/functional/any_invocable.h" -#include "api/include/opentelemetry/metrics/provider.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "opentelemetry/metrics/provider.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" diff --git a/test/cpp/ext/otel/otel_test_library.cc b/test/cpp/ext/otel/otel_test_library.cc index 6017b58f3c5..ee474a9e330 100644 --- a/test/cpp/ext/otel/otel_test_library.cc +++ b/test/cpp/ext/otel/otel_test_library.cc @@ -19,9 +19,9 @@ #include "test/cpp/ext/otel/otel_test_library.h" #include "absl/functional/any_invocable.h" -#include "api/include/opentelemetry/metrics/provider.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "opentelemetry/metrics/provider.h" #include "opentelemetry/sdk/metrics/export/metric_producer.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" diff --git a/test/cpp/ext/otel/otel_test_library.h b/test/cpp/ext/otel/otel_test_library.h index 303babe2d99..8e800d1e30f 100644 --- a/test/cpp/ext/otel/otel_test_library.h +++ b/test/cpp/ext/otel/otel_test_library.h @@ -20,9 +20,9 @@ #define GRPC_TEST_CPP_EXT_OTEL_OTEL_TEST_LIBRARY_H #include "absl/functional/any_invocable.h" -#include "api/include/opentelemetry/metrics/provider.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "opentelemetry/metrics/provider.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" diff --git a/tools/buildgen/extract_metadata_from_bazel_xml.py b/tools/buildgen/extract_metadata_from_bazel_xml.py index 23c41909ff4..1803bf0e520 100755 --- a/tools/buildgen/extract_metadata_from_bazel_xml.py +++ b/tools/buildgen/extract_metadata_from_bazel_xml.py @@ -351,6 +351,10 @@ def _external_dep_name_from_bazel_dependency(bazel_dep: str) -> Optional[str]: return "protobuf" elif bazel_dep == "@com_google_protobuf//:protoc_lib": return "protoc" + elif bazel_dep == "@io_opentelemetry_cpp//api:api": + return "opentelemetry-cpp::api" + elif bazel_dep == "@io_opentelemetry_cpp//sdk/src/metrics:metrics": + return "opentelemetry-cpp::metrics" else: # Two options here: # * either this is not external dependency at all (which is fine, we will treat it as internal library) @@ -831,8 +835,7 @@ def _exclude_unwanted_cc_tests(tests: List[str]) -> List[str]: tests = [ test for test in tests - if not test.startswith("test/cpp/ext/otel:") - and not test.startswith("test/cpp/ext/csm:") + if not test.startswith("test/cpp/ext/csm:") and not test.startswith("test/cpp/interop:xds_interop") ] @@ -1156,6 +1159,7 @@ _BUILD_EXTRA_METADATA = { "generate_plugin_registry": True, }, "grpcpp_channelz": {"language": "c++", "build": "all"}, + "grpcpp_otel_plugin": {"language": "c++", "build": "all"}, "grpc++_test": { "language": "c++", "build": "private", diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 49f6bb44395..8965c31bb25 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -6455,6 +6455,30 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "otel_plugin_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,