From 7c85abe21f80123ea0bb6d29dfc8dee438c9b25c Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Fri, 10 Apr 2026 16:59:23 +0200 Subject: [PATCH] Centralize shared OpenTelemetry code into `source/extensions/common/opentelemetry/` Signed-off-by: Matthieu MOREL fix: correct clang-format issues and add opentelemetry changelog entry --- CODEOWNERS | 2 + changelogs/current.yaml | 4 + .../access_loggers/open_telemetry/BUILD | 5 +- .../access_log_proto_descriptors.cc | 4 +- .../open_telemetry/grpc_access_log_impl.cc | 4 +- .../open_telemetry/http_access_log_impl.cc | 3 +- .../open_telemetry/otlp_log_utils.cc | 15 -- .../open_telemetry/otlp_log_utils.h | 13 +- source/extensions/common/opentelemetry/BUILD | 24 ++++ .../common/opentelemetry/exporters/otlp/BUILD | 70 ++++++++++ .../exporters/otlp/environment.cc | 20 +++ .../exporters/otlp/environment.h | 20 +++ .../exporters/otlp/grpc_trace_exporter.cc | 57 ++++++++ .../exporters/otlp/grpc_trace_exporter.h | 45 ++++++ .../exporters/otlp/http_trace_exporter.cc | 102 ++++++++++++++ .../exporters/otlp/http_trace_exporter.h | 51 +++++++ .../otlp/populate_attribute_utils.cc} | 33 +++-- .../exporters/otlp/populate_attribute_utils.h | 26 ++++ .../exporters/otlp/trace_exporter.h | 39 ++++++ .../common/opentelemetry/sdk/common/BUILD | 23 ++++ .../common/opentelemetry/sdk/common/types.h | 30 ++++ .../common/opentelemetry/sdk/logs/BUILD | 19 +++ .../common/opentelemetry/sdk/logs/constants.h | 23 ++++ .../common/opentelemetry/sdk/metrics/BUILD | 19 +++ .../opentelemetry/sdk/metrics/constants.h | 23 ++++ .../common/opentelemetry/sdk/trace/BUILD | 19 +++ .../opentelemetry/sdk/trace/constants.h | 23 ++++ .../extensions/common/opentelemetry/types.h | 26 ++++ .../stat_sinks/open_telemetry/BUILD | 5 +- .../open_telemetry_http_impl.cc | 5 +- .../open_telemetry/open_telemetry_impl.cc | 5 +- .../open_telemetry_proto_descriptors.cc | 4 +- source/extensions/tracers/opentelemetry/BUILD | 24 +--- .../opentelemetry/grpc_trace_exporter.cc | 53 +------ .../opentelemetry/grpc_trace_exporter.h | 26 +--- .../opentelemetry/http_trace_exporter.cc | 100 +------------- .../opentelemetry/http_trace_exporter.h | 40 +----- .../tracers/opentelemetry/otlp_utils.h | 64 +-------- .../tracers/opentelemetry/trace_exporter.h | 42 +----- .../common/opentelemetry/exporters/otlp/BUILD | 25 ++++ .../otlp/populate_attribute_utils_test.cc | 130 ++++++++++++++++++ .../opentelemetry/http_trace_exporter_test.cc | 29 ++++ .../opentelemetry/samplers/dynatrace/BUILD | 1 + .../dynatrace/dynatrace_sampler_test.cc | 1 + 44 files changed, 928 insertions(+), 368 deletions(-) create mode 100644 source/extensions/common/opentelemetry/BUILD create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/BUILD create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/environment.cc create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/environment.h create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.cc create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.cc create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h rename source/extensions/{tracers/opentelemetry/otlp_utils.cc => common/opentelemetry/exporters/otlp/populate_attribute_utils.cc} (77%) create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h create mode 100644 source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h create mode 100644 source/extensions/common/opentelemetry/sdk/common/BUILD create mode 100644 source/extensions/common/opentelemetry/sdk/common/types.h create mode 100644 source/extensions/common/opentelemetry/sdk/logs/BUILD create mode 100644 source/extensions/common/opentelemetry/sdk/logs/constants.h create mode 100644 source/extensions/common/opentelemetry/sdk/metrics/BUILD create mode 100644 source/extensions/common/opentelemetry/sdk/metrics/constants.h create mode 100644 source/extensions/common/opentelemetry/sdk/trace/BUILD create mode 100644 source/extensions/common/opentelemetry/sdk/trace/constants.h create mode 100644 source/extensions/common/opentelemetry/types.h create mode 100644 test/extensions/common/opentelemetry/exporters/otlp/BUILD create mode 100644 test/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils_test.cc diff --git a/CODEOWNERS b/CODEOWNERS index e12d493291cfc..2e1345fd1c266 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -85,6 +85,8 @@ extensions/filters/common/original_src @klarose @mattklein123 /*/extensions/tracers/skywalking @wbpcode @Shikugawa # tracers.opentelemetry extension /*/extensions/tracers/opentelemetry @alexanderellis @yanavlasov +# OpenTelemetry common extensions +/*/extensions/common/opentelemetry @kyessenov @mmorel-35 # tracers.fluentd extension /*/extensions/common/fluentd @kevintan250 @yanavlasov /*/extensions/tracers/fluentd @kevintan250 @yanavlasov diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 8fce0410e1fa0..474233a02a69e 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -37,6 +37,10 @@ behavior_changes: minor_behavior_changes: # *Changes that may cause incompatibilities for some users, but should not for most* +- area: opentelemetry + change: | + Refactored shared OpenTelemetry OTLP code into ``source/extensions/common/opentelemetry/``, + consolidating exporter utilities, constants, and include paths. No functional behavior changes. - area: load_balancing change: | Changed the default behavior of ``OrcaWeightManager`` to prefer named metrics over application diff --git a/source/extensions/access_loggers/open_telemetry/BUILD b/source/extensions/access_loggers/open_telemetry/BUILD index 320ebc1a54cee..465e6656938da 100644 --- a/source/extensions/access_loggers/open_telemetry/BUILD +++ b/source/extensions/access_loggers/open_telemetry/BUILD @@ -27,7 +27,8 @@ envoy_cc_library( "//source/common/tracing:custom_tag_lib", "//source/common/tracing:http_tracer_lib", "//source/common/tracing:trace_context_lib", - "//source/common/version:version_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:environment_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:populate_attribute_utils_lib", "@abseil-cpp//absl/strings", "@envoy_api//envoy/data/accesslog/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/access_loggers/open_telemetry/v3:pkg_cc_proto", @@ -52,6 +53,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/extensions/access_loggers/common:grpc_access_logger", "//source/extensions/access_loggers/common:grpc_access_logger_clients_lib", + "//source/extensions/common/opentelemetry/sdk/logs:constants_lib", "//source/extensions/tracers/opentelemetry/resource_detectors:resource_detector_lib", "@envoy_api//envoy/extensions/access_loggers/grpc/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/access_loggers/open_telemetry/v3:pkg_cc_proto", @@ -123,6 +125,7 @@ envoy_cc_library( deps = [ "//source/common/common:assert_lib", "//source/common/protobuf", + "//source/extensions/common/opentelemetry/sdk/logs:constants_lib", ], ) diff --git a/source/extensions/access_loggers/open_telemetry/access_log_proto_descriptors.cc b/source/extensions/access_loggers/open_telemetry/access_log_proto_descriptors.cc index aa8ba51c77578..9087e0790ce67 100644 --- a/source/extensions/access_loggers/open_telemetry/access_log_proto_descriptors.cc +++ b/source/extensions/access_loggers/open_telemetry/access_log_proto_descriptors.cc @@ -3,6 +3,7 @@ #include "source/common/common/assert.h" #include "source/common/common/fmt.h" #include "source/common/protobuf/protobuf.h" +#include "source/extensions/common/opentelemetry/sdk/logs/constants.h" namespace Envoy { namespace Extensions { @@ -10,7 +11,8 @@ namespace AccessLoggers { namespace OpenTelemetry { void validateProtoDescriptors() { - const auto method = "opentelemetry.proto.collector.logs.v1.LogsService.Export"; + const auto method = + std::string(Envoy::Extensions::OpenTelemetry::Sdk::Logs::Constants::kLogsServiceExportMethod); RELEASE_ASSERT(Protobuf::DescriptorPool::generated_pool()->FindMethodByName(method) != nullptr, ""); diff --git a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc index 46d45f5734598..0102085ce7e3f 100644 --- a/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc +++ b/source/extensions/access_loggers/open_telemetry/grpc_access_log_impl.cc @@ -10,6 +10,7 @@ #include "source/common/protobuf/utility.h" #include "source/extensions/access_loggers/common/grpc_access_logger_clients.h" #include "source/extensions/access_loggers/open_telemetry/otlp_log_utils.h" +#include "source/extensions/common/opentelemetry/sdk/logs/constants.h" #include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h" #include "opentelemetry/proto/common/v1/common.pb.h" @@ -37,7 +38,8 @@ GrpcAccessLoggerImpl::GrpcAccessLoggerImpl( ExportLogsServiceResponse>>( client, *Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - "opentelemetry.proto.collector.logs.v1.LogsService.Export"), + std::string(Envoy::Extensions::OpenTelemetry::Sdk::Logs::Constants:: + kLogsServiceExportMethod)), GrpcCommon::optionalRetryPolicy(config.common_config()), genOTelCallbacksFactory())), stats_({ALL_GRPC_ACCESS_LOGGER_STATS(POOL_COUNTER_PREFIX( scope, absl::StrCat(OtlpAccessLogStatsPrefix, config.stat_prefix())))}) { diff --git a/source/extensions/access_loggers/open_telemetry/http_access_log_impl.cc b/source/extensions/access_loggers/open_telemetry/http_access_log_impl.cc index 58a973cadb7eb..f7409e3034b68 100644 --- a/source/extensions/access_loggers/open_telemetry/http_access_log_impl.cc +++ b/source/extensions/access_loggers/open_telemetry/http_access_log_impl.cc @@ -88,7 +88,8 @@ void HttpAccessLoggerImpl::flush() { message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf); // User-Agent header follows the OTLP specification. - message->headers().setReferenceUserAgent(getOtlpUserAgentHeader()); + message->headers().setReferenceUserAgent( + Envoy::Extensions::OpenTelemetry::Exporters::Otlp::GetUserAgent()); // Adds all custom headers to the request. headers_applicator_->apply(message->headers()); diff --git a/source/extensions/access_loggers/open_telemetry/otlp_log_utils.cc b/source/extensions/access_loggers/open_telemetry/otlp_log_utils.cc index 29fd6c7d04454..b65f7c1926cfd 100644 --- a/source/extensions/access_loggers/open_telemetry/otlp_log_utils.cc +++ b/source/extensions/access_loggers/open_telemetry/otlp_log_utils.cc @@ -11,21 +11,12 @@ #include "source/common/protobuf/utility.h" #include "source/common/tracing/custom_tag_impl.h" #include "source/common/tracing/http_tracer_impl.h" -#include "source/common/version/version.h" namespace Envoy { namespace Extensions { namespace AccessLoggers { namespace OpenTelemetry { -opentelemetry::proto::common::v1::KeyValue getStringKeyValue(const std::string& key, - const std::string& value) { - opentelemetry::proto::common::v1::KeyValue keyValue; - keyValue.set_key(key); - keyValue.mutable_value()->set_string_value(value); - return keyValue; -} - ::opentelemetry::proto::common::v1::KeyValueList packBody(const ::opentelemetry::proto::common::v1::AnyValue& body) { ::opentelemetry::proto::common::v1::KeyValueList output; @@ -41,12 +32,6 @@ unpackBody(const ::opentelemetry::proto::common::v1::KeyValueList& value) { return value.values(0).value(); } -// User-Agent header follows the OTLP specification: -// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.52.0/specification/protocol/exporter.md#user-agent -const std::string& getOtlpUserAgentHeader() { - CONSTRUCT_ON_FIRST_USE(std::string, "OTel-OTLP-Exporter-Envoy/" + VersionInfo::version()); -} - void populateTraceContext(opentelemetry::proto::logs::v1::LogRecord& log_entry, const std::string& trace_id_hex, const std::string& span_id_hex) { // Sets trace_id if available. OpenTelemetry trace_id is a 16-byte array, and backends diff --git a/source/extensions/access_loggers/open_telemetry/otlp_log_utils.h b/source/extensions/access_loggers/open_telemetry/otlp_log_utils.h index cf85f319d69ca..dae1b225cec35 100644 --- a/source/extensions/access_loggers/open_telemetry/otlp_log_utils.h +++ b/source/extensions/access_loggers/open_telemetry/otlp_log_utils.h @@ -13,6 +13,8 @@ #include "source/common/common/hex.h" #include "source/common/tracing/custom_tag_impl.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h" #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" @@ -50,8 +52,11 @@ struct OtlpAccessLogStats { }; // Creates a KeyValue protobuf with a string value. -opentelemetry::proto::common::v1::KeyValue getStringKeyValue(const std::string& key, - const std::string& value); +inline opentelemetry::proto::common::v1::KeyValue getStringKeyValue(const std::string& key, + const std::string& value) { + return ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::PopulateAttributeUtils::makeKeyValue( + key, value); +} // Packs the body "AnyValue" to a "KeyValueList" with a single key. ::opentelemetry::proto::common::v1::KeyValueList @@ -62,7 +67,9 @@ ::opentelemetry::proto::common::v1::AnyValue unpackBody(const ::opentelemetry::proto::common::v1::KeyValueList& value); // User-Agent header per OTLP specification. -const std::string& getOtlpUserAgentHeader(); +inline const std::string& getOtlpUserAgentHeader() { + return ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::GetUserAgent(); +} // Populates trace context (trace_id, span_id) on a LogRecord. // Handles 128-bit (32 hex chars) and 64-bit Zipkin-style (16 hex chars) trace IDs. diff --git a/source/extensions/common/opentelemetry/BUILD b/source/extensions/common/opentelemetry/BUILD new file mode 100644 index 0000000000000..b4734e388ae90 --- /dev/null +++ b/source/extensions/common/opentelemetry/BUILD @@ -0,0 +1,24 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "types_lib", + hdrs = ["types.h"], + copts = [ + # Make sure that headers included from opentelemetry-api use Abseil from Envoy + # https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.14.0/api/BUILD#L32 + "-DHAVE_ABSEIL", + ], + deps = [ + "//source/extensions/common/opentelemetry/sdk/common:sdk_common_types_lib", + "@opentelemetry-proto//:common_proto_cc", + "@opentelemetry-proto//:trace_proto_cc", + ], +) diff --git a/source/extensions/common/opentelemetry/exporters/otlp/BUILD b/source/extensions/common/opentelemetry/exporters/otlp/BUILD new file mode 100644 index 0000000000000..dfe0cc5ec72f0 --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/BUILD @@ -0,0 +1,70 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "environment_lib", + srcs = ["environment.cc"], + hdrs = ["environment.h"], + deps = [ + "//source/common/common:macros", + "//source/common/version:version_lib", + ], +) + +envoy_cc_library( + name = "populate_attribute_utils_lib", + srcs = ["populate_attribute_utils.cc"], + hdrs = ["populate_attribute_utils.h"], + copts = [ + # Make sure that headers included from opentelemetry-api use Abseil from Envoy + # https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.14.0/api/BUILD#L32 + "-DHAVE_ABSEIL", + ], + deps = [ + "//source/common/common:assert_lib", + "//source/extensions/common/opentelemetry:types_lib", + "@opentelemetry-cpp//api", + ], +) + +envoy_cc_library( + name = "trace_exporter_lib", + srcs = [ + "grpc_trace_exporter.cc", + "http_trace_exporter.cc", + ], + hdrs = [ + "grpc_trace_exporter.h", + "http_trace_exporter.h", + "trace_exporter.h", + ], + deps = [ + "//envoy/grpc:async_client_manager_interface", + "//envoy/server:factory_context_interface", + "//envoy/upstream:cluster_manager_interface", + "//source/common/grpc:typed_async_client_lib", + "//source/common/http:async_client_utility_lib", + "//source/common/http:header_map_lib", + "//source/common/http:http_service_headers_lib", + "//source/common/http:message_lib", + "//source/common/http:utility_lib", + "//source/common/protobuf", + "//source/common/tracing:trace_context_lib", + "//source/common/version:version_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:environment_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:populate_attribute_utils_lib", + "//source/extensions/common/opentelemetry/sdk/trace:constants_lib", + "//source/server:generic_factory_context_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@opentelemetry-cpp//api", + "@opentelemetry-proto//:trace_proto_cc", + "@opentelemetry-proto//:trace_service_proto_cc", + ], +) diff --git a/source/extensions/common/opentelemetry/exporters/otlp/environment.cc b/source/extensions/common/opentelemetry/exporters/otlp/environment.cc new file mode 100644 index 0000000000000..b82ba1f41d75a --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/environment.cc @@ -0,0 +1,20 @@ +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" + +#include "source/common/common/macros.h" +#include "source/common/version/version.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +const std::string& GetUserAgent() { + CONSTRUCT_ON_FIRST_USE(std::string, "OTel-OTLP-Exporter-Envoy/" + VersionInfo::version()); +} + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/environment.h b/source/extensions/common/opentelemetry/exporters/otlp/environment.h new file mode 100644 index 0000000000000..9f87cb4c69a7d --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/environment.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +// Returns the User-Agent header value to use on OTLP exporter requests. +// The value is compliant with the OpenTelemetry specification: +// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent +const std::string& GetUserAgent(); + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.cc b/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.cc new file mode 100644 index 0000000000000..397d6155b3549 --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.cc @@ -0,0 +1,57 @@ +#include "source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h" + +#include "source/common/common/logger.h" +#include "source/common/grpc/status.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" +#include "source/extensions/common/opentelemetry/sdk/trace/constants.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +OtlpGrpcTraceExporter::OtlpGrpcTraceExporter(const Grpc::RawAsyncClientSharedPtr& client) + : client_(client), + service_method_(*Protobuf::DescriptorPool::generated_pool()->FindMethodByName(std::string( + Envoy::Extensions::OpenTelemetry::Sdk::Trace::Constants::kTraceServiceExportMethod))) {} + +void OtlpGrpcTraceExporter::onCreateInitialMetadata(Http::RequestHeaderMap& metadata) { + metadata.setReferenceUserAgent(GetUserAgent()); +} + +void OtlpGrpcTraceExporter::onSuccess( + Grpc::ResponsePtr&& + response, + Tracing::Span&) { + if (response->has_partial_success()) { + auto msg = response->partial_success().error_message(); + auto rejected_spans = response->partial_success().rejected_spans(); + if (rejected_spans > 0 || !msg.empty()) { + if (msg.empty()) { + msg = "empty message"; + } + ENVOY_LOG(debug, "OTLP partial success: {} ({} spans rejected)", msg, rejected_spans); + } + } +} + +void OtlpGrpcTraceExporter::onFailure(Grpc::Status::GrpcStatus status, const std::string& message, + Tracing::Span&) { + ENVOY_LOG(debug, "OTLP trace export failed with status: {}, message: {}", + Grpc::Utility::grpcStatusToString(status), message); +} + +bool OtlpGrpcTraceExporter::log( + const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) { + client_->send(service_method_, request, *this, Tracing::NullSpan::instance(), + Http::AsyncClient::RequestOptions()); + OtlpTraceExporter::logExportedSpans(request); + return true; +} + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h b/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h new file mode 100644 index 0000000000000..2cca6e9b5a8b5 --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h @@ -0,0 +1,45 @@ +#pragma once + +#include "source/common/grpc/typed_async_client.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h" + +#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +class OtlpGrpcTraceExporter + : public OtlpTraceExporter, + public Grpc::AsyncRequestCallbacks< + opentelemetry::proto::collector::trace::v1::ExportTraceServiceResponse> { +public: + OtlpGrpcTraceExporter(const Grpc::RawAsyncClientSharedPtr& client); + ~OtlpGrpcTraceExporter() override = default; + + void onCreateInitialMetadata(Http::RequestHeaderMap& metadata) override; + + void onSuccess( + Grpc::ResponsePtr&& + response, + Tracing::Span&) override; + + void onFailure(Grpc::Status::GrpcStatus status, const std::string& message, + Tracing::Span&) override; + + bool log(const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) + override; + + Grpc::AsyncClient + client_; + const Protobuf::MethodDescriptor& service_method_; +}; + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.cc b/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.cc new file mode 100644 index 0000000000000..644efd5373b6b --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.cc @@ -0,0 +1,102 @@ +#include "source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h" + +#include +#include +#include +#include + +#include "source/common/common/enum_to_int.h" +#include "source/common/common/logger.h" +#include "source/common/protobuf/utility.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +OtlpHttpTraceExporter::OtlpHttpTraceExporter( + Upstream::ClusterManager& cluster_manager, + const envoy::config::core::v3::HttpService& http_service, + std::shared_ptr headers_applicator) + : cluster_manager_(cluster_manager), http_service_(http_service), + headers_applicator_(std::move(headers_applicator)) {} + +bool OtlpHttpTraceExporter::log( + const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) { + std::string request_body; + + const auto ok = request.SerializeToString(&request_body); + if (!ok) { + ENVOY_LOG(warn, "Error while serializing the binary proto ExportTraceServiceRequest."); + return false; + } + + const auto thread_local_cluster = + cluster_manager_.getThreadLocalCluster(http_service_.http_uri().cluster()); + if (thread_local_cluster == nullptr) { + ENVOY_LOG(error, "OTLP HTTP exporter failed: [cluster = {}] is not configured", + http_service_.http_uri().cluster()); + return false; + } + + Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_service_.http_uri()); + + // The request follows the OTLP HTTP specification: + // https://github.com/open-telemetry/opentelemetry-proto/blob/v1.0.0/docs/specification.md#otlphttp. + message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Post); + message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf); + + // User-Agent header follows the OTLP specification: + // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent + message->headers().setReferenceUserAgent(GetUserAgent()); + + // Add all custom headers to the request (static values set once; formatted values + // re-evaluated now so that runtime updates, e.g. SDS rotation, are reflected). + headers_applicator_->apply(message->headers()); + + message->body().add(request_body); + + const auto options = + Http::AsyncClient::RequestOptions() + .setTimeout(std::chrono::milliseconds( + DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout()))) + .setDiscardResponseBody(true); + + Http::AsyncClient::Request* in_flight_request = + thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options); + + OtlpTraceExporter::logExportedSpans(request); + + if (in_flight_request == nullptr) { + return false; + } + + active_requests_.add(*in_flight_request); + return true; +} + +void OtlpHttpTraceExporter::onSuccess(const Http::AsyncClient::Request& request, + Http::ResponseMessagePtr&& http_response) { + active_requests_.remove(request); + const auto response_code = Http::Utility::getResponseStatus(http_response->headers()); + if (response_code != enumToInt(Http::Code::OK)) { + ENVOY_LOG(error, + "OTLP HTTP exporter received a non-success status code: {} while exporting the OTLP " + "message", + response_code); + } +} + +void OtlpHttpTraceExporter::onFailure(const Http::AsyncClient::Request& request, + Http::AsyncClient::FailureReason reason) { + active_requests_.remove(request); + ENVOY_LOG(debug, "The OTLP export request failed. Reason {}", enumToInt(reason)); +} + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h b/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h new file mode 100644 index 0000000000000..a234533c2d10b --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h @@ -0,0 +1,51 @@ +#pragma once + +#include "envoy/config/core/v3/http_service.pb.h" +#include "envoy/server/factory_context.h" +#include "envoy/upstream/cluster_manager.h" + +#include "source/common/common/logger.h" +#include "source/common/http/async_client_impl.h" +#include "source/common/http/async_client_utility.h" +#include "source/common/http/http_service_headers.h" +#include "source/common/http/message_impl.h" +#include "source/common/http/utility.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +/** + * HTTP implementation of the OTLP trace exporter. + */ +class OtlpHttpTraceExporter : public OtlpTraceExporter, public Http::AsyncClient::Callbacks { +public: + OtlpHttpTraceExporter( + Upstream::ClusterManager& cluster_manager, + const envoy::config::core::v3::HttpService& http_service, + std::shared_ptr headers_applicator); + + bool log(const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) + override; + + // Http::AsyncClient::Callbacks. + void onSuccess(const Http::AsyncClient::Request&, Http::ResponseMessagePtr&&) override; + void onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason) override; + void onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) override {} + +private: + Upstream::ClusterManager& cluster_manager_; + envoy::config::core::v3::HttpService http_service_; + // Track active HTTP requests to be able to cancel them on destruction. + Http::AsyncClientRequestTracker active_requests_; + std::shared_ptr headers_applicator_; +}; + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/opentelemetry/otlp_utils.cc b/source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.cc similarity index 77% rename from source/extensions/tracers/opentelemetry/otlp_utils.cc rename to source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.cc index e60bc0ee3f86e..a686f92b1f27f 100644 --- a/source/extensions/tracers/opentelemetry/otlp_utils.cc +++ b/source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.cc @@ -1,18 +1,20 @@ -#include "source/extensions/tracers/opentelemetry/otlp_utils.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h" #include #include -#include "envoy/common/exception.h" +#include "source/common/common/assert.h" -#include "source/common/common/fmt.h" -#include "source/common/common/macros.h" -#include "source/common/version/version.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/proto/common/v1/common.pb.h" namespace Envoy { namespace Extensions { -namespace Tracers { namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +namespace { enum OTelAttributeType { KTypeBool, @@ -34,13 +36,10 @@ enum OTelAttributeType { KTypeSpanByte }; -const std::string& OtlpUtils::getOtlpUserAgentHeader() { - CONSTRUCT_ON_FIRST_USE(std::string, - fmt::format("OTel-OTLP-Exporter-Envoy/{}", Envoy::VersionInfo::version())); -} +} // namespace -void OtlpUtils::populateAnyValue(opentelemetry::proto::common::v1::AnyValue& value_proto, - const OTelAttribute& attribute_value) { +void PopulateAttributeUtils::populateAnyValue( + opentelemetry::proto::common::v1::AnyValue& value_proto, const OTelAttribute& attribute_value) { switch (attribute_value.index()) { case OTelAttributeType::KTypeBool: value_proto.set_bool_value(opentelemetry::nostd::get(attribute_value) ? true : false); @@ -90,7 +89,15 @@ void OtlpUtils::populateAnyValue(opentelemetry::proto::common::v1::AnyValue& val } } +KeyValue PopulateAttributeUtils::makeKeyValue(const std::string& key, const std::string& value) { + KeyValue key_value; + key_value.set_key(key); + key_value.mutable_value()->set_string_value(value); + return key_value; +} + +} // namespace Otlp +} // namespace Exporters } // namespace OpenTelemetry -} // namespace Tracers } // namespace Extensions } // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h b/source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h new file mode 100644 index 0000000000000..d653ed6a867ff --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "source/extensions/common/opentelemetry/types.h" + +#include "opentelemetry/proto/common/v1/common.pb.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +class PopulateAttributeUtils { +public: + static void populateAnyValue(opentelemetry::proto::common::v1::AnyValue& value_proto, + const OTelAttribute& attribute_value); + static KeyValue makeKeyValue(const std::string& key, const std::string& value); +}; + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h b/source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h new file mode 100644 index 0000000000000..3b1eeda8ab6d5 --- /dev/null +++ b/source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "source/common/common/logger.h" + +#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { + +class OtlpTraceExporter : public Logger::Loggable { +public: + virtual ~OtlpTraceExporter() = default; + + virtual bool + log(const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) = 0; + + void logExportedSpans( + const opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest& request) { + if (request.resource_spans(0).has_resource()) { + if (request.resource_spans(0).scope_spans(0).has_scope()) { + ENVOY_LOG(debug, "Number of exported spans: {}", + request.resource_spans(0).scope_spans(0).spans_size()); + } + } + } +}; + +using OtlpTraceExporterPtr = std::unique_ptr; + +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/sdk/common/BUILD b/source/extensions/common/opentelemetry/sdk/common/BUILD new file mode 100644 index 0000000000000..a4367779f737e --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/common/BUILD @@ -0,0 +1,23 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "sdk_common_types_lib", + hdrs = ["types.h"], + copts = [ + # Make sure that headers included from opentelemetry-api use Abseil from Envoy + # https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.14.0/api/BUILD#L32 + "-DHAVE_ABSEIL", + ], + deps = [ + "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/types:variant", + ], +) diff --git a/source/extensions/common/opentelemetry/sdk/common/types.h b/source/extensions/common/opentelemetry/sdk/common/types.h new file mode 100644 index 0000000000000..f129ac0c36639 --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/common/types.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/variant.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Sdk { +namespace Common { + +using AttributeValue = + absl::variant, std::vector, std::vector, + std::vector, std::vector, std::vector, + std::vector, uint64_t, std::vector, + std::vector>; + +using OwnedAttributeMap = std::map; + +} // namespace Common +} // namespace Sdk +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/sdk/logs/BUILD b/source/extensions/common/opentelemetry/sdk/logs/BUILD new file mode 100644 index 0000000000000..08cd7c5130cfa --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/logs/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +# OpenTelemetry SDK logs signal constants. +# Mirrors opentelemetry::sdk::logs from opentelemetry-cpp. +envoy_extension_package() + +envoy_cc_library( + name = "constants_lib", + hdrs = ["constants.h"], + deps = [ + "@abseil-cpp//absl/strings", + ], +) diff --git a/source/extensions/common/opentelemetry/sdk/logs/constants.h b/source/extensions/common/opentelemetry/sdk/logs/constants.h new file mode 100644 index 0000000000000..e6b23492335cf --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/logs/constants.h @@ -0,0 +1,23 @@ +#pragma once +// NOLINT(namespace-envoy) + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Sdk { +namespace Logs { + +struct Constants { + static constexpr absl::string_view kLogsServiceExportMethod = + "opentelemetry.proto.collector.logs.v1.LogsService.Export"; + + static constexpr absl::string_view kDefaultOtlpLogsEndpoint = "/v1/logs"; +}; + +} // namespace Logs +} // namespace Sdk +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/sdk/metrics/BUILD b/source/extensions/common/opentelemetry/sdk/metrics/BUILD new file mode 100644 index 0000000000000..43cbbebf7ffac --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/metrics/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +# OpenTelemetry SDK metrics signal constants. +# Mirrors opentelemetry::sdk::metrics from opentelemetry-cpp. +envoy_extension_package() + +envoy_cc_library( + name = "constants_lib", + hdrs = ["constants.h"], + deps = [ + "@abseil-cpp//absl/strings", + ], +) diff --git a/source/extensions/common/opentelemetry/sdk/metrics/constants.h b/source/extensions/common/opentelemetry/sdk/metrics/constants.h new file mode 100644 index 0000000000000..44e097ec47a2e --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/metrics/constants.h @@ -0,0 +1,23 @@ +#pragma once +// NOLINT(namespace-envoy) + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Sdk { +namespace Metrics { + +struct Constants { + static constexpr absl::string_view kMetricsServiceExportMethod = + "opentelemetry.proto.collector.metrics.v1.MetricsService.Export"; + + static constexpr absl::string_view kDefaultOtlpMetricsEndpoint = "/v1/metrics"; +}; + +} // namespace Metrics +} // namespace Sdk +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/sdk/trace/BUILD b/source/extensions/common/opentelemetry/sdk/trace/BUILD new file mode 100644 index 0000000000000..863b08d111e86 --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/trace/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +# OpenTelemetry SDK trace signal constants. +# Mirrors opentelemetry::sdk::trace from opentelemetry-cpp. +envoy_extension_package() + +envoy_cc_library( + name = "constants_lib", + hdrs = ["constants.h"], + deps = [ + "@abseil-cpp//absl/strings", + ], +) diff --git a/source/extensions/common/opentelemetry/sdk/trace/constants.h b/source/extensions/common/opentelemetry/sdk/trace/constants.h new file mode 100644 index 0000000000000..ee11913a7f010 --- /dev/null +++ b/source/extensions/common/opentelemetry/sdk/trace/constants.h @@ -0,0 +1,23 @@ +#pragma once +// NOLINT(namespace-envoy) + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Sdk { +namespace Trace { + +struct Constants { + static constexpr absl::string_view kTraceServiceExportMethod = + "opentelemetry.proto.collector.trace.v1.TraceService.Export"; + + static constexpr absl::string_view kDefaultOtlpTracesEndpoint = "/v1/traces"; +}; + +} // namespace Trace +} // namespace Sdk +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/common/opentelemetry/types.h b/source/extensions/common/opentelemetry/types.h new file mode 100644 index 0000000000000..3593d456bbb68 --- /dev/null +++ b/source/extensions/common/opentelemetry/types.h @@ -0,0 +1,26 @@ +#pragma once + +#include "source/extensions/common/opentelemetry/sdk/common/types.h" + +#include "opentelemetry/proto/common/v1/common.pb.h" +#include "opentelemetry/proto/trace/v1/trace.pb.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { + +/** + * @brief The type of the span. + * see + * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spankind + */ +using OTelSpanKind = ::opentelemetry::proto::trace::v1::Span::SpanKind; +using OTelAttribute = Sdk::Common::AttributeValue; +using OtelAttributes = Sdk::Common::OwnedAttributeMap; + +using KeyValue = ::opentelemetry::proto::common::v1::KeyValue; +using AnyValue = ::opentelemetry::proto::common::v1::AnyValue; + +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/stat_sinks/open_telemetry/BUILD b/source/extensions/stat_sinks/open_telemetry/BUILD index 7030fae629b9e..630c907b348bd 100644 --- a/source/extensions/stat_sinks/open_telemetry/BUILD +++ b/source/extensions/stat_sinks/open_telemetry/BUILD @@ -33,6 +33,8 @@ envoy_cc_library( "//source/common/common:matchers_lib", "//source/common/grpc:async_client_lib", "//source/common/stats:stat_match_input_lib", + "//source/extensions/common/opentelemetry:types_lib", + "//source/extensions/common/opentelemetry/sdk/metrics:constants_lib", "//source/extensions/tracers/opentelemetry/resource_detectors:resource_detector_lib", "@envoy_api//envoy/extensions/stat_sinks/open_telemetry/v3:pkg_cc_proto", "@opentelemetry-proto//:metrics_proto_cc", @@ -54,7 +56,7 @@ envoy_cc_library( "//source/common/http:message_lib", "//source/common/http:utility_lib", "//source/common/protobuf", - "//source/extensions/access_loggers/open_telemetry:otlp_log_utils_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:environment_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) @@ -66,6 +68,7 @@ envoy_cc_library( deps = [ "//source/common/common:assert_lib", "//source/common/protobuf", + "//source/extensions/common/opentelemetry/sdk/metrics:constants_lib", ], ) diff --git a/source/extensions/stat_sinks/open_telemetry/open_telemetry_http_impl.cc b/source/extensions/stat_sinks/open_telemetry/open_telemetry_http_impl.cc index 9bd3ad532ef42..aee579710c8b2 100644 --- a/source/extensions/stat_sinks/open_telemetry/open_telemetry_http_impl.cc +++ b/source/extensions/stat_sinks/open_telemetry/open_telemetry_http_impl.cc @@ -5,7 +5,7 @@ #include "source/common/http/message_impl.h" #include "source/common/http/utility.h" #include "source/common/protobuf/protobuf.h" -#include "source/extensions/access_loggers/open_telemetry/otlp_log_utils.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" namespace Envoy { namespace Extensions { @@ -44,7 +44,8 @@ void OpenTelemetryHttpMetricsExporter::send(MetricsExportRequestPtr&& metrics) { message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf); // User-Agent header follows the OTLP specification. - message->headers().setReferenceUserAgent(AccessLoggers::OpenTelemetry::getOtlpUserAgentHeader()); + message->headers().setReferenceUserAgent( + ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::GetUserAgent()); // Add custom headers from config. headers_applicator_->apply(message->headers()); diff --git a/source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.cc b/source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.cc index acaca0f104ae0..bfae69dca672f 100644 --- a/source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.cc +++ b/source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.cc @@ -1,6 +1,7 @@ #include "source/extensions/stat_sinks/open_telemetry/open_telemetry_impl.h" #include "source/common/tracing/null_span_impl.h" +#include "source/extensions/common/opentelemetry/sdk/metrics/constants.h" #include "source/extensions/stat_sinks/open_telemetry/stat_match_action.h" namespace Envoy { @@ -245,8 +246,8 @@ OpenTelemetryGrpcMetricsExporterImpl::OpenTelemetryGrpcMetricsExporterImpl( const OtlpOptionsSharedPtr config, Grpc::RawAsyncClientSharedPtr raw_async_client) : config_(config), client_(raw_async_client), service_method_(*Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - "opentelemetry.proto.collector.metrics.v1.MetricsService." - "Export")) {} + std::string(Envoy::Extensions::OpenTelemetry::Sdk::Metrics::Constants:: + kMetricsServiceExportMethod))) {} void OpenTelemetryGrpcMetricsExporterImpl::send(MetricsExportRequestPtr&& export_request) { ENVOY_LOG(debug, "sending a OTLP metric request: {}", export_request->DebugString()); diff --git a/source/extensions/stat_sinks/open_telemetry/open_telemetry_proto_descriptors.cc b/source/extensions/stat_sinks/open_telemetry/open_telemetry_proto_descriptors.cc index 881edabb94f17..0734f4381af99 100644 --- a/source/extensions/stat_sinks/open_telemetry/open_telemetry_proto_descriptors.cc +++ b/source/extensions/stat_sinks/open_telemetry/open_telemetry_proto_descriptors.cc @@ -2,6 +2,7 @@ #include "source/common/common/assert.h" #include "source/common/protobuf/protobuf.h" +#include "source/extensions/common/opentelemetry/sdk/metrics/constants.h" namespace Envoy { namespace Extensions { @@ -9,7 +10,8 @@ namespace StatSinks { namespace OpenTelemetry { void validateProtoDescriptors() { - const auto method = "opentelemetry.proto.collector.metrics.v1.MetricsService.Export"; + const auto method = std::string( + Envoy::Extensions::OpenTelemetry::Sdk::Metrics::Constants::kMetricsServiceExportMethod); RELEASE_ASSERT(Protobuf::DescriptorPool::generated_pool()->FindMethodByName(method) != nullptr, ""); diff --git a/source/extensions/tracers/opentelemetry/BUILD b/source/extensions/tracers/opentelemetry/BUILD index d0e33d85cf328..9d4453895272f 100644 --- a/source/extensions/tracers/opentelemetry/BUILD +++ b/source/extensions/tracers/opentelemetry/BUILD @@ -58,11 +58,6 @@ envoy_cc_library( envoy_cc_library( name = "trace_exporter", - srcs = [ - "grpc_trace_exporter.cc", - "http_trace_exporter.cc", - "otlp_utils.cc", - ], hdrs = [ "grpc_trace_exporter.h", "http_trace_exporter.h", @@ -70,22 +65,7 @@ envoy_cc_library( "trace_exporter.h", ], deps = [ - "//envoy/grpc:async_client_manager_interface", - "//envoy/server:factory_context_interface", - "//envoy/upstream:cluster_manager_interface", - "//source/common/grpc:typed_async_client_lib", - "//source/common/http:async_client_utility_lib", - "//source/common/http:header_map_lib", - "//source/common/http:http_service_headers_lib", - "//source/common/http:message_lib", - "//source/common/http:utility_lib", - "//source/common/protobuf", - "//source/common/tracing:trace_context_lib", - "//source/common/version:version_lib", - "//source/server:generic_factory_context_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - "@opentelemetry-cpp//api", - "@opentelemetry-proto//:trace_proto_cc", - "@opentelemetry-proto//:trace_service_proto_cc", + "//source/extensions/common/opentelemetry/exporters/otlp:populate_attribute_utils_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:trace_exporter_lib", ], ) diff --git a/source/extensions/tracers/opentelemetry/grpc_trace_exporter.cc b/source/extensions/tracers/opentelemetry/grpc_trace_exporter.cc index b5b528a6b926d..fafc4c928ff93 100644 --- a/source/extensions/tracers/opentelemetry/grpc_trace_exporter.cc +++ b/source/extensions/tracers/opentelemetry/grpc_trace_exporter.cc @@ -1,52 +1 @@ -#include "source/extensions/tracers/opentelemetry/grpc_trace_exporter.h" - -#include "source/common/common/logger.h" -#include "source/common/grpc/status.h" -#include "source/extensions/tracers/opentelemetry/otlp_utils.h" - -namespace Envoy { -namespace Extensions { -namespace Tracers { -namespace OpenTelemetry { - -OpenTelemetryGrpcTraceExporter::OpenTelemetryGrpcTraceExporter( - const Grpc::RawAsyncClientSharedPtr& client) - : client_(client), - service_method_(*Protobuf::DescriptorPool::generated_pool()->FindMethodByName( - "opentelemetry.proto.collector.trace.v1.TraceService.Export")) {} - -void OpenTelemetryGrpcTraceExporter::onCreateInitialMetadata(Http::RequestHeaderMap& metadata) { - metadata.setReferenceUserAgent(OtlpUtils::getOtlpUserAgentHeader()); -} - -void OpenTelemetryGrpcTraceExporter::onSuccess( - Grpc::ResponsePtr&& response, Tracing::Span&) { - if (response->has_partial_success()) { - auto msg = response->partial_success().error_message(); - auto rejected_spans = response->partial_success().rejected_spans(); - if (rejected_spans > 0 || !msg.empty()) { - if (msg.empty()) { - msg = "empty message"; - } - ENVOY_LOG(debug, "OTLP partial success: {} ({} spans rejected)", msg, rejected_spans); - } - } -} - -void OpenTelemetryGrpcTraceExporter::onFailure(Grpc::Status::GrpcStatus status, - const std::string& message, Tracing::Span&) { - ENVOY_LOG(debug, "OTLP trace export failed with status: {}, message: {}", - Grpc::Utility::grpcStatusToString(status), message); -} - -bool OpenTelemetryGrpcTraceExporter::log(const ExportTraceServiceRequest& request) { - client_->send(service_method_, request, *this, Tracing::NullSpan::instance(), - Http::AsyncClient::RequestOptions()); - OpenTelemetryTraceExporter::logExportedSpans(request); - return true; -} - -} // namespace OpenTelemetry -} // namespace Tracers -} // namespace Extensions -} // namespace Envoy +// NOLINT(namespace-envoy) diff --git a/source/extensions/tracers/opentelemetry/grpc_trace_exporter.h b/source/extensions/tracers/opentelemetry/grpc_trace_exporter.h index 521fb6b84d280..1756471a729e6 100644 --- a/source/extensions/tracers/opentelemetry/grpc_trace_exporter.h +++ b/source/extensions/tracers/opentelemetry/grpc_trace_exporter.h @@ -1,6 +1,6 @@ #pragma once -#include "source/common/grpc/typed_async_client.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/grpc_trace_exporter.h" #include "source/extensions/tracers/opentelemetry/trace_exporter.h" namespace Envoy { @@ -8,28 +8,8 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { -/** - * Exporter client for OTLP Traces. Provides abstraction on top of gRPC stream. - */ -class OpenTelemetryGrpcTraceExporter - : public OpenTelemetryTraceExporter, - public Grpc::AsyncRequestCallbacks { -public: - OpenTelemetryGrpcTraceExporter(const Grpc::RawAsyncClientSharedPtr& client); - ~OpenTelemetryGrpcTraceExporter() override = default; - - void onCreateInitialMetadata(Http::RequestHeaderMap& metadata) override; - - void onSuccess(Grpc::ResponsePtr&& response, Tracing::Span&) override; - - void onFailure(Grpc::Status::GrpcStatus status, const std::string& message, - Tracing::Span&) override; - - bool log(const ExportTraceServiceRequest& request) override; - - Grpc::AsyncClient client_; - const Protobuf::MethodDescriptor& service_method_; -}; +using OpenTelemetryGrpcTraceExporter = + ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::OtlpGrpcTraceExporter; } // namespace OpenTelemetry } // namespace Tracers diff --git a/source/extensions/tracers/opentelemetry/http_trace_exporter.cc b/source/extensions/tracers/opentelemetry/http_trace_exporter.cc index 43df4204e59e1..fafc4c928ff93 100644 --- a/source/extensions/tracers/opentelemetry/http_trace_exporter.cc +++ b/source/extensions/tracers/opentelemetry/http_trace_exporter.cc @@ -1,99 +1 @@ -#include "source/extensions/tracers/opentelemetry/http_trace_exporter.h" - -#include -#include -#include -#include - -#include "source/common/common/enum_to_int.h" -#include "source/common/common/logger.h" -#include "source/common/protobuf/utility.h" -#include "source/extensions/tracers/opentelemetry/otlp_utils.h" - -namespace Envoy { -namespace Extensions { -namespace Tracers { -namespace OpenTelemetry { - -OpenTelemetryHttpTraceExporter::OpenTelemetryHttpTraceExporter( - Upstream::ClusterManager& cluster_manager, - const envoy::config::core::v3::HttpService& http_service, - std::shared_ptr headers_applicator) - : cluster_manager_(cluster_manager), http_service_(http_service), - headers_applicator_(std::move(headers_applicator)) {} - -bool OpenTelemetryHttpTraceExporter::log(const ExportTraceServiceRequest& request) { - std::string request_body; - - const auto ok = request.SerializeToString(&request_body); - if (!ok) { - ENVOY_LOG(warn, "Error while serializing the binary proto ExportTraceServiceRequest."); - return false; - } - - const auto thread_local_cluster = - cluster_manager_.getThreadLocalCluster(http_service_.http_uri().cluster()); - if (thread_local_cluster == nullptr) { - ENVOY_LOG(error, "OTLP HTTP exporter failed: [cluster = {}] is not configured", - http_service_.http_uri().cluster()); - return false; - } - - Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_service_.http_uri()); - - // The request follows the OTLP HTTP specification: - // https://github.com/open-telemetry/opentelemetry-proto/blob/v1.0.0/docs/specification.md#otlphttp. - message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Post); - message->headers().setReferenceContentType(Http::Headers::get().ContentTypeValues.Protobuf); - - // User-Agent header follows the OTLP specification: - // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent - message->headers().setReferenceUserAgent(OtlpUtils::getOtlpUserAgentHeader()); - - // Add all custom headers to the request (static values set once; formatted values - // re-evaluated now so that runtime updates, e.g. SDS rotation, are reflected). - headers_applicator_->apply(message->headers()); - - message->body().add(request_body); - - const auto options = - Http::AsyncClient::RequestOptions() - .setTimeout(std::chrono::milliseconds( - DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout()))) - .setDiscardResponseBody(true); - - Http::AsyncClient::Request* in_flight_request = - thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options); - - OpenTelemetryTraceExporter::logExportedSpans(request); - - if (in_flight_request == nullptr) { - return false; - } - - active_requests_.add(*in_flight_request); - return true; -} - -void OpenTelemetryHttpTraceExporter::onSuccess(const Http::AsyncClient::Request& request, - Http::ResponseMessagePtr&& http_response) { - active_requests_.remove(request); - const auto response_code = Http::Utility::getResponseStatus(http_response->headers()); - if (response_code != enumToInt(Http::Code::OK)) { - ENVOY_LOG(error, - "OTLP HTTP exporter received a non-success status code: {} while exporting the OTLP " - "message", - response_code); - } -} - -void OpenTelemetryHttpTraceExporter::onFailure(const Http::AsyncClient::Request& request, - Http::AsyncClient::FailureReason reason) { - active_requests_.remove(request); - ENVOY_LOG(debug, "The OTLP export request failed. Reason {}", enumToInt(reason)); -} - -} // namespace OpenTelemetry -} // namespace Tracers -} // namespace Extensions -} // namespace Envoy +// NOLINT(namespace-envoy) diff --git a/source/extensions/tracers/opentelemetry/http_trace_exporter.h b/source/extensions/tracers/opentelemetry/http_trace_exporter.h index f8eb891b72a6d..4b3a63226873f 100644 --- a/source/extensions/tracers/opentelemetry/http_trace_exporter.h +++ b/source/extensions/tracers/opentelemetry/http_trace_exporter.h @@ -1,49 +1,15 @@ #pragma once -#include "envoy/config/core/v3/http_service.pb.h" -#include "envoy/server/factory_context.h" -#include "envoy/upstream/cluster_manager.h" - -#include "source/common/common/logger.h" -#include "source/common/http/async_client_impl.h" -#include "source/common/http/async_client_utility.h" -#include "source/common/http/http_service_headers.h" -#include "source/common/http/message_impl.h" -#include "source/common/http/utility.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/http_trace_exporter.h" #include "source/extensions/tracers/opentelemetry/trace_exporter.h" -#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" - namespace Envoy { namespace Extensions { namespace Tracers { namespace OpenTelemetry { -/** - * Exporter for OTLP traces over HTTP. - */ -class OpenTelemetryHttpTraceExporter : public OpenTelemetryTraceExporter, - public Http::AsyncClient::Callbacks { -public: - OpenTelemetryHttpTraceExporter( - Upstream::ClusterManager& cluster_manager, - const envoy::config::core::v3::HttpService& http_service, - std::shared_ptr headers_applicator); - - bool log(const ExportTraceServiceRequest& request) override; - - // Http::AsyncClient::Callbacks. - void onSuccess(const Http::AsyncClient::Request&, Http::ResponseMessagePtr&&) override; - void onFailure(const Http::AsyncClient::Request&, Http::AsyncClient::FailureReason) override; - void onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) override {} - -private: - Upstream::ClusterManager& cluster_manager_; - envoy::config::core::v3::HttpService http_service_; - // Track active HTTP requests to be able to cancel them on destruction. - Http::AsyncClientRequestTracker active_requests_; - std::shared_ptr headers_applicator_; -}; +using OpenTelemetryHttpTraceExporter = + ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::OtlpHttpTraceExporter; } // namespace OpenTelemetry } // namespace Tracers diff --git a/source/extensions/tracers/opentelemetry/otlp_utils.h b/source/extensions/tracers/opentelemetry/otlp_utils.h index ac6784722993a..799c863a0cf4b 100644 --- a/source/extensions/tracers/opentelemetry/otlp_utils.h +++ b/source/extensions/tracers/opentelemetry/otlp_utils.h @@ -1,68 +1,18 @@ #pragma once -#include -#include -#include - -#include "absl/strings/string_view.h" -#include "absl/types/variant.h" -#include "opentelemetry/common/attribute_value.h" -#include "opentelemetry/proto/common/v1/common.pb.h" -#include "opentelemetry/proto/trace/v1/trace.pb.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h" +#include "source/extensions/common/opentelemetry/types.h" namespace Envoy { namespace Extensions { namespace Tracers { namespace OpenTelemetry { -/** - * @brief The type of the span. - * see - * https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spankind - */ -using OTelSpanKind = ::opentelemetry::proto::trace::v1::Span::SpanKind; - -/** - * @brief Based on Open-telemetry OwnedAttributeValue - * see - * https://github.com/open-telemetry/opentelemetry-cpp/blob/main/sdk/include/opentelemetry/sdk/common/attribute_utils.h - */ -using OTelAttribute = - absl::variant, std::vector, std::vector, - std::vector, std::vector, std::vector, - std::vector, uint64_t, std::vector, - std::vector>; - -/** - * @brief Container holding Open-telemetry Attributes - */ -using OtelAttributes = std::map; - -/** - * Contains utility functions for Otel - */ -class OtlpUtils { - -public: - /** - * @brief Get the User-Agent header value to be used on the OTLP exporter request. - * - * The header value is compliant with the OpenTelemetry specification. See: - * https://github.com/open-telemetry/opentelemetry-specification/blob/v1.30.0/specification/protocol/exporter.md#user-agent - * @return std::string The User-Agent for the OTLP exporters in Envoy. - */ - static const std::string& getOtlpUserAgentHeader(); - - /** - * @brief Set the Otel attribute on a Proto Value object - * - * @param value_proto Proto object which gets the value set. - * @param attribute_value Value to set on the proto object. - */ - static void populateAnyValue(opentelemetry::proto::common::v1::AnyValue& value_proto, - const OTelAttribute& attribute_value); -}; +// Import shared types into the tracer namespace for backward compatibility. +using OTelSpanKind = ::Envoy::Extensions::OpenTelemetry::OTelSpanKind; +using OTelAttribute = ::Envoy::Extensions::OpenTelemetry::OTelAttribute; +using OtelAttributes = ::Envoy::Extensions::OpenTelemetry::OtelAttributes; +using OtlpUtils = ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::PopulateAttributeUtils; } // namespace OpenTelemetry } // namespace Tracers diff --git a/source/extensions/tracers/opentelemetry/trace_exporter.h b/source/extensions/tracers/opentelemetry/trace_exporter.h index f3b3a74cf4119..ee09871d69ed0 100644 --- a/source/extensions/tracers/opentelemetry/trace_exporter.h +++ b/source/extensions/tracers/opentelemetry/trace_exporter.h @@ -1,8 +1,6 @@ #pragma once -#include "source/common/common/logger.h" - -#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/trace_exporter.h" using opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest; using opentelemetry::proto::collector::trace::v1::ExportTraceServiceResponse; @@ -12,40 +10,10 @@ namespace Extensions { namespace Tracers { namespace OpenTelemetry { -/** - * @brief Base class for all OpenTelemetry Protocol (OTLP) exporters. - * @see - * https://github.com/open-telemetry/opentelemetry-proto/blob/v1.0.0/docs/specification.md#otlphttp - */ -class OpenTelemetryTraceExporter : public Logger::Loggable { -public: - virtual ~OpenTelemetryTraceExporter() = default; - - /** - * @brief Exports the trace request to the configured OTLP service. - * - * @param request The protobuf-encoded OTLP trace request. - * @return true When the request was sent. - * @return false When sending the request failed. - */ - virtual bool log(const ExportTraceServiceRequest& request) = 0; - - /** - * @brief Logs as debug the number of exported spans. - * - * @param request The protobuf-encoded OTLP trace request. - */ - void logExportedSpans(const ExportTraceServiceRequest& request) { - if (request.resource_spans(0).has_resource()) { - if (request.resource_spans(0).scope_spans(0).has_scope()) { - ENVOY_LOG(debug, "Number of exported spans: {}", - request.resource_spans(0).scope_spans(0).spans_size()); - } - } - } -}; - -using OpenTelemetryTraceExporterPtr = std::unique_ptr; +using OpenTelemetryTraceExporter = + ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::OtlpTraceExporter; +using OpenTelemetryTraceExporterPtr = + ::Envoy::Extensions::OpenTelemetry::Exporters::Otlp::OtlpTraceExporterPtr; } // namespace OpenTelemetry } // namespace Tracers diff --git a/test/extensions/common/opentelemetry/exporters/otlp/BUILD b/test/extensions/common/opentelemetry/exporters/otlp/BUILD new file mode 100644 index 0000000000000..d5faed61a0925 --- /dev/null +++ b/test/extensions/common/opentelemetry/exporters/otlp/BUILD @@ -0,0 +1,25 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "populate_attribute_utils_test", + srcs = ["populate_attribute_utils_test.cc"], + copts = [ + # Make sure that headers included from opentelemetry-api use Abseil from Envoy + # https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.14.0/api/BUILD#L32 + "-DHAVE_ABSEIL", + ], + rbe_pool = "6gig", + deps = [ + "//source/common/version:version_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:environment_lib", + "//source/extensions/common/opentelemetry/exporters/otlp:populate_attribute_utils_lib", + ], +) diff --git a/test/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils_test.cc b/test/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils_test.cc new file mode 100644 index 0000000000000..c8202c20d278b --- /dev/null +++ b/test/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils_test.cc @@ -0,0 +1,130 @@ +#include "source/common/version/version.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/environment.h" +#include "source/extensions/common/opentelemetry/exporters/otlp/populate_attribute_utils.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace OpenTelemetry { +namespace Exporters { +namespace Otlp { +namespace { + +// Tests for PopulateAttributeUtils::makeKeyValue +TEST(PopulateAttributeUtilsTest, MakeKeyValue) { + auto kv = PopulateAttributeUtils::makeKeyValue("my_key", "my_value"); + EXPECT_EQ("my_key", kv.key()); + EXPECT_EQ("my_value", kv.value().string_value()); +} + +TEST(PopulateAttributeUtilsTest, MakeKeyValueEmptyStrings) { + auto kv = PopulateAttributeUtils::makeKeyValue("", ""); + EXPECT_EQ("", kv.key()); + EXPECT_EQ("", kv.value().string_value()); +} + +// Tests for PopulateAttributeUtils::populateAnyValue — scalar types +TEST(PopulateAttributeUtilsTest, PopulateAnyValueBool) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{true}); + EXPECT_TRUE(proto.bool_value()); + + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{false}); + EXPECT_FALSE(proto.bool_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueInt32) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{static_cast(42)}); + EXPECT_EQ(42, proto.int_value()); + + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{static_cast(-7)}); + EXPECT_EQ(-7, proto.int_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueUInt32) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{static_cast(100)}); + EXPECT_EQ(100, proto.int_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueInt64) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, + OTelAttribute{static_cast(9876543210LL)}); + EXPECT_EQ(9876543210LL, proto.int_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueUInt64) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, + OTelAttribute{static_cast(12345678900ULL)}); + EXPECT_EQ(12345678900LL, proto.int_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueDouble) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{3.14}); + EXPECT_DOUBLE_EQ(3.14, proto.double_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueString) { + AnyValue proto; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{std::string("hello")}); + EXPECT_EQ("hello", proto.string_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueStringView) { + AnyValue proto; + const absl::string_view sv = "world"; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{sv}); + EXPECT_EQ("world", proto.string_value()); +} + +// Tests for PopulateAttributeUtils::populateAnyValue — array/span types +TEST(PopulateAttributeUtilsTest, PopulateAnyValueVectorOfStrings) { + AnyValue proto; + std::vector values = {"alpha", "beta", "gamma"}; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{values}); + ASSERT_EQ(3, proto.array_value().values_size()); + EXPECT_EQ("alpha", proto.array_value().values(0).string_value()); + EXPECT_EQ("beta", proto.array_value().values(1).string_value()); + EXPECT_EQ("gamma", proto.array_value().values(2).string_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueVectorOfStringViews) { + AnyValue proto; + std::vector values = {"x", "y"}; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{values}); + ASSERT_EQ(2, proto.array_value().values_size()); + EXPECT_EQ("x", proto.array_value().values(0).string_value()); + EXPECT_EQ("y", proto.array_value().values(1).string_value()); +} + +TEST(PopulateAttributeUtilsTest, PopulateAnyValueEmptyVectorOfStrings) { + AnyValue proto; + std::vector empty; + PopulateAttributeUtils::populateAnyValue(proto, OTelAttribute{empty}); + EXPECT_EQ(0, proto.array_value().values_size()); +} + +// Tests for GetUserAgent +TEST(EnvironmentTest, GetUserAgent) { + const std::string& agent = GetUserAgent(); + EXPECT_EQ(agent, "OTel-OTLP-Exporter-Envoy/" + Envoy::VersionInfo::version()); +} + +TEST(EnvironmentTest, GetUserAgentReturnsSameInstance) { + const std::string& agent1 = GetUserAgent(); + const std::string& agent2 = GetUserAgent(); + // CONSTRUCT_ON_FIRST_USE guarantees a singleton: same address. + EXPECT_EQ(&agent1, &agent2); +} + +} // namespace +} // namespace Otlp +} // namespace Exporters +} // namespace OpenTelemetry +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc b/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc index 74bba1d870373..746b096f84ea4 100644 --- a/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc +++ b/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc @@ -222,6 +222,35 @@ TEST_F(OpenTelemetryHttpTraceExporterTest, UnsuccessfulLogWithoutThreadLocalClus EXPECT_FALSE(trace_exporter_->log(export_trace_service_request)); } +TEST_F(OpenTelemetryHttpTraceExporterTest, UnsuccessfulLogWhenAsyncClientReturnsNullRequest) { + std::string yaml_string = fmt::format(R"EOF( + http_uri: + uri: "https://some-o11y.com/otlp/v1/traces" + cluster: "my_o11y_backend" + timeout: 0.250s + )EOF"); + + envoy::config::core::v3::HttpService http_service; + TestUtility::loadFromYaml(yaml_string, http_service); + setup(http_service); + + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, send_(_, _, _)) + .WillOnce(Return(nullptr)); + + opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest + export_trace_service_request; + opentelemetry::proto::trace::v1::Span span; + span.set_name("test"); + auto* resource_spans = export_trace_service_request.add_resource_spans(); + resource_spans->mutable_resource(); + auto* scope_spans = resource_spans->add_scope_spans(); + scope_spans->mutable_scope(); + *scope_spans->add_spans() = span; + + EXPECT_LOG_CONTAINS("debug", "Number of exported spans: 1", + EXPECT_FALSE(trace_exporter_->log(export_trace_service_request))); +} + } // namespace OpenTelemetry } // namespace Tracers } // namespace Extensions diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/BUILD b/test/extensions/tracers/opentelemetry/samplers/dynatrace/BUILD index 6fee86f079a0d..7b5e9f6c04d50 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/BUILD +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/BUILD @@ -45,6 +45,7 @@ envoy_extension_cc_test( "//test/test_common:utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/tracers/opentelemetry/samplers/v3:pkg_cc_proto", + "@opentelemetry-cpp//api", ], ) diff --git a/test/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_sampler_test.cc b/test/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_sampler_test.cc index 3610426a92280..20c35a1c00460 100644 --- a/test/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_sampler_test.cc +++ b/test/extensions/tracers/opentelemetry/samplers/dynatrace/dynatrace_sampler_test.cc @@ -14,6 +14,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "opentelemetry/nostd/variant.h" namespace Envoy { namespace Extensions {