From 3dedf6a023c42810383319d72b45037b426043cd Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 15:45:50 -0400 Subject: [PATCH 01/11] break up common test objects into signal specific files --- sdk/test/configuration/config_test_logs.h | 237 +++++++++++ sdk/test/configuration/config_test_metrics.h | 379 ++++++++++++++++++ .../configuration/config_test_propagators.h | 41 ++ sdk/test/configuration/config_test_trace.h | 208 ++++++++++ 4 files changed, 865 insertions(+) create mode 100644 sdk/test/configuration/config_test_logs.h create mode 100644 sdk/test/configuration/config_test_metrics.h create mode 100644 sdk/test/configuration/config_test_propagators.h create mode 100644 sdk/test/configuration/config_test_trace.h diff --git a/sdk/test/configuration/config_test_logs.h b/sdk/test/configuration/config_test_logs.h new file mode 100644 index 000000000..31a23c9f9 --- /dev/null +++ b/sdk/test/configuration/config_test_logs.h @@ -0,0 +1,237 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Logs-signal test helpers for SDK configuration tests. + +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/common/exporter_utils.h" +#include "opentelemetry/sdk/configuration/batch_log_record_processor_builder.h" +#include "opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/console_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_log_record_processor_builder.h" +#include "opentelemetry/sdk/configuration/extension_log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_file_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/read_write_log_record.h" +#include "opentelemetry/sdk/logs/simple_log_record_processor.h" + +namespace config_test +{ + +using LogRecordBuffer = std::vector>; + +// --------------------------------------------------------------------------- +// No-op logs helpers + +class NoopLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExporter +{ +public: + std::unique_ptr MakeRecordable() noexcept override + { + return std::make_unique(); + } + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::nostd::span> + &) noexcept override + { + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } +}; + +class NoopConsoleLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::ConsoleLogRecordExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsoleLogRecordExporterConfiguration *) + const override + { + return std::make_unique(); + } +}; + +class NoopLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionLogRecordExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionLogRecordExporterConfiguration *) + const override + { + return std::make_unique(); + } +}; + +// --------------------------------------------------------------------------- +// Recording logs helpers + +class RecordingLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExporter +{ +public: + explicit RecordingLogRecordExporter(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::make_unique(); + } + + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::nostd::span> + &records) noexcept override + { + for (auto &rec : records) + { + buffer_->emplace_back( + static_cast(rec.release())); + } + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } + +private: + std::shared_ptr buffer_; +}; + +class RecordingLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionLogRecordExporterBuilder +{ +public: + explicit RecordingLogRecordExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionLogRecordExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingConsoleLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::ConsoleLogRecordExporterBuilder +{ +public: + explicit RecordingConsoleLogRecordExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsoleLogRecordExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpHttpLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::OtlpHttpLogRecordExporterBuilder +{ +public: + explicit RecordingOtlpHttpLogRecordExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpHttpLogRecordExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpGrpcLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::OtlpGrpcLogRecordExporterBuilder +{ +public: + explicit RecordingOtlpGrpcLogRecordExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpGrpcLogRecordExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpFileLogRecordExporterBuilder + : public opentelemetry::sdk::configuration::OtlpFileLogRecordExporterBuilder +{ +public: + explicit RecordingOtlpFileLogRecordExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpFileLogRecordExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingLogRecordProcessorBuilder + : public opentelemetry::sdk::configuration::ExtensionLogRecordProcessorBuilder +{ +public: + mutable bool called{false}; + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionLogRecordProcessorConfiguration *) + const override + { + called = true; + return std::make_unique( + std::make_unique()); + } +}; + +// --------------------------------------------------------------------------- +// Mock batch processor builder: uses SimpleLogRecordProcessor for synchronous tests. + +class MockBatchLogRecordProcessorBuilder + : public opentelemetry::sdk::configuration::BatchLogRecordProcessorBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::BatchLogRecordProcessorConfiguration *, + std::unique_ptr &&exporter) const override + { + return std::make_unique( + std::move(exporter)); + } +}; + +} // namespace config_test diff --git a/sdk/test/configuration/config_test_metrics.h b/sdk/test/configuration/config_test_metrics.h new file mode 100644 index 000000000..97ebd187e --- /dev/null +++ b/sdk/test/configuration/config_test_metrics.h @@ -0,0 +1,379 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Metrics-signal test helpers for SDK configuration tests. + +#pragma once + +#include +#include +#include +#include + +#include "opentelemetry/sdk/common/exporter_utils.h" +#include "opentelemetry/sdk/configuration/console_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_pull_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_pull_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/periodic_metric_reader_builder.h" +#include "opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h" +#include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h" +#include "opentelemetry/sdk/metrics/data/metric_data.h" +#include "opentelemetry/sdk/metrics/export/metric_producer.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/metric_reader.h" +#include "opentelemetry/sdk/metrics/push_metric_exporter.h" + +namespace config_test +{ + +using MetricBuffer = std::vector; + +// --------------------------------------------------------------------------- +// No-op metrics helpers + +class NoopPushMetricExporter : public opentelemetry::sdk::metrics::PushMetricExporter +{ +public: + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::sdk::metrics::ResourceMetrics &) noexcept override + { + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( + opentelemetry::sdk::metrics::InstrumentType) const noexcept override + { + return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; + } + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } +}; + +class NoopConsolePushMetricExporterBuilder + : public opentelemetry::sdk::configuration::ConsolePushMetricExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsolePushMetricExporterConfiguration *) + const override + { + return std::make_unique(); + } +}; + +class NoopPushMetricExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionPushMetricExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionPushMetricExporterConfiguration *) + const override + { + return std::make_unique(); + } +}; + +class NoopMetricReader : public opentelemetry::sdk::metrics::MetricReader +{ +public: + opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( + opentelemetry::sdk::metrics::InstrumentType) const noexcept override + { + return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; + } + +private: + bool OnForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool OnShutDown(std::chrono::microseconds) noexcept override { return true; } +}; + +class NoopPullMetricExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionPullMetricExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionPullMetricExporterConfiguration *) + const override + { + return std::make_unique(); + } +}; + +class NoopPeriodicMetricReaderBuilder + : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *, + std::unique_ptr &&exporter) const override + { + auto unused = std::move(exporter); + return std::make_unique(); + } +}; + +// --------------------------------------------------------------------------- +// Recording metrics helpers + +class RecordingPushMetricExporter : public opentelemetry::sdk::metrics::PushMetricExporter +{ +public: + explicit RecordingPushMetricExporter(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::sdk::metrics::ResourceMetrics &resource_metrics) noexcept override + { + for (const auto &scope : resource_metrics.scope_metric_data_) + { + for (const auto &metric : scope.metric_data_) + { + buffer_->emplace_back(metric); + } + } + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( + opentelemetry::sdk::metrics::InstrumentType) const noexcept override + { + return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; + } + + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } + +private: + std::shared_ptr buffer_; +}; + +class RecordingPushMetricExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionPushMetricExporterBuilder +{ +public: + explicit RecordingPushMetricExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionPushMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingConsolePushMetricExporterBuilder + : public opentelemetry::sdk::configuration::ConsolePushMetricExporterBuilder +{ +public: + explicit RecordingConsolePushMetricExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsolePushMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpHttpPushMetricExporterBuilder + : public opentelemetry::sdk::configuration::OtlpHttpPushMetricExporterBuilder +{ +public: + explicit RecordingOtlpHttpPushMetricExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpHttpPushMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpGrpcPushMetricExporterBuilder + : public opentelemetry::sdk::configuration::OtlpGrpcPushMetricExporterBuilder +{ +public: + explicit RecordingOtlpGrpcPushMetricExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpGrpcPushMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpFilePushMetricExporterBuilder + : public opentelemetry::sdk::configuration::OtlpFilePushMetricExporterBuilder +{ +public: + explicit RecordingOtlpFilePushMetricExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpFilePushMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class SyncPullMetricReader : public opentelemetry::sdk::metrics::MetricReader +{ +public: + explicit SyncPullMetricReader(std::shared_ptr buffer) : buffer_(std::move(buffer)) + {} + opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( + opentelemetry::sdk::metrics::InstrumentType) const noexcept override + { + return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; + } + +private: + bool OnForceFlush(std::chrono::microseconds) noexcept override + { + return Collect([this](opentelemetry::sdk::metrics::ResourceMetrics &data) { + for (const auto &scope : data.scope_metric_data_) + for (const auto &metric : scope.metric_data_) + buffer_->emplace_back(metric); + return true; + }); + } + bool OnShutDown(std::chrono::microseconds) noexcept override { return true; } + + std::shared_ptr buffer_; +}; + +class RecordingPrometheusPullMetricExporterBuilder + : public opentelemetry::sdk::configuration::PrometheusPullMetricExporterBuilder +{ +public: + explicit RecordingPrometheusPullMetricExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::PrometheusPullMetricExporterConfiguration *) + const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +// --------------------------------------------------------------------------- +// Synchronous metric reader: collects and exports on ForceFlush in the calling thread. + +class SyncMetricReader : public opentelemetry::sdk::metrics::MetricReader +{ +public: + explicit SyncMetricReader( + std::unique_ptr exporter) + : exporter_(std::move(exporter)) + {} + + opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( + opentelemetry::sdk::metrics::InstrumentType instrument_type) const noexcept override + { + return exporter_->GetAggregationTemporality(instrument_type); + } + +private: + bool CollectAndExport() noexcept + { + return Collect([this](opentelemetry::sdk::metrics::ResourceMetrics &metric_data) { + return (exporter_->Export(metric_data) == opentelemetry::sdk::common::ExportResult::kSuccess); + }); + } + + bool OnForceFlush(std::chrono::microseconds timeout) noexcept override + { + return CollectAndExport() && exporter_->ForceFlush(timeout); + } + + bool OnShutDown(std::chrono::microseconds timeout) noexcept override + { + return exporter_->Shutdown(timeout); + } + + std::unique_ptr exporter_; +}; + +class SyncPeriodicMetricReaderBuilder + : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *, + std::unique_ptr &&exporter) const override + { + return std::make_unique(std::move(exporter)); + } +}; + +// --------------------------------------------------------------------------- +// Capturing periodic metric reader builder: records args passed to Build(). + +struct CapturedPeriodicReaderArgs +{ + std::size_t interval{0}; + std::size_t timeout{0}; + std::unique_ptr exporter; + bool called{false}; +}; + +class CapturingPeriodicMetricReaderBuilder + : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder +{ +public: + explicit CapturingPeriodicMetricReaderBuilder( + std::shared_ptr captured) + : captured_(std::move(captured)) + {} + + std::unique_ptr Build( + const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *model, + std::unique_ptr &&exporter) const override + { + captured_->called = true; + captured_->interval = model->interval; + captured_->timeout = model->timeout; + captured_->exporter = std::move(exporter); + return std::make_unique(); + } + +private: + std::shared_ptr captured_; +}; + +} // namespace config_test diff --git a/sdk/test/configuration/config_test_propagators.h b/sdk/test/configuration/config_test_propagators.h new file mode 100644 index 000000000..72a17364f --- /dev/null +++ b/sdk/test/configuration/config_test_propagators.h @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Propagator test helpers for SDK configuration tests. + +#pragma once + +#include +#include + +#include "opentelemetry/context/propagation/text_map_propagator.h" +#include "opentelemetry/nostd/string_view.h" + +namespace config_test +{ + +// --------------------------------------------------------------------------- +// TextMapCarrier for propagator tests. + +class MapCarrier : public opentelemetry::context::propagation::TextMapCarrier +{ +public: + opentelemetry::nostd::string_view Get( + opentelemetry::nostd::string_view key) const noexcept override + { + auto it = map_.find(std::string(key)); + return it != map_.end() ? opentelemetry::nostd::string_view(it->second) : ""; + } + void Set(opentelemetry::nostd::string_view key, + opentelemetry::nostd::string_view value) noexcept override + { + map_[std::string(key)] = std::string(value); + } + + const std::map &map() const { return map_; } + +private: + std::map map_; +}; + +} // namespace config_test diff --git a/sdk/test/configuration/config_test_trace.h b/sdk/test/configuration/config_test_trace.h new file mode 100644 index 000000000..6d8111378 --- /dev/null +++ b/sdk/test/configuration/config_test_trace.h @@ -0,0 +1,208 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Trace-signal test helpers for SDK configuration tests. + +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/common/exporter_utils.h" +#include "opentelemetry/sdk/configuration/batch_span_processor_builder.h" +#include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h" +#include "opentelemetry/sdk/configuration/console_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_file_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/span_data.h" + +namespace config_test +{ + +using SpanBuffer = std::vector>; + +// --------------------------------------------------------------------------- +// No-op trace helpers + +class NoopSpanExporter : public opentelemetry::sdk::trace::SpanExporter +{ +public: + std::unique_ptr MakeRecordable() noexcept override + { + return std::make_unique(); + } + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::nostd::span> + &) noexcept override + { + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } +}; + +class NoopConsoleSpanExporterBuilder + : public opentelemetry::sdk::configuration::ConsoleSpanExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsoleSpanExporterConfiguration *) const override + { + return std::make_unique(); + } +}; + +class NoopSpanExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionSpanExporterBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionSpanExporterConfiguration *) const override + { + return std::make_unique(); + } +}; + +// --------------------------------------------------------------------------- +// Recording trace helpers + +class RecordingSpanExporter : public opentelemetry::sdk::trace::SpanExporter +{ +public: + explicit RecordingSpanExporter(std::shared_ptr buffer) : buffer_(std::move(buffer)) {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::make_unique(); + } + + opentelemetry::sdk::common::ExportResult Export( + const opentelemetry::nostd::span> + &spans) noexcept override + { + for (auto &span : spans) + { + buffer_->emplace_back(static_cast(span.release())); + } + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds) noexcept override { return true; } + +private: + std::shared_ptr buffer_; +}; + +class RecordingSpanExporterBuilder + : public opentelemetry::sdk::configuration::ExtensionSpanExporterBuilder +{ +public: + explicit RecordingSpanExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ExtensionSpanExporterConfiguration *) const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingConsoleSpanExporterBuilder + : public opentelemetry::sdk::configuration::ConsoleSpanExporterBuilder +{ +public: + explicit RecordingConsoleSpanExporterBuilder(std::shared_ptr buffer) + : buffer_(std::move(buffer)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::ConsoleSpanExporterConfiguration *) const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpHttpSpanExporterBuilder + : public opentelemetry::sdk::configuration::OtlpHttpSpanExporterBuilder +{ +public: + explicit RecordingOtlpHttpSpanExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpHttpSpanExporterConfiguration *) const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpGrpcSpanExporterBuilder + : public opentelemetry::sdk::configuration::OtlpGrpcSpanExporterBuilder +{ +public: + explicit RecordingOtlpGrpcSpanExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpGrpcSpanExporterConfiguration *) const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +class RecordingOtlpFileSpanExporterBuilder + : public opentelemetry::sdk::configuration::OtlpFileSpanExporterBuilder +{ +public: + explicit RecordingOtlpFileSpanExporterBuilder(std::shared_ptr b) + : buffer_(std::move(b)) + {} + std::unique_ptr Build( + const opentelemetry::sdk::configuration::OtlpFileSpanExporterConfiguration *) const override + { + return std::make_unique(buffer_); + } + +private: + std::shared_ptr buffer_; +}; + +// --------------------------------------------------------------------------- +// Mock batch processor builder: uses SimpleSpanProcessor for synchronous tests. + +class MockBatchSpanProcessorBuilder + : public opentelemetry::sdk::configuration::BatchSpanProcessorBuilder +{ +public: + std::unique_ptr Build( + const opentelemetry::sdk::configuration::BatchSpanProcessorConfiguration *, + std::unique_ptr &&exporter) const override + { + return std::make_unique(std::move(exporter)); + } +}; + +} // namespace config_test From ae63f35f57b425f6254fb587dd71b3c6ba0ed8a5 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 15:47:22 -0400 Subject: [PATCH 02/11] update programmatic config test to use the separate signal test files and improve propogator coverage --- .../programmatic_configuration_test.cc | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/sdk/test/configuration/programmatic_configuration_test.cc b/sdk/test/configuration/programmatic_configuration_test.cc index b57b9fd05..c2647f060 100644 --- a/sdk/test/configuration/programmatic_configuration_test.cc +++ b/sdk/test/configuration/programmatic_configuration_test.cc @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #include - #include #include #include @@ -33,12 +32,6 @@ #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" -#include "opentelemetry/trace/context.h" -#include "opentelemetry/trace/noop.h" -#include "opentelemetry/trace/provider.h" -#include "opentelemetry/trace/span.h" -#include "opentelemetry/trace/tracer.h" -#include "opentelemetry/trace/tracer_provider.h" #include "opentelemetry/sdk/configuration/aggregation_configuration.h" #include "opentelemetry/sdk/configuration/always_off_sampler_configuration.h" @@ -99,8 +92,17 @@ #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/span_data.h" #include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/context.h" +#include "opentelemetry/trace/noop.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/trace/tracer_provider.h" -#include "config_test_common.h" +#include "config_test_logs.h" +#include "config_test_metrics.h" +#include "config_test_propagators.h" +#include "config_test_trace.h" namespace common = opentelemetry::common; namespace nostd = opentelemetry::nostd; @@ -961,3 +963,27 @@ TEST_F(ProgrammaticConfigTest, PropagatorsComposite) CheckPropagators(); } + +TEST_F(ProgrammaticConfigTest, PropagatorsDuplicateNames) +{ + // Duplicate names in composite + composite_list must each be registered only once. + auto propagator_config = std::make_unique(); + propagator_config->composite.emplace_back("tracecontext"); + propagator_config->composite.emplace_back("baggage"); + propagator_config->composite.emplace_back("b3"); + propagator_config->composite.emplace_back("b3multi"); + propagator_config->composite.emplace_back("jaeger"); + // tracecontext and baggage duplicated via composite_list — must be skipped + propagator_config->composite_list = "tracecontext,baggage"; + + auto model = std::make_unique(); + model->tracer_provider = MakeTracerProviderConfig(); + model->propagator = std::move(propagator_config); + + CreateAndInstallSdk(model); + ASSERT_NE(sdk_->propagator, nullptr); + + CheckPropagators(); +} + +// --------------------------------------------------------------------------- From 3a1375504bf7d5bc65597ac4dcf60f4efa1a56f9 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 15:48:25 -0400 Subject: [PATCH 03/11] update configured_sdk test to use the separate signal test files --- sdk/test/configuration/configured_sdk_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/test/configuration/configured_sdk_test.cc b/sdk/test/configuration/configured_sdk_test.cc index 2cc1f10de..0e35e3530 100644 --- a/sdk/test/configuration/configured_sdk_test.cc +++ b/sdk/test/configuration/configured_sdk_test.cc @@ -47,7 +47,9 @@ #include "opentelemetry/sdk/configuration/span_processor_configuration.h" #include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" -#include "config_test_common.h" +#include "config_test_logs.h" +#include "config_test_metrics.h" +#include "config_test_trace.h" namespace trace = opentelemetry::trace; namespace logs = opentelemetry::logs; From b55fd5ef5901ce1eacd8009da4f75c3fbc8b5501 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 15:50:42 -0400 Subject: [PATCH 04/11] move test cases from sdk_builder_test.cc to signal specific test files and expand test coverage --- sdk/test/configuration/config_test_logs.h | 4 + sdk/test/configuration/config_test_metrics.h | 4 + sdk/test/configuration/config_test_trace.h | 4 + sdk/test/configuration/logs_builders_test.cc | 465 +++++- .../configuration/metrics_builders_test.cc | 923 ++++++++++- sdk/test/configuration/sdk_builder_test.cc | 1408 ++++------------- sdk/test/configuration/trace_builders_test.cc | 1292 ++++++++++++++- 7 files changed, 2949 insertions(+), 1151 deletions(-) diff --git a/sdk/test/configuration/config_test_logs.h b/sdk/test/configuration/config_test_logs.h index 31a23c9f9..42a5002da 100644 --- a/sdk/test/configuration/config_test_logs.h +++ b/sdk/test/configuration/config_test_logs.h @@ -97,6 +97,10 @@ class RecordingLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExp const opentelemetry::nostd::span> &records) noexcept override { + if (!buffer_) + { + return opentelemetry::sdk::common::ExportResult::kFailure; + } for (auto &rec : records) { buffer_->emplace_back( diff --git a/sdk/test/configuration/config_test_metrics.h b/sdk/test/configuration/config_test_metrics.h index 97ebd187e..c5cb465c1 100644 --- a/sdk/test/configuration/config_test_metrics.h +++ b/sdk/test/configuration/config_test_metrics.h @@ -134,6 +134,10 @@ class RecordingPushMetricExporter : public opentelemetry::sdk::metrics::PushMetr opentelemetry::sdk::common::ExportResult Export( const opentelemetry::sdk::metrics::ResourceMetrics &resource_metrics) noexcept override { + if (!buffer_) + { + return opentelemetry::sdk::common::ExportResult::kFailure; + } for (const auto &scope : resource_metrics.scope_metric_data_) { for (const auto &metric : scope.metric_data_) diff --git a/sdk/test/configuration/config_test_trace.h b/sdk/test/configuration/config_test_trace.h index 6d8111378..64a057e75 100644 --- a/sdk/test/configuration/config_test_trace.h +++ b/sdk/test/configuration/config_test_trace.h @@ -91,6 +91,10 @@ class RecordingSpanExporter : public opentelemetry::sdk::trace::SpanExporter const opentelemetry::nostd::span> &spans) noexcept override { + if (!buffer_) + { + return opentelemetry::sdk::common::ExportResult::kFailure; + } for (auto &span : spans) { buffer_->emplace_back(static_cast(span.release())); diff --git a/sdk/test/configuration/logs_builders_test.cc b/sdk/test/configuration/logs_builders_test.cc index 59a73c2b0..90bd78694 100644 --- a/sdk/test/configuration/logs_builders_test.cc +++ b/sdk/test/configuration/logs_builders_test.cc @@ -2,30 +2,481 @@ // SPDX-License-Identifier: Apache-2.0 #include + +#include #include #include +#include +#include + +#include "opentelemetry/logs/logger.h" +#include "opentelemetry/logs/logger_provider.h" +#include "opentelemetry/logs/provider.h" +#include "opentelemetry/logs/severity.h" +#include "opentelemetry/sdk/configuration/batch_log_record_processor_builder.h" +#include "opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/configuration.h" +#include "opentelemetry/sdk/configuration/configured_sdk.h" +#include "opentelemetry/sdk/configuration/console_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_log_record_processor_builder.h" +#include "opentelemetry/sdk/configuration/extension_log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/logger_config_configuration.h" +#include "opentelemetry/sdk/configuration/logger_configurator_builder.h" +#include "opentelemetry/sdk/configuration/logger_configurator_configuration.h" +#include "opentelemetry/sdk/configuration/logger_matcher_and_config_configuration.h" +#include "opentelemetry/sdk/configuration/logger_provider_configuration.h" #include "opentelemetry/sdk/configuration/logs_builders.h" +#include "opentelemetry/sdk/configuration/otlp_file_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_log_record_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_log_record_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_log_record_exporter_configuration.h" #include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/sdk_builder.h" +#include "opentelemetry/sdk/configuration/severity_number.h" +#include "opentelemetry/sdk/configuration/simple_log_record_processor_builder.h" +#include "opentelemetry/sdk/configuration/simple_log_record_processor_configuration.h" +#include "opentelemetry/sdk/configuration/unsupported_exception.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/exporter.h" +#include "opentelemetry/sdk/logs/logger_config.h" +#include "opentelemetry/sdk/logs/logger_provider.h" +#include "opentelemetry/sdk/logs/processor.h" + +#include "config_test_logs.h" + +namespace logs = opentelemetry::logs; +namespace logs_sdk = opentelemetry::sdk::logs; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; +namespace config_sdk = opentelemetry::sdk::configuration; + +namespace +{ +class LogsBuildersTest : public ::testing::Test +{ +protected: + void SetUp() override + { + registry_ = std::make_shared(); + config_sdk::RegisterDefaultLogsBuilders(registry_.get()); + registry_->SetConsoleLogRecordBuilder( + std::make_unique()); + registry_->SetOtlpHttpLogRecordBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpGrpcLogRecordBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpFileLogRecordBuilder( + std::make_unique(nullptr)); + } -namespace configuration = opentelemetry::sdk::configuration; + std::unique_ptr MakeLogRecordProcessor( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateLogRecordProcessor(model); + } -TEST(LogsBuilders, EmptyRegistryHasNoLogsBuilders) + std::unique_ptr MakeLogRecordExporter( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateLogRecordExporter(model); + } + + auto MakeLoggerConfigurator(std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateLoggerConfigurator(model); + } + + std::shared_ptr registry_; +}; +} // namespace + +TEST_F(LogsBuildersTest, DefaultRegistry) { - auto registry = std::make_shared(); + auto registry = std::make_shared(); EXPECT_EQ(registry->GetBatchLogRecordProcessorBuilder(), nullptr); EXPECT_EQ(registry->GetSimpleLogRecordProcessorBuilder(), nullptr); EXPECT_EQ(registry->GetLoggerConfiguratorBuilder(), nullptr); } -TEST(LogsBuilders, RegisterDefaultLogsBuildersFillesAllLogsSlots) +TEST_F(LogsBuildersTest, RegisterDefaultLogsBuilders) { - auto registry = std::make_shared(); - - configuration::RegisterDefaultLogsBuilders(registry.get()); + auto registry = std::make_shared(); + config_sdk::RegisterDefaultLogsBuilders(registry.get()); EXPECT_NE(registry->GetBatchLogRecordProcessorBuilder(), nullptr); EXPECT_NE(registry->GetSimpleLogRecordProcessorBuilder(), nullptr); EXPECT_NE(registry->GetLoggerConfiguratorBuilder(), nullptr); } + +TEST_F(LogsBuildersTest, LoggerConfiguratorBuilder) +{ + config_sdk::LoggerConfigConfiguration default_config; + default_config.enabled = true; + default_config.minimum_severity = config_sdk::SeverityNumber::warn; + default_config.trace_based = false; + + config_sdk::LoggerMatcherAndConfigConfiguration matcher1; + matcher1.name = "enabled_minsev_error_not_trace_based"; + matcher1.config.enabled = true; + matcher1.config.minimum_severity = config_sdk::SeverityNumber::error3; + matcher1.config.trace_based = false; + + config_sdk::LoggerMatcherAndConfigConfiguration matcher2; + matcher2.name = "disabled_minsev_info_trace_based"; + matcher2.config.enabled = false; + matcher2.config.minimum_severity = config_sdk::SeverityNumber::debug; + matcher2.config.trace_based = true; + + auto model = std::make_unique(); + model->default_config = default_config; + model->loggers.push_back(matcher1); + model->loggers.push_back(matcher2); + + auto logger_configurator = MakeLoggerConfigurator(std::move(model)); + ASSERT_NE(logger_configurator, nullptr); + + auto default_scope = scope_sdk::InstrumentationScope::Create("default_scope"); + logs_sdk::LoggerConfig sdk_logger_config_default = + logger_configurator->ComputeConfig(*default_scope); + + auto scope_1 = scope_sdk::InstrumentationScope::Create(matcher1.name); + logs_sdk::LoggerConfig sdk_logger_config_1 = logger_configurator->ComputeConfig(*scope_1); + + auto scope_2 = scope_sdk::InstrumentationScope::Create(matcher2.name); + logs_sdk::LoggerConfig sdk_logger_config_2 = logger_configurator->ComputeConfig(*scope_2); + + EXPECT_TRUE(sdk_logger_config_default.IsEnabled()); + EXPECT_EQ(sdk_logger_config_default.GetMinimumSeverity(), logs::Severity::kWarn); + EXPECT_FALSE(sdk_logger_config_default.IsTraceBased()); + + EXPECT_TRUE(sdk_logger_config_1.IsEnabled()); + EXPECT_EQ(sdk_logger_config_1.GetMinimumSeverity(), logs::Severity::kError3); + EXPECT_FALSE(sdk_logger_config_1.IsTraceBased()); + + EXPECT_FALSE(sdk_logger_config_2.IsEnabled()); + EXPECT_EQ(sdk_logger_config_2.GetMinimumSeverity(), logs::Severity::kDebug); + EXPECT_TRUE(sdk_logger_config_2.IsTraceBased()); +} + +TEST_F(LogsBuildersTest, LoggerConfiguratorMinSeverity) +{ + struct TestCases + { + config_sdk::SeverityNumber input; + logs::Severity expected; + }; + + TestCases kCases[] = { + {config_sdk::SeverityNumber::trace, logs::Severity::kTrace}, + {config_sdk::SeverityNumber::trace2, logs::Severity::kTrace2}, + {config_sdk::SeverityNumber::trace3, logs::Severity::kTrace3}, + {config_sdk::SeverityNumber::trace4, logs::Severity::kTrace4}, + {config_sdk::SeverityNumber::debug, logs::Severity::kDebug}, + {config_sdk::SeverityNumber::debug2, logs::Severity::kDebug2}, + {config_sdk::SeverityNumber::debug3, logs::Severity::kDebug3}, + {config_sdk::SeverityNumber::debug4, logs::Severity::kDebug4}, + {config_sdk::SeverityNumber::info, logs::Severity::kInfo}, + {config_sdk::SeverityNumber::info2, logs::Severity::kInfo2}, + {config_sdk::SeverityNumber::info3, logs::Severity::kInfo3}, + {config_sdk::SeverityNumber::info4, logs::Severity::kInfo4}, + {config_sdk::SeverityNumber::warn, logs::Severity::kWarn}, + {config_sdk::SeverityNumber::warn2, logs::Severity::kWarn2}, + {config_sdk::SeverityNumber::warn3, logs::Severity::kWarn3}, + {config_sdk::SeverityNumber::warn4, logs::Severity::kWarn4}, + {config_sdk::SeverityNumber::error, logs::Severity::kError}, + {config_sdk::SeverityNumber::error2, logs::Severity::kError2}, + {config_sdk::SeverityNumber::error3, logs::Severity::kError3}, + {config_sdk::SeverityNumber::error4, logs::Severity::kError4}, + {config_sdk::SeverityNumber::fatal, logs::Severity::kFatal}, + {config_sdk::SeverityNumber::fatal2, logs::Severity::kFatal2}, + {config_sdk::SeverityNumber::fatal3, logs::Severity::kFatal3}, + {config_sdk::SeverityNumber::fatal4, logs::Severity::kFatal4}, + }; + + for (const auto &test_case : kCases) + { + auto model = std::make_unique(); + model->default_config.minimum_severity = test_case.input; + auto configurator = MakeLoggerConfigurator(std::move(model)); + ASSERT_NE(configurator, nullptr); + auto scope = scope_sdk::InstrumentationScope::Create("s"); + auto config = configurator->ComputeConfig(*scope); + EXPECT_EQ(config.GetMinimumSeverity(), test_case.expected); + } +} + +TEST_F(LogsBuildersTest, CreateBatchLogRecordProcessor) +{ + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + auto result = MakeLogRecordProcessor(std::move(model)); + EXPECT_NE(result, nullptr); +} + +TEST_F(LogsBuildersTest, CreateSimpleLogRecordProcessor) +{ + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + auto result = MakeLogRecordProcessor(std::move(model)); + EXPECT_NE(result, nullptr); +} + +TEST_F(LogsBuildersTest, UnregisteredLoggerConfigurator) +{ + registry_->SetLoggerConfiguratorBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeLoggerConfigurator(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredExtensionLogRecordExporter) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + std::unique_ptr model = std::move(ext); + EXPECT_THROW(MakeLogRecordExporter(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, RegisteredExtensionLogRecordExporterBuilder) +{ + auto buffer = std::make_shared(); + auto builder = std::make_unique(buffer); + registry_->SetExtensionLogRecordExporterBuilder("my_exporter", std::move(builder)); + + auto ext = std::make_unique(); + ext->name = "my_exporter"; + std::unique_ptr model = std::move(ext); + auto exporter = MakeLogRecordExporter(std::move(model)); + + EXPECT_NE(exporter, nullptr); +} + +TEST_F(LogsBuildersTest, UnregisteredExtensionLogRecordProcessorBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + std::unique_ptr model = std::move(ext); + EXPECT_THROW(MakeLogRecordProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, RegisteredExtensionLogRecordProcessorBuilder) +{ + auto builder = std::make_unique(); + auto *builder_ptr = builder.get(); + registry_->SetExtensionLogRecordProcessorBuilder("my_processor", std::move(builder)); + + auto ext = std::make_unique(); + ext->name = "my_processor"; + std::unique_ptr model = std::move(ext); + auto processor = MakeLogRecordProcessor(std::move(model)); + + EXPECT_NE(processor, nullptr); + EXPECT_TRUE(builder_ptr->called); +} + +TEST_F(LogsBuildersTest, UnregisteredBatchLogRecordProcessorBuilder) +{ + registry_->SetBatchLogRecordProcessorBuilder(nullptr); + + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + EXPECT_THROW(MakeLogRecordProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredSimpleLogRecordProcessorBuilder) +{ + registry_->SetSimpleLogRecordProcessorBuilder(nullptr); + + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + EXPECT_THROW(MakeLogRecordProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredConsoleLogRecordExporterBuilder) +{ + registry_->SetConsoleLogRecordBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeLogRecordExporter(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredOtlpHttpLogRecordExporterBuilder) +{ + registry_->SetOtlpHttpLogRecordBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeLogRecordExporter(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredOtlpGrpcLogRecordExporterBuilder) +{ + registry_->SetOtlpGrpcLogRecordBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeLogRecordExporter(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(LogsBuildersTest, UnregisteredOtlpFileLogRecordExporterBuilder) +{ + registry_->SetOtlpFileLogRecordBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeLogRecordExporter(std::move(model)), config_sdk::UnsupportedException); +} + +// --------------------------------------------------------------------------- +// LoggerProviderConfigTest: Use ConfiguredSdk::Create to verify logger provider configuration and +// installation. + +namespace +{ + +class LoggerProviderConfigTest : public ::testing::Test +{ +protected: + void SetUp() override + { + registry_ = std::make_shared(); + log_buffer_ = std::make_shared(); + config_sdk::RegisterDefaultLogsBuilders(registry_.get()); + } + + void TearDown() override + { + if (sdk_) + sdk_->UnInstall(); + } + + static std::unique_ptr MakeLoggerProviderConfig( + std::unique_ptr exporter) + { + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + return MakeLoggerProviderConfig(std::move(processor)); + } + + static std::unique_ptr MakeLoggerProviderConfig( + std::unique_ptr processor) + { + auto lp = std::make_unique(); + lp->processors.emplace_back(std::move(processor)); + auto model = std::make_unique(); + model->logger_provider = std::move(lp); + return model; + } + + void BuildAndInstall(const std::unique_ptr &model) + { + sdk_ = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk_, nullptr); + sdk_->Install(); + } + + void EmitAndVerify() + { + ASSERT_NE(sdk_->logger_provider, nullptr); + ASSERT_EQ(sdk_->tracer_provider, nullptr); + ASSERT_EQ(sdk_->meter_provider, nullptr); + + auto provider = logs::Provider::GetLoggerProvider(); + ASSERT_NE(provider, nullptr); + + provider->GetLogger("logger")->Info("logs"); + ASSERT_TRUE(sdk_->logger_provider->ForceFlush(std::chrono::milliseconds(5000))); + ASSERT_TRUE(sdk_->logger_provider->Shutdown(std::chrono::milliseconds(5000))); + EXPECT_EQ(log_buffer_->size(), 1u); + } + + std::shared_ptr registry_; + std::shared_ptr log_buffer_; + std::unique_ptr sdk_; +}; + +} // namespace + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithExtensionLogRecordExporter) +{ + registry_->SetExtensionLogRecordExporterBuilder( + "recording", std::make_unique(log_buffer_)); + + auto exporter = std::make_unique(); + exporter->name = "recording"; + auto model = MakeLoggerProviderConfig(std::move(exporter)); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithConsoleLogRecordExporter) +{ + registry_->SetConsoleLogRecordBuilder( + std::make_unique(log_buffer_)); + auto model = MakeLoggerProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithOtlpHttpLogRecordExporter) +{ + registry_->SetOtlpHttpLogRecordBuilder( + std::make_unique(log_buffer_)); + auto model = MakeLoggerProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithOtlpGrpcLogRecordExporter) +{ + registry_->SetOtlpGrpcLogRecordBuilder( + std::make_unique(log_buffer_)); + auto model = MakeLoggerProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithOtlpFileLogRecordExporter) +{ + registry_->SetOtlpFileLogRecordBuilder( + std::make_unique(log_buffer_)); + auto model = MakeLoggerProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(LoggerProviderConfigTest, LoggerProviderWithBatchLogRecordProcessor) +{ + registry_->SetBatchLogRecordProcessorBuilder( + std::make_unique()); + registry_->SetExtensionLogRecordExporterBuilder( + "recording", std::make_unique(log_buffer_)); + + auto exporter = std::make_unique(); + exporter->name = "recording"; + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + auto model = MakeLoggerProviderConfig(std::move(processor)); + BuildAndInstall(model); + EmitAndVerify(); +} diff --git a/sdk/test/configuration/metrics_builders_test.cc b/sdk/test/configuration/metrics_builders_test.cc index 92c7af681..21b318e16 100644 --- a/sdk/test/configuration/metrics_builders_test.cc +++ b/sdk/test/configuration/metrics_builders_test.cc @@ -2,28 +2,937 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include +#include #include #include +#include +#include +#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) +# include +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk/configuration/exemplar_filter.h" +# include "opentelemetry/test_common/sdk/common/scoped_test_log_handler.h" + +# if defined(_MSC_VER) +# include "opentelemetry/sdk/common/env_variables.h" +# endif +#else +# include +#endif + +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/meter_provider.h" +#include "opentelemetry/metrics/provider.h" +#include "opentelemetry/metrics/sync_instruments.h" +#include "opentelemetry/nostd/function_ref.h" +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/nostd/variant.h" + +#include "opentelemetry/sdk/configuration/aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" +#include "opentelemetry/sdk/configuration/configuration.h" +#include "opentelemetry/sdk/configuration/configured_sdk.h" +#include "opentelemetry/sdk/configuration/console_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/default_aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/drop_aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/extension_pull_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_pull_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/include_exclude_configuration.h" +#include "opentelemetry/sdk/configuration/instrument_type.h" +#include "opentelemetry/sdk/configuration/last_value_aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/meter_configurator_builder.h" +#include "opentelemetry/sdk/configuration/meter_configurator_configuration.h" +#include "opentelemetry/sdk/configuration/meter_provider_configuration.h" +#include "opentelemetry/sdk/configuration/metric_producer_configuration.h" +#include "opentelemetry/sdk/configuration/metric_reader_configuration.h" #include "opentelemetry/sdk/configuration/metrics_builders.h" +#include "opentelemetry/sdk/configuration/open_census_metric_producer_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/periodic_metric_reader_builder.h" +#include "opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h" +#include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_builder.h" +#include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/pull_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/pull_metric_reader_configuration.h" +#include "opentelemetry/sdk/configuration/push_metric_exporter_configuration.h" #include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/sdk_builder.h" +#include "opentelemetry/sdk/configuration/string_array_configuration.h" +#include "opentelemetry/sdk/configuration/sum_aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/unsupported_exception.h" +#include "opentelemetry/sdk/configuration/view_configuration.h" +#include "opentelemetry/sdk/configuration/view_selector_configuration.h" +#include "opentelemetry/sdk/configuration/view_stream_configuration.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/aggregation/aggregation.h" +#include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h" +#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +#include "opentelemetry/sdk/metrics/data/point_data.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/meter_config.h" +#include "opentelemetry/sdk/metrics/meter_provider.h" +#include "opentelemetry/sdk/metrics/metric_reader.h" +#include "opentelemetry/sdk/metrics/push_metric_exporter.h" +#include "opentelemetry/sdk/metrics/view/attributes_processor.h" +#include "opentelemetry/sdk/metrics/view/view.h" +#include "opentelemetry/sdk/metrics/view/view_registry.h" +#include "opentelemetry/sdk/resource/resource.h" + +#include "config_test_metrics.h" -namespace configuration = opentelemetry::sdk::configuration; +namespace metrics = opentelemetry::metrics; +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; +namespace config_sdk = opentelemetry::sdk::configuration; -TEST(MetricsBuilders, EmptyRegistryHasNoMetricsBuilders) +namespace { - auto registry = std::make_shared(); +class MetricsBuilderTest : public ::testing::Test +{ +protected: + void SetUp() override + { + registry_ = std::make_shared(); + config_sdk::RegisterDefaultMetricsBuilders(registry_.get()); + registry_->SetConsolePushMetricExporterBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpHttpPushMetricExporterBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpGrpcPushMetricExporterBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpFilePushMetricExporterBuilder( + std::make_unique(nullptr)); + registry_->SetPrometheusPullMetricExporterBuilder( + std::make_unique(nullptr)); + } + + std::unique_ptr MakeMetricReader( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateMetricReader(model); + } + + static std::unique_ptr MakeViewWithAggregation( + std::unique_ptr aggregation) + { + auto model = std::make_unique(); + model->selector = std::make_unique(); + model->selector->instrument_type = config_sdk::InstrumentType::none; + model->stream = std::make_unique(); + model->stream->aggregation = std::move(aggregation); + return model; + } + + static std::unique_ptr MakeCardinalityOnlyViewConfig( + config_sdk::InstrumentType instrument_type, + std::size_t cardinality_limit) + { + auto model = std::make_unique(); + model->selector = std::make_unique(); + model->selector->instrument_type = instrument_type; + + model->stream = std::make_unique(); + model->stream->aggregation_cardinality_limit = cardinality_limit; + + return model; + } + + std::unique_ptr> MakeMeterConfigurator( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateMeterConfigurator(model); + } + + void AddView(metrics_sdk::ViewRegistry *view_registry, + const std::unique_ptr &model) + { + config_sdk::SdkBuilder builder(registry_); + builder.AddView(view_registry, model); + } + + std::unique_ptr CreateAttributesProcessor( + const std::unique_ptr &model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateAttributesProcessor(model); + } + + std::unique_ptr MakeMeterProvider( + std::unique_ptr model, + const opentelemetry::sdk::resource::Resource &resource) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateMeterProvider(model, resource); + } + + void CheckInstrumentType( + config_sdk::InstrumentType config_type, + metrics_sdk::InstrumentType sdk_type, + metrics_sdk::AggregationType expected_aggregation = metrics_sdk::AggregationType::kDefault) + { + auto model = MakeCardinalityOnlyViewConfig(config_type, 7); + + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + metrics_sdk::InstrumentDescriptor descriptor{"test.instrument", "test description", "units", + sdk_type, metrics_sdk::InstrumentValueType::kLong}; + auto scope = scope_sdk::InstrumentationScope::Create(""); + + int matched = 0; + view_registry.FindViews(descriptor, *scope, [&](const metrics_sdk::View &view) { + matched++; + auto *config = view.GetAggregationConfig(); + EXPECT_NE(config, nullptr); + if (config) + { + EXPECT_EQ(config->GetType(), expected_aggregation); + EXPECT_EQ(config->cardinality_limit_, 7u); + } + return true; + }); + EXPECT_EQ(matched, 1); + } + + void CheckAggregationType(std::unique_ptr aggregation, + metrics_sdk::AggregationType expected_type) + { + auto model = MakeViewWithAggregation(std::move(aggregation)); + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + auto scope = scope_sdk::InstrumentationScope::Create(""); + metrics_sdk::InstrumentDescriptor descriptor{"m", "", "", metrics_sdk::InstrumentType::kCounter, + metrics_sdk::InstrumentValueType::kLong}; + int matched = 0; + view_registry.FindViews(descriptor, *scope, [&](const metrics_sdk::View &view) { + EXPECT_EQ(view.GetAggregationType(), expected_type); + matched++; + return true; + }); + EXPECT_EQ(matched, 1); + } + + std::shared_ptr registry_; +}; +} // namespace + +TEST_F(MetricsBuilderTest, DefaultRegistry) +{ + auto registry = std::make_shared(); EXPECT_EQ(registry->GetPeriodicMetricReaderBuilder(), nullptr); EXPECT_EQ(registry->GetMeterConfiguratorBuilder(), nullptr); } -TEST(MetricsBuilders, RegisterDefaultMetricsBuildersFillesAllMetricsSlots) +TEST_F(MetricsBuilderTest, RegisterDefaultMetricsBuilders) { - auto registry = std::make_shared(); - - configuration::RegisterDefaultMetricsBuilders(registry.get()); + auto registry = std::make_shared(); + config_sdk::RegisterDefaultMetricsBuilders(registry.get()); EXPECT_NE(registry->GetPeriodicMetricReaderBuilder(), nullptr); EXPECT_NE(registry->GetMeterConfiguratorBuilder(), nullptr); } + +#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) + +namespace +{ +constexpr char kMetricsExemplarFilterEnv[] = "OTEL_METRICS_EXEMPLAR_FILTER"; + +# if defined(_MSC_VER) +using opentelemetry::sdk::common::setenv; +using opentelemetry::sdk::common::unsetenv; +# endif +} // namespace + +TEST_F(MetricsBuilderTest, DeclarativeExemplarFilterDoesNotReadEnvironment) +{ + unsetenv(kMetricsExemplarFilterEnv); + opentelemetry::test_common::ScopedTestLogHandler log_handler{ + opentelemetry::sdk::common::internal_log::LogLevel::Warning}; + setenv(kMetricsExemplarFilterEnv, "invalid", 1); + + auto model = std::make_unique(); + model->exemplar_filter = config_sdk::ExemplarFilter::always_on; + + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto provider = MakeMeterProvider(std::move(model), resource); + ASSERT_NE(provider, nullptr); + EXPECT_TRUE(log_handler.Drain().empty()); + unsetenv(kMetricsExemplarFilterEnv); +} +#endif + +TEST_F(MetricsBuilderTest, AggregationTypeDefault) +{ + CheckAggregationType(std::make_unique(), + metrics_sdk::AggregationType::kDefault); +} + +TEST_F(MetricsBuilderTest, AggregationTypeSumAggregation) +{ + CheckAggregationType(std::make_unique(), + metrics_sdk::AggregationType::kSum); +} + +TEST_F(MetricsBuilderTest, AggregationTypeLastValueAggregation) +{ + CheckAggregationType(std::make_unique(), + metrics_sdk::AggregationType::kLastValue); +} + +TEST_F(MetricsBuilderTest, AggregationTypeDropAggregation) +{ + CheckAggregationType(std::make_unique(), + metrics_sdk::AggregationType::kDrop); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeCounter) +{ + CheckInstrumentType(config_sdk::InstrumentType::counter, metrics_sdk::InstrumentType::kCounter); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeUpDownCounter) +{ + CheckInstrumentType(config_sdk::InstrumentType::up_down_counter, + metrics_sdk::InstrumentType::kUpDownCounter); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeObservableCounter) +{ + CheckInstrumentType(config_sdk::InstrumentType::observable_counter, + metrics_sdk::InstrumentType::kObservableCounter); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeObservableGauge) +{ + CheckInstrumentType(config_sdk::InstrumentType::observable_gauge, + metrics_sdk::InstrumentType::kObservableGauge); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeObservableUpDownCounter) +{ + CheckInstrumentType(config_sdk::InstrumentType::observable_up_down_counter, + metrics_sdk::InstrumentType::kObservableUpDownCounter); +} + +TEST_F(MetricsBuilderTest, InstrumentTypeHistogram) +{ + CheckInstrumentType(config_sdk::InstrumentType::histogram, + metrics_sdk::InstrumentType::kHistogram, + metrics_sdk::AggregationType::kHistogram); +} + +#if OPENTELEMETRY_ABI_VERSION_NO < 2 +// No CheckInstrumentType test for gauge: gauge is unsupported in ABI v1 and throws instead. +TEST_F(MetricsBuilderTest, InstrumentTypeGaugeABIv1) +{ + auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::gauge, 42); + metrics_sdk::ViewRegistry view_registry; + EXPECT_THROW(AddView(&view_registry, model), config_sdk::UnsupportedException); +} +#else +TEST_F(MetricsBuilderTest, InstrumentTypeGaugeABIv2) +{ + CheckInstrumentType(config_sdk::InstrumentType::gauge, metrics_sdk::InstrumentType::kGauge); +} +#endif + +TEST_F(MetricsBuilderTest, ViewSelectorInstrumentTypeNone) +{ + namespace metrics_sdk = metrics_sdk; + + auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::none, 42); + + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); + std::vector supported_instrument_types{ + metrics_sdk::InstrumentType::kCounter, + metrics_sdk::InstrumentType::kHistogram, + metrics_sdk::InstrumentType::kUpDownCounter, + metrics_sdk::InstrumentType::kObservableCounter, + metrics_sdk::InstrumentType::kObservableGauge, + metrics_sdk::InstrumentType::kObservableUpDownCounter}; +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + supported_instrument_types.push_back(metrics_sdk::InstrumentType::kGauge); +#endif + + for (auto instrument_type : supported_instrument_types) + { + metrics_sdk::InstrumentDescriptor instrument_descriptor{ + "test.instrument", "test description", "units", instrument_type, + metrics_sdk::InstrumentValueType::kLong}; + int matched = 0; + view_registry.FindViews(instrument_descriptor, *instrumentation_scope, + [&](const metrics_sdk::View &view) { + auto *config = view.GetAggregationConfig(); + EXPECT_NE(config, nullptr); + if (config != nullptr) + { + EXPECT_EQ(config->cardinality_limit_, 42u); + matched++; + } + return true; + }); + EXPECT_EQ(matched, 1); + } +} + +TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramCardinalityLimit) +{ + // Verify that AddView populates default bucket boundaries on a cardinality-only + // histogram view, rather than leaving boundaries_ empty (which would produce a + // single-bucket histogram instead of the spec's 15-bucket default). + auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::histogram, 42); + + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + metrics_sdk::InstrumentDescriptor instrument_descriptor{ + "test.instrument", "test description", "units", metrics_sdk::InstrumentType::kHistogram, + metrics_sdk::InstrumentValueType::kLong}; + auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); + + view_registry.FindViews( + instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { + auto *aggregation_config = view.GetAggregationConfig(); + EXPECT_NE(aggregation_config, nullptr); + if (!aggregation_config) + return true; + auto aggregation = metrics_sdk::DefaultAggregation::CreateAggregation( + metrics_sdk::AggregationType::kHistogram, instrument_descriptor, aggregation_config); + EXPECT_NE(aggregation, nullptr); + if (!aggregation) + return true; + auto histogram_data = + opentelemetry::nostd::get(aggregation->ToPoint()); + EXPECT_EQ(histogram_data.boundaries_.size(), 15u); + EXPECT_EQ(histogram_data.counts_.size(), 16u); + return true; + }); +} + +TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramExplicitAggregation) +{ + auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::histogram, 42); + auto aggregation = + std::make_unique(); + aggregation->boundaries = {1.0, 2.0}; + model->stream->aggregation = std::move(aggregation); + + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + metrics_sdk::InstrumentDescriptor instrument_descriptor{ + "test.instrument", "test description", "units", metrics_sdk::InstrumentType::kHistogram, + metrics_sdk::InstrumentValueType::kLong}; + auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); + + int matched = 0; + view_registry.FindViews( + instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { + ++matched; + auto *aggregation_config = view.GetAggregationConfig(); + EXPECT_NE(aggregation_config, nullptr); + if (aggregation_config) + { + EXPECT_EQ(aggregation_config->GetType(), metrics_sdk::AggregationType::kHistogram); + EXPECT_EQ(aggregation_config->cardinality_limit_, 42u); + auto *histogram_config = + static_cast(aggregation_config); + EXPECT_EQ(histogram_config->boundaries_, (std::vector{1.0, 2.0})); + } + return true; + }); + + EXPECT_EQ(matched, 1); +} + +TEST_F(MetricsBuilderTest, ViewSelectorWithAttributesProcessor) +{ + + auto model = std::make_unique(); + model->selector = std::make_unique(); + model->selector->instrument_type = config_sdk::InstrumentType::counter; + model->stream = std::make_unique(); + model->stream->attribute_keys = std::make_unique(); + model->stream->attribute_keys->included = + std::make_unique(); + model->stream->attribute_keys->included->string_array = {"allowed"}; + + metrics_sdk::ViewRegistry view_registry; + AddView(&view_registry, model); + + auto scope = scope_sdk::InstrumentationScope::Create(""); + metrics_sdk::InstrumentDescriptor descriptor{"m", "", "", metrics_sdk::InstrumentType::kCounter, + metrics_sdk::InstrumentValueType::kLong}; + + std::map attributes = {{"allowed", 1}, {"filtered_out", 2}}; + opentelemetry::common::KeyValueIterableView> attr_view{attributes}; + + view_registry.FindViews(descriptor, *scope, [&](const metrics_sdk::View &view) { + auto processor = view.GetAttributesProcessor(); + EXPECT_NE(processor, nullptr); + if (!processor) + return true; + auto result = processor->process(attr_view); + EXPECT_EQ(result.size(), 1u); + EXPECT_NE(result.find("allowed"), result.end()); + EXPECT_EQ(result.find("filtered_out"), result.end()); + return true; + }); +} + +TEST_F(MetricsBuilderTest, CreateBuiltInPeriodicMetricReader) +{ + auto model = std::make_unique(); + model->interval = 2000; + model->timeout = 500; + + const auto *builder = registry_->GetPeriodicMetricReaderBuilder(); + ASSERT_NE(builder, nullptr); + auto reader = + builder->Build(model.get(), std::make_unique()); + ASSERT_NE(reader, nullptr); + EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); +} + +TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithExtensionExporter) +{ + registry_->SetExtensionPullMetricExporterBuilder( + "test_pull", std::make_unique()); + + auto exporter_config = std::make_unique(); + exporter_config->name = "test_pull"; + auto model = std::make_unique(); + model->exporter = std::move(exporter_config); + std::unique_ptr reader_config = std::move(model); + + auto reader = MakeMetricReader(std::move(reader_config)); + ASSERT_NE(reader, nullptr); + EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); +} + +TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithProducer) +{ + registry_->SetExtensionPullMetricExporterBuilder( + "test_pull", std::make_unique()); + + auto exporter_config = std::make_unique(); + exporter_config->name = "test_pull"; + auto model = std::make_unique(); + model->exporter = std::move(exporter_config); + model->producers.push_back(std::make_unique()); + std::unique_ptr reader_config = std::move(model); + + auto reader = MakeMetricReader(std::move(reader_config)); + ASSERT_NE(reader, nullptr); + EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); +} + +TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithCardinalityLimits) +{ + registry_->SetExtensionPullMetricExporterBuilder( + "test_pull", std::make_unique()); + + auto exporter_config = std::make_unique(); + exporter_config->name = "test_pull"; + auto model = std::make_unique(); + model->exporter = std::move(exporter_config); + model->cardinality_limits = std::make_unique(); + model->cardinality_limits->counter = 42; + std::unique_ptr reader_config = std::move(model); + + auto reader = MakeMetricReader(std::move(reader_config)); + ASSERT_NE(reader, nullptr); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kCounter), 42u); + EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); +} + +TEST_F(MetricsBuilderTest, CreatePeriodicMetricReader) +{ + auto exporter = std::make_unique(); + exporter->name = "noop"; + + auto model = std::make_unique(); + model->exporter = std::move(exporter); + model->interval = 12345; + model->timeout = 678; + model->cardinality_limits = std::make_unique(); + model->cardinality_limits->default_limit = 100; + model->cardinality_limits->counter = 200; + model->cardinality_limits->gauge = 300; + model->cardinality_limits->histogram = 400; + model->cardinality_limits->observable_counter = 500; + model->cardinality_limits->observable_gauge = 600; + model->cardinality_limits->observable_up_down_counter = 700; + model->cardinality_limits->up_down_counter = 800; + + const auto captured = std::make_shared(); + const auto expected_interval = model->interval; + const auto expected_timeout = model->timeout; + + registry_->SetExtensionPushMetricExporterBuilder( + "noop", std::make_unique()); + registry_->SetPeriodicMetricReaderBuilder( + std::make_unique(captured)); + + std::unique_ptr reader_config = std::move(model); + auto reader = MakeMetricReader(std::move(reader_config)); + ASSERT_NE(reader, nullptr); + + EXPECT_TRUE(captured->called); + EXPECT_EQ(captured->interval, expected_interval); + EXPECT_EQ(captured->timeout, expected_timeout); + EXPECT_TRUE(captured->exporter != nullptr); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kCounter), 200u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kGauge), 300u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kHistogram), 400u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kObservableCounter), 500u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kObservableGauge), 600u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kObservableUpDownCounter), + 700u); + EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kUpDownCounter), 800u); +} + +TEST_F(MetricsBuilderTest, CreateAttributesProcessor) +{ + std::map attributes = {{"included", 1}, {"excluded", 2}, {"unlisted", 3}}; + opentelemetry::common::KeyValueIterableView> iterable(attributes); + + // When both lists are configured, exclusion takes precedence over inclusion. + { + auto model = std::make_unique(); + model->included = std::make_unique(); + model->included->string_array = {"included", "excluded"}; + model->excluded = std::make_unique(); + model->excluded->string_array = {"excluded"}; + + auto processor = CreateAttributesProcessor(model); + ASSERT_NE(processor, nullptr); + auto filtered = processor->process(iterable); + + EXPECT_EQ(filtered.size(), 1u); + EXPECT_NE(filtered.find("included"), filtered.end()); + } + + // Wildcard patterns are evaluated per key, with exclusion taking precedence. + { + std::map wildcard_attributes = { + {"foo.bar", 1}, {"foo.baz", 2}, {"question.x", 3}, {"question.xy", 4}, {"other", 5}}; + opentelemetry::common::KeyValueIterableView> wildcard_iterable( + wildcard_attributes); + + auto model = std::make_unique(); + model->included = std::make_unique(); + model->included->string_array = {"foo.*", "question.?"}; + model->excluded = std::make_unique(); + model->excluded->string_array = {"foo.bar"}; + + auto processor = CreateAttributesProcessor(model); + ASSERT_NE(processor, nullptr); + auto filtered = processor->process(wildcard_iterable); + + EXPECT_EQ(filtered.size(), 2u); + EXPECT_NE(filtered.find("foo.baz"), filtered.end()); + EXPECT_NE(filtered.find("question.x"), filtered.end()); + } + + // An exclude-only configuration retains every key that is not excluded. + { + auto model = std::make_unique(); + model->excluded = std::make_unique(); + model->excluded->string_array = {"excluded"}; + + auto processor = CreateAttributesProcessor(model); + ASSERT_NE(processor, nullptr); + auto filtered = processor->process(iterable); + + EXPECT_EQ(filtered.size(), 2u); + EXPECT_EQ(filtered.find("excluded"), filtered.end()); + } + + // A null include/exclude block leaves attributes unchanged. + { + auto model = std::make_unique(); + auto processor = CreateAttributesProcessor(model); + ASSERT_NE(processor, nullptr); + auto filtered = processor->process(iterable); + + EXPECT_EQ(filtered.size(), attributes.size()); + } + + // An empty include list leaves attributes unchanged. + { + auto model = std::make_unique(); + model->included = std::make_unique(); + model->excluded = std::make_unique(); + + auto processor = CreateAttributesProcessor(model); + ASSERT_NE(processor, nullptr); + auto filtered = processor->process(iterable); + + EXPECT_EQ(filtered.size(), attributes.size()); + } +} + +// --------------------------------------------------------------------------- +// MetricsBuilderTest: Verify that unregistered builders throw UnsupportedException. + +TEST_F(MetricsBuilderTest, UnregisteredMeterConfiguratorBuilder) +{ + registry_->SetMeterConfiguratorBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeMeterConfigurator(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredExtensionPushMetricExporterBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + auto reader = std::make_unique(); + reader->exporter = std::move(ext); + std::unique_ptr model = std::move(reader); + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredExtensionPullMetricExporterBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + auto reader = std::make_unique(); + reader->exporter = std::move(ext); + std::unique_ptr model = std::move(reader); + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredPeriodicMetricReaderBuilder) +{ + registry_->SetPeriodicMetricReaderBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredConsolePushMetricExporterBuilder) +{ + registry_->SetConsolePushMetricExporterBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredOtlpHttpPushMetricExporterBuilder) +{ + registry_->SetOtlpHttpPushMetricExporterBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredOtlpGrpcPushMetricExporterBuilder) +{ + registry_->SetOtlpGrpcPushMetricExporterBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredOtlpFilePushMetricExporterBuilder) +{ + registry_->SetOtlpFilePushMetricExporterBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(MetricsBuilderTest, UnregisteredPrometheusPullMetricExporterBuilder) +{ + registry_->SetPrometheusPullMetricExporterBuilder(nullptr); + + auto exporter = std::make_unique(); + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + std::unique_ptr model = std::move(reader); + + EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); +} + +// --------------------------------------------------------------------------- +// MeterProviderConfigTest: Use ConfiguredSdk::Create to verify meter provider configuration and +// installation. + +namespace +{ + +class MeterProviderConfigTest : public ::testing::Test +{ +protected: + void SetUp() override + { + registry_ = std::make_shared(); + metric_buffer_ = std::make_shared(); + config_sdk::RegisterDefaultMetricsBuilders(registry_.get()); + registry_->SetPeriodicMetricReaderBuilder( + std::make_unique()); + } + + void TearDown() override + { + if (sdk_) + sdk_->UnInstall(); + } + + static std::unique_ptr MakeMeterProviderConfig( + std::unique_ptr exporter) + { + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + auto mp = std::make_unique(); + mp->readers.emplace_back(std::move(reader)); + auto model = std::make_unique(); + model->meter_provider = std::move(mp); + return model; + } + + static std::unique_ptr MakeMeterProviderConfig( + std::unique_ptr exporter) + { + auto reader = std::make_unique(); + reader->exporter = std::move(exporter); + auto mp = std::make_unique(); + mp->readers.emplace_back(std::move(reader)); + auto model = std::make_unique(); + model->meter_provider = std::move(mp); + return model; + } + + void BuildAndInstall(const std::unique_ptr &model) + { + sdk_ = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk_, nullptr); + sdk_->Install(); + } + + void EmitAndVerify() + { + ASSERT_NE(sdk_, nullptr); + ASSERT_NE(sdk_->meter_provider, nullptr); + ASSERT_EQ(sdk_->tracer_provider, nullptr); + ASSERT_EQ(sdk_->logger_provider, nullptr); + + auto provider = metrics::Provider::GetMeterProvider(); + ASSERT_NE(provider, nullptr); + provider->GetMeter("meter")->CreateUInt64Counter("counter")->Add(1); + ASSERT_TRUE(sdk_->meter_provider->ForceFlush(std::chrono::milliseconds(5000))); + ASSERT_TRUE(sdk_->meter_provider->Shutdown(std::chrono::milliseconds(5000))); + EXPECT_FALSE(metric_buffer_->empty()); + } + + std::shared_ptr registry_; + std::shared_ptr metric_buffer_; + std::unique_ptr sdk_; +}; + +} // namespace + +TEST_F(MeterProviderConfigTest, MeterProviderWithExtensionPushMetricExporter) +{ + registry_->SetExtensionPushMetricExporterBuilder( + "recording", + std::make_unique(metric_buffer_)); + + auto exporter = std::make_unique(); + exporter->name = "recording"; + auto model = MakeMeterProviderConfig(std::move(exporter)); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(MeterProviderConfigTest, MeterProviderWithConsolePushMetricExporter) +{ + registry_->SetConsolePushMetricExporterBuilder( + std::make_unique(metric_buffer_)); + auto model = MakeMeterProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(MeterProviderConfigTest, MeterProviderWithOtlpHttpPushMetricExporter) +{ + registry_->SetOtlpHttpPushMetricExporterBuilder( + std::make_unique(metric_buffer_)); + auto model = MakeMeterProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(MeterProviderConfigTest, MeterProviderWithOtlpGrpcPushMetricExporter) +{ + registry_->SetOtlpGrpcPushMetricExporterBuilder( + std::make_unique(metric_buffer_)); + auto model = MakeMeterProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(MeterProviderConfigTest, MeterProviderWithOtlpFilePushMetricExporter) +{ + registry_->SetOtlpFilePushMetricExporterBuilder( + std::make_unique(metric_buffer_)); + auto model = MakeMeterProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(MeterProviderConfigTest, MeterProviderWithPrometheusPullMetricExporter) +{ + registry_->SetPrometheusPullMetricExporterBuilder( + std::make_unique(metric_buffer_)); + auto model = MakeMeterProviderConfig( + std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} diff --git a/sdk/test/configuration/sdk_builder_test.cc b/sdk/test/configuration/sdk_builder_test.cc index 7550c44fd..f613919e2 100644 --- a/sdk/test/configuration/sdk_builder_test.cc +++ b/sdk/test/configuration/sdk_builder_test.cc @@ -3,13 +3,8 @@ #include -#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) -# include -#else -# include -#endif - #include +#include #include #include #include @@ -17,197 +12,42 @@ #include #include -#include "config_test_common.h" -#include "opentelemetry/common/attribute_value.h" -#include "opentelemetry/common/key_value_iterable_view.h" -#include "opentelemetry/logs/severity.h" -#include "opentelemetry/nostd/span.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/nostd/utility.h" - -#include "opentelemetry/sdk/configuration/aggregation_configuration.h" -#include "opentelemetry/sdk/configuration/always_off_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/always_on_sampler_configuration.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/configuration/attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/attributes_configuration.h" -#include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" -#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h" -#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h" -#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h" -#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/composite_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/explicit_bucket_histogram_aggregation_configuration.h" -#include "opentelemetry/sdk/configuration/extension_composable_sampler_builder.h" -#include "opentelemetry/sdk/configuration/extension_composable_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_builder.h" -#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/boolean_array_attribute_value_configuration.h" +#include "opentelemetry/sdk/configuration/boolean_attribute_value_configuration.h" +#include "opentelemetry/sdk/configuration/container_resource_detector_builder.h" +#include "opentelemetry/sdk/configuration/container_resource_detector_configuration.h" +#include "opentelemetry/sdk/configuration/double_array_attribute_value_configuration.h" +#include "opentelemetry/sdk/configuration/double_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/extension_resource_detector_builder.h" #include "opentelemetry/sdk/configuration/extension_resource_detector_configuration.h" #include "opentelemetry/sdk/configuration/host_resource_detector_configuration.h" #include "opentelemetry/sdk/configuration/include_exclude_configuration.h" -#include "opentelemetry/sdk/configuration/instrument_type.h" -#include "opentelemetry/sdk/configuration/logger_config_configuration.h" -#include "opentelemetry/sdk/configuration/logger_configurator_configuration.h" -#include "opentelemetry/sdk/configuration/logger_matcher_and_config_configuration.h" -#include "opentelemetry/sdk/configuration/parent_based_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/periodic_metric_reader_builder.h" -#include "opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h" -#include "opentelemetry/sdk/configuration/probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/integer_array_attribute_value_configuration.h" +#include "opentelemetry/sdk/configuration/integer_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/process_resource_detector_builder.h" #include "opentelemetry/sdk/configuration/process_resource_detector_configuration.h" -#include "opentelemetry/sdk/configuration/push_metric_exporter_configuration.h" #include "opentelemetry/sdk/configuration/registry.h" -#include "opentelemetry/sdk/configuration/registry_factory.h" #include "opentelemetry/sdk/configuration/resource_configuration.h" #include "opentelemetry/sdk/configuration/resource_detection_configuration.h" #include "opentelemetry/sdk/configuration/resource_detector_configuration.h" -#include "opentelemetry/sdk/configuration/sampler_configuration.h" #include "opentelemetry/sdk/configuration/sdk_builder.h" +#include "opentelemetry/sdk/configuration/service_resource_detector_builder.h" +#include "opentelemetry/sdk/configuration/service_resource_detector_configuration.h" #include "opentelemetry/sdk/configuration/severity_number.h" -#include "opentelemetry/sdk/configuration/span_limits_configuration.h" +#include "opentelemetry/sdk/configuration/string_array_attribute_value_configuration.h" #include "opentelemetry/sdk/configuration/string_array_configuration.h" #include "opentelemetry/sdk/configuration/string_attribute_value_configuration.h" -#include "opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h" -#include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" #include "opentelemetry/sdk/configuration/unsupported_exception.h" -#include "opentelemetry/sdk/configuration/view_configuration.h" -#include "opentelemetry/sdk/configuration/view_selector_configuration.h" -#include "opentelemetry/sdk/configuration/view_stream_configuration.h" - -#include "opentelemetry/nostd/function_ref.h" -#include "opentelemetry/nostd/variant.h" -#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" -#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" -#include "opentelemetry/sdk/logs/logger_config.h" -#include "opentelemetry/sdk/metrics/aggregation/aggregation.h" -#include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h" -#include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" -#include "opentelemetry/sdk/metrics/data/point_data.h" -#include "opentelemetry/sdk/metrics/instruments.h" -#include "opentelemetry/sdk/metrics/metric_reader.h" -#include "opentelemetry/sdk/metrics/view/attributes_processor.h" -#include "opentelemetry/sdk/metrics/view/view.h" -#include "opentelemetry/sdk/metrics/view/view_registry.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/resource/resource_detector.h" -#include "opentelemetry/sdk/trace/sampler.h" -#include "opentelemetry/sdk/trace/samplers/composable_always_on.h" -#include "opentelemetry/sdk/trace/span_limits.h" -#include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/trace/span_context.h" -#include "opentelemetry/trace/span_context_kv_iterable_view.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/span_metadata.h" -#include "opentelemetry/trace/trace_flags.h" -#include "opentelemetry/trace/trace_id.h" - -#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) -# include "opentelemetry/sdk/common/global_log_handler.h" -# include "opentelemetry/sdk/configuration/exemplar_filter.h" -# include "opentelemetry/sdk/configuration/meter_provider_configuration.h" -# include "opentelemetry/sdk/metrics/meter_provider.h" -# include "opentelemetry/test_common/sdk/common/scoped_test_log_handler.h" - -# if defined(_MSC_VER) -# include "opentelemetry/sdk/common/env_variables.h" -using opentelemetry::sdk::common::setenv; -using opentelemetry::sdk::common::unsetenv; -# endif -#endif - -using opentelemetry::sdk::configuration::Registry; -using opentelemetry::sdk::configuration::RegistryFactory; -using opentelemetry::sdk::configuration::SdkBuilder; -using opentelemetry::sdk::configuration::SpanLimitsConfiguration; -using opentelemetry::sdk::configuration::TracerProviderConfiguration; - -namespace logs = opentelemetry::logs; -namespace logs_sdk = opentelemetry::sdk::logs; -namespace scope_sdk = opentelemetry::sdk::instrumentationscope; -namespace config_sdk = opentelemetry::sdk::configuration; -namespace trace_api = opentelemetry::trace; - -namespace -{ - -using RuleAttrMap = std::map; - -opentelemetry::sdk::trace::Decision SampleWith(opentelemetry::sdk::trace::Sampler &sampler, - const trace_api::SpanContext &parent, - trace_api::SpanKind span_kind, - const RuleAttrMap &attrs) -{ - uint8_t trace_buf[trace_api::TraceId::kSize] = {1}; - std::vector>> links; - opentelemetry::common::KeyValueIterableView attrs_view{attrs}; - trace_api::SpanContextKeyValueIterableView links_view{links}; - auto result = sampler.ShouldSample(parent, trace_api::TraceId(trace_buf), "span", span_kind, - attrs_view, links_view); - return result.decision; -} -trace_api::SpanContext MakeRuleParent(bool sampled, bool is_remote) -{ - uint8_t trace_buf[trace_api::TraceId::kSize] = {1}; - uint8_t span_buf[trace_api::SpanId::kSize] = {1}; - return trace_api::SpanContext(trace_api::TraceId(trace_buf), trace_api::SpanId(span_buf), - trace_api::TraceFlags(sampled ? 1 : 0), is_remote); -} - -class TestExtensionComposableSamplerBuilder : public config_sdk::ExtensionComposableSamplerBuilder -{ -public: - std::unique_ptr Build( - const config_sdk::ExtensionComposableSamplerConfiguration *model) const override - { - called = true; - name = model->name; - depth = model->depth; - return std::make_unique(); - } - - mutable bool called{false}; - mutable std::string name; - mutable std::size_t depth{0}; -}; - -// Builds composite(rule_based{[rule]}) where the rule maps to always_on. -std::unique_ptr BuildRuleSampler( - std::unique_ptr rule) -{ - rule->sampler = std::make_unique(); - auto rule_based_config = std::make_unique(); - rule_based_config->rules.push_back(std::move(rule)); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(rule_based_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - return builder.CreateSampler(sampler_config); -} - -} // namespace - -#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) -namespace -{ - -constexpr char kMetricsExemplarFilterEnv[] = "OTEL_METRICS_EXEMPLAR_FILTER"; - -class SdkBuilderExemplarFilterEnvironmentTest : public ::testing::Test -{ -protected: - void SetUp() override { unsetenv(kMetricsExemplarFilterEnv); } - - void TearDown() override { unsetenv(kMetricsExemplarFilterEnv); } -}; - -} // namespace -#endif +namespace internal_log = opentelemetry::sdk::common::internal_log; +namespace config_sdk = opentelemetry::sdk::configuration; +namespace nostd = opentelemetry::nostd; //------------------------------------------------------------------------------ // Tests for the SdkBuilder class methods that create SDK components from configuration models @@ -215,1006 +55,181 @@ class SdkBuilderExemplarFilterEnvironmentTest : public ::testing::Test // independently verified. For full integration tests of the SdkBuilder with configuration models, // see the programmatic_configuration_test.cc file. -TEST(SdkBuilder, SpanLimitsDefaults) +TEST(SdkBuilder, SetLogLevel) { - auto model = std::make_unique(); - model->limits = nullptr; - - SdkBuilder builder(RegistryFactory::Create()); - auto resource = opentelemetry::sdk::resource::Resource::Create({}); - auto provider = builder.CreateTracerProvider(model, resource); - ASSERT_NE(provider, nullptr); - - const auto limits = provider->GetSpanLimits(); - const auto default_limits = opentelemetry::sdk::trace::SpanLimits{}; - - EXPECT_EQ(limits.attribute_count_limit, default_limits.attribute_count_limit); - EXPECT_EQ(limits.event_count_limit, default_limits.event_count_limit); - EXPECT_EQ(limits.link_count_limit, default_limits.link_count_limit); - EXPECT_EQ(limits.event_attribute_count_limit, default_limits.event_attribute_count_limit); - EXPECT_EQ(limits.link_attribute_count_limit, default_limits.link_attribute_count_limit); - EXPECT_EQ(limits.attribute_value_length_limit, default_limits.attribute_value_length_limit); -} - -TEST(SdkBuilder, SpanLimitsConfiguration) -{ - auto model = std::make_unique(); - model->limits = std::make_unique(); - model->limits->attribute_value_length_limit = 1111; - model->limits->attribute_count_limit = 2222; - model->limits->event_count_limit = 3333; - model->limits->link_count_limit = 4444; - model->limits->event_attribute_count_limit = 5555; - model->limits->link_attribute_count_limit = 6666; - - SdkBuilder builder(RegistryFactory::Create()); - auto resource = opentelemetry::sdk::resource::Resource::Create({}); - auto provider = builder.CreateTracerProvider(model, resource); - ASSERT_NE(provider, nullptr); - - auto limits = provider->GetSpanLimits(); - EXPECT_EQ(limits.attribute_value_length_limit, model->limits->attribute_value_length_limit); - EXPECT_EQ(limits.attribute_count_limit, model->limits->attribute_count_limit); - EXPECT_EQ(limits.event_count_limit, model->limits->event_count_limit); - EXPECT_EQ(limits.link_count_limit, model->limits->link_count_limit); - EXPECT_EQ(limits.event_attribute_count_limit, model->limits->event_attribute_count_limit); - EXPECT_EQ(limits.link_attribute_count_limit, model->limits->link_attribute_count_limit); -} - -#if defined(ENABLE_METRICS_EXEMPLAR_PREVIEW) && !defined(NO_GETENV) -TEST_F(SdkBuilderExemplarFilterEnvironmentTest, DeclarativeExemplarFilterDoesNotReadEnvironment) -{ - opentelemetry::test_common::ScopedTestLogHandler log_handler{ - opentelemetry::sdk::common::internal_log::LogLevel::Warning}; - setenv(kMetricsExemplarFilterEnv, "invalid", 1); - - auto model = std::make_unique(); - model->exemplar_filter = config_sdk::ExemplarFilter::always_on; - - SdkBuilder builder(RegistryFactory::Create()); - auto resource = opentelemetry::sdk::resource::Resource::Create({}); - auto provider = builder.CreateMeterProvider(model, resource); - ASSERT_NE(provider, nullptr); - - EXPECT_TRUE(log_handler.Drain().empty()); -} -#endif - -TEST(SdkBuilder, CreateLoggerConfigurator) -{ - config_sdk::LoggerConfigConfiguration default_config; - default_config.enabled = true; - default_config.minimum_severity = config_sdk::SeverityNumber::warn; - default_config.trace_based = false; - - config_sdk::LoggerMatcherAndConfigConfiguration matcher1; - matcher1.name = "enabled_minsev_error_not_trace_based"; - matcher1.config.enabled = true; - matcher1.config.minimum_severity = config_sdk::SeverityNumber::error3; - matcher1.config.trace_based = false; - - config_sdk::LoggerMatcherAndConfigConfiguration matcher2; - matcher2.name = "disabled_minsev_info_trace_based"; - matcher2.config.enabled = false; - matcher2.config.minimum_severity = config_sdk::SeverityNumber::debug; - matcher2.config.trace_based = true; - - auto model = std::make_unique(); - model->default_config = default_config; - model->loggers.push_back(matcher1); - model->loggers.push_back(matcher2); - - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - - auto logger_configurator = builder.CreateLoggerConfigurator(model); - ASSERT_NE(logger_configurator, nullptr); - - auto default_scope = scope_sdk::InstrumentationScope::Create("default_scope"); - logs_sdk::LoggerConfig sdk_logger_config_default = - logger_configurator->ComputeConfig(*default_scope); - - auto scope_1 = scope_sdk::InstrumentationScope::Create(matcher1.name); - logs_sdk::LoggerConfig sdk_logger_config_1 = logger_configurator->ComputeConfig(*scope_1); - - auto scope_2 = scope_sdk::InstrumentationScope::Create(matcher2.name); - logs_sdk::LoggerConfig sdk_logger_config_2 = logger_configurator->ComputeConfig(*scope_2); - - EXPECT_TRUE(sdk_logger_config_default.IsEnabled()); - EXPECT_EQ(sdk_logger_config_default.GetMinimumSeverity(), logs::Severity::kWarn); - EXPECT_FALSE(sdk_logger_config_default.IsTraceBased()); - - EXPECT_TRUE(sdk_logger_config_1.IsEnabled()); - EXPECT_EQ(sdk_logger_config_1.GetMinimumSeverity(), logs::Severity::kError3); - EXPECT_FALSE(sdk_logger_config_1.IsTraceBased()); - - EXPECT_FALSE(sdk_logger_config_2.IsEnabled()); - EXPECT_EQ(sdk_logger_config_2.GetMinimumSeverity(), logs::Severity::kDebug); - EXPECT_TRUE(sdk_logger_config_2.IsTraceBased()); -} - -TEST(SdkBuilder, CreateParentBasedSampler) -{ - // parent based with no root configured should default to always on - { - config_sdk::ParentBasedSamplerConfiguration parent_based_sampler_config; - parent_based_sampler_config.root = nullptr; - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateParentBasedSampler(&parent_based_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOnSampler})"); - } - - // parent based with root always on - { - config_sdk::ParentBasedSamplerConfiguration parent_based_sampler_config; - parent_based_sampler_config.root = std::make_unique(); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateParentBasedSampler(&parent_based_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOnSampler})"); - } - - // parent based with root always off - { - config_sdk::ParentBasedSamplerConfiguration parent_based_sampler_config; - parent_based_sampler_config.root = - std::make_unique(); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateParentBasedSampler(&parent_based_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOffSampler})"); - } - - // parent based with a custom root sampler - { - config_sdk::ParentBasedSamplerConfiguration parent_based_sampler_config; - auto trace_id_ratio_based_sampler_config = - std::make_unique(); - trace_id_ratio_based_sampler_config->ratio = 0.5; - parent_based_sampler_config.root = std::move(trace_id_ratio_based_sampler_config); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateParentBasedSampler(&parent_based_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(ParentBased{TraceIdRatioBasedSampler{0.500000}})"); - } - - // parent based with all sub samplers set - { - config_sdk::ParentBasedSamplerConfiguration parent_based_sampler_config; - auto trace_id_ratio_based_sampler_config = - std::make_unique(); - trace_id_ratio_based_sampler_config->ratio = 0.25; - parent_based_sampler_config.root = std::move(trace_id_ratio_based_sampler_config); - - auto always_off_sampler_config = std::make_unique(); - parent_based_sampler_config.remote_parent_sampled = std::move(always_off_sampler_config); - - auto always_on_sampler_config = std::make_unique(); - parent_based_sampler_config.remote_parent_not_sampled = std::move(always_on_sampler_config); - - auto trace_id_ratio_based_sampler_config_2 = - std::make_unique(); - trace_id_ratio_based_sampler_config_2->ratio = 0.35; - parent_based_sampler_config.local_parent_sampled = - std::move(trace_id_ratio_based_sampler_config_2); - - auto always_off_sampler_config_2 = - std::make_unique(); - parent_based_sampler_config.local_parent_not_sampled = std::move(always_off_sampler_config_2); - - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateParentBasedSampler(&parent_based_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(ParentBased{TraceIdRatioBasedSampler{0.250000}})"); - } -} - -TEST(SdkBuilder, CreateProbabilitySampler) -{ - // default ratio is 1.0 - { - config_sdk::ProbabilitySamplerConfiguration probability_sampler_config; - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateProbabilitySampler(&probability_sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ProbabilitySampler{1.000000})"); - } - - // explicit ratio, dispatched through CreateSampler - { - auto probability_sampler_config = - std::make_unique(); - probability_sampler_config->ratio = 0.5; - std::unique_ptr sampler_config = - std::move(probability_sampler_config); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ProbabilitySampler{0.500000})"); - } -} - -TEST(SdkBuilder, CreateComposableAlwaysOnSampler) -{ - auto composable_config = std::make_unique(); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(composable_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableAlwaysOnSampler})"); -} - -TEST(SdkBuilder, CreateComposableAlwaysOffSampler) -{ - auto composable_config = std::make_unique(); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(composable_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableAlwaysOffSampler})"); -} - -TEST(SdkBuilder, CreateExtensionComposableSampler) -{ - auto registry = config_sdk::RegistryFactory::Create(); - auto extension_builder = std::make_unique(); - auto *extension_builder_ptr = extension_builder.get(); - registry->SetExtensionComposableSamplerBuilder("custom_composable", std::move(extension_builder)); - - auto extension_config = std::make_unique(); - extension_config->name = "custom_composable"; - extension_config->depth = 2; - auto composite = std::make_unique(); - composite->composable_sampler = std::move(extension_config); - std::unique_ptr sampler_config = std::move(composite); - - config_sdk::SdkBuilder builder(std::move(registry)); - auto sampler = builder.CreateSampler(sampler_config); - - ASSERT_NE(sampler, nullptr); - EXPECT_TRUE(extension_builder_ptr->called); - EXPECT_EQ(extension_builder_ptr->name, "custom_composable"); - EXPECT_EQ(extension_builder_ptr->depth, 2); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableAlwaysOnSampler})"); -} - -TEST(SdkBuilder, CreateUnregisteredExtensionComposableSampler) -{ - auto extension_config = std::make_unique(); - extension_config->name = "missing_composable"; - auto composite = std::make_unique(); - composite->composable_sampler = std::move(extension_config); - std::unique_ptr sampler_config = std::move(composite); - - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - EXPECT_THROW(builder.CreateSampler(sampler_config), config_sdk::UnsupportedException); -} - -TEST(SdkBuilder, CreateComposableProbabilitySampler) -{ - auto composable_probability_sampler_config = - std::make_unique(); - composable_probability_sampler_config->ratio = 0.25; - auto composite = std::make_unique(); - composite->composable_sampler = std::move(composable_probability_sampler_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableProbabilitySampler{0.250000}})"); -} - -TEST(SdkBuilder, CreateComposableParentThresholdSampler) -{ - auto root_config = std::make_unique(); - root_config->ratio = 0.25; - auto parent_config = - std::make_unique(); - parent_config->root = std::move(root_config); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(parent_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ( - std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableParentThresholdSampler{ComposableProbabilitySampler{0.250000}}})"); -} - -TEST(SdkBuilder, CreateComposableParentThresholdSamplerNullRoot) -{ - auto parent_config = - std::make_unique(); - parent_config->root = nullptr; - auto composite = std::make_unique(); - composite->composable_sampler = std::move(parent_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableParentThresholdSampler{ComposableAlwaysOnSampler}})"); -} - -TEST(SdkBuilder, CreateComposableParentThresholdSamplerNestedDepth3) -{ - auto innermost_config = std::make_unique(); - innermost_config->ratio = 0.25; - - auto middle_config = - std::make_unique(); - middle_config->root = std::move(innermost_config); - - auto outer_config = std::make_unique(); - outer_config->root = std::move(middle_config); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(outer_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ(std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableParentThresholdSampler{ComposableParentThresholdSampler{)" - R"(ComposableProbabilitySampler{0.250000}}}})"); -} - -namespace -{ - -// Builds a chain of parent_threshold nodes ending in an always_on leaf. -// Total nesting depth, root included, is num_parent_nodes + 1. -std::unique_ptr MakeNestedComposableConfig( - std::size_t num_parent_nodes) -{ - std::unique_ptr node = - std::make_unique(); - for (std::size_t i = 0; i < num_parent_nodes; ++i) - { - auto parent = std::make_unique(); - parent->root = std::move(node); - node = std::move(parent); - } - auto composite = std::make_unique(); - composite->composable_sampler = std::move(node); - return composite; -} - -} // namespace - -TEST(SdkBuilder, CreateComposableSamplerAtMaxDepth) -{ - // 9 parent nodes + leaf = depth 10, the default maximum. - auto sampler_config = MakeNestedComposableConfig(9); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); -} - -TEST(SdkBuilder, CreateComposableSamplerBeyondMaxDepth) -{ - // 10 parent nodes + leaf = depth 11, exceeding the default maximum. - auto sampler_config = MakeNestedComposableConfig(10); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - EXPECT_THROW(builder.CreateSampler(sampler_config), config_sdk::UnsupportedException); -} - -TEST(SdkBuilder, CreateComposableRuleBasedSampler) -{ - auto rule_based_config = std::make_unique(); - - auto rule = std::make_unique(); - auto attribute_values = - std::make_unique(); - attribute_values->key = "http.route"; - attribute_values->values = {"/health"}; - rule->attribute_values = std::move(attribute_values); - rule->sampler = std::make_unique(); - rule_based_config->rules.push_back(std::move(rule)); - - auto fallback = std::make_unique(); - fallback->sampler = std::make_unique(); - rule_based_config->rules.push_back(std::move(fallback)); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(rule_based_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - EXPECT_EQ( - std::string{sampler->GetDescription()}, - R"(CompositeSampler{ComposableRuleBasedSampler{ComposableAlwaysOffSampler,ComposableAlwaysOnSampler}})"); -} - -TEST(SdkBuilder, RuleBasedPredicateAttributeValues) -{ - using config_sdk::ComposableRuleBasedSamplerRuleAttributeValuesConfiguration; - using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; - using opentelemetry::sdk::trace::Decision; - - auto parent = MakeRuleParent(true, true); - - // string attribute, exact match - { - auto rule = std::make_unique(); - auto values = std::make_unique(); - values->key = "http.route"; - values->values = {"/health", "/metrics"}; - rule->attribute_values = std::move(values); - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/health"}}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/users"}}), - Decision::DROP); - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); - } - - // non-string attribute matches via string representation - { - auto rule = std::make_unique(); - auto values = std::make_unique(); - values->key = "http.response.status_code"; - values->values = {"404"}; - rule->attribute_values = std::move(values); - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, - {{"http.response.status_code", static_cast(404)}}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, - {{"http.response.status_code", static_cast(200)}}), - Decision::DROP); - } - - // array attribute matches if any element matches - { - auto rule = std::make_unique(); - auto values = std::make_unique(); - values->key = "tags"; - values->values = {"b"}; - rule->attribute_values = std::move(values); - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - std::vector matching_tags{"a", "b"}; - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, - {{"tags", opentelemetry::nostd::span( - matching_tags.data(), matching_tags.size())}}), - Decision::RECORD_AND_SAMPLE); - - std::vector non_matching_tags{"x", "z"}; - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, - {{"tags", opentelemetry::nostd::span( - non_matching_tags.data(), non_matching_tags.size())}}), - Decision::DROP); - } -} - -TEST(SdkBuilder, RuleBasedPredicateAttributePatterns) -{ - using config_sdk::ComposableRuleBasedSamplerRuleAttributePatternsConfiguration; - using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; - using opentelemetry::sdk::trace::Decision; - - auto parent = MakeRuleParent(true, true); - - // included glob, excluded overrides - auto rule = std::make_unique(); - auto patterns = std::make_unique(); - patterns->key = "url.path"; - patterns->included = {"/api/*"}; - patterns->excluded = {"/api/health?"}; - rule->attribute_patterns = std::move(patterns); - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/api/users"}}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/api/healthz"}}), - Decision::DROP); - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/other"}}), - Decision::DROP); - - // excluded only: no included patterns means match-all, then filter by excluded - { - auto excluded_only_rule = std::make_unique(); - auto excluded_only_patterns = - std::make_unique(); - excluded_only_patterns->key = "url.path"; - excluded_only_patterns->excluded = {"/internal/*"}; - excluded_only_rule->attribute_patterns = std::move(excluded_only_patterns); - auto excluded_only_sampler = BuildRuleSampler(std::move(excluded_only_rule)); - ASSERT_NE(excluded_only_sampler, nullptr); - - EXPECT_EQ(SampleWith(*excluded_only_sampler, parent, trace_api::SpanKind::kServer, - {{"url.path", "/internal/x"}}), - Decision::DROP); - EXPECT_EQ(SampleWith(*excluded_only_sampler, parent, trace_api::SpanKind::kServer, - {{"url.path", "/public/x"}}), - Decision::RECORD_AND_SAMPLE); - } -} - -TEST(SdkBuilder, RuleBasedPredicateSpanKindAndParent) -{ - using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; - using opentelemetry::sdk::trace::Decision; - - // span kind - { - auto rule = std::make_unique(); - rule->match_span_kind_server = true; - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - auto parent = MakeRuleParent(true, true); - - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kClient, {}), Decision::DROP); - } + config_sdk::SdkBuilder builder(std::make_shared()); - // parent: remote only - { - auto rule = std::make_unique(); - rule->match_parent_remote = true; - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, false), trace_api::SpanKind::kServer, {}), - Decision::DROP); - EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), - trace_api::SpanKind::kServer, {}), - Decision::DROP); - } + internal_log::LogLevel level{}; - // parent: local only + // trace/debug group -> Debug + for (auto sev : {config_sdk::SeverityNumber::trace, config_sdk::SeverityNumber::trace2, + config_sdk::SeverityNumber::trace3, config_sdk::SeverityNumber::trace4, + config_sdk::SeverityNumber::debug, config_sdk::SeverityNumber::debug2, + config_sdk::SeverityNumber::debug3, config_sdk::SeverityNumber::debug4}) { - auto rule = std::make_unique(); - rule->match_parent_local = true; - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, false), trace_api::SpanKind::kServer, {}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), - Decision::DROP); - EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), - trace_api::SpanKind::kServer, {}), - Decision::DROP); + builder.SetLogLevel(level, sev); + EXPECT_EQ(level, internal_log::LogLevel::Debug); } - // parent: none only (root spans) + // info group -> Info + for (auto sev : {config_sdk::SeverityNumber::info, config_sdk::SeverityNumber::info2, + config_sdk::SeverityNumber::info3, config_sdk::SeverityNumber::info4}) { - auto rule = std::make_unique(); - rule->match_parent_none = true; - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), - trace_api::SpanKind::kServer, {}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), - Decision::DROP); + builder.SetLogLevel(level, sev); + EXPECT_EQ(level, internal_log::LogLevel::Info); } - // span kind and attribute values: both conditions must match (AND, not OR) - { - using config_sdk::ComposableRuleBasedSamplerRuleAttributeValuesConfiguration; - - auto rule = std::make_unique(); - rule->match_span_kind_server = true; - auto values = std::make_unique(); - values->key = "http.route"; - values->values = {"/health"}; - rule->attribute_values = std::move(values); - auto sampler = BuildRuleSampler(std::move(rule)); - ASSERT_NE(sampler, nullptr); - auto parent = MakeRuleParent(true, true); - - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/health"}}), - Decision::RECORD_AND_SAMPLE); - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/other"}}), - Decision::DROP); - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); - EXPECT_EQ( - SampleWith(*sampler, parent, trace_api::SpanKind::kClient, {{"http.route", "/health"}}), - Decision::DROP); - } -} - -TEST(SdkBuilder, RuleBasedFirstMatchAndDefaults) -{ - using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; - using opentelemetry::sdk::trace::Decision; - - auto parent = MakeRuleParent(true, true); - - // first match wins: rule 1 (no conditions -> matches all) is always_off, rule 2 always_on + // warn group -> Warning + for (auto sev : {config_sdk::SeverityNumber::warn, config_sdk::SeverityNumber::warn2, + config_sdk::SeverityNumber::warn3, config_sdk::SeverityNumber::warn4}) { - auto rule_based_config = - std::make_unique(); - auto first = std::make_unique(); - first->sampler = std::make_unique(); - rule_based_config->rules.push_back(std::move(first)); - auto second = std::make_unique(); - second->sampler = std::make_unique(); - rule_based_config->rules.push_back(std::move(second)); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(rule_based_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + builder.SetLogLevel(level, sev); + EXPECT_EQ(level, internal_log::LogLevel::Warning); } - // empty rules -> nothing matches -> drop + // error/fatal group -> Error + for (auto sev : {config_sdk::SeverityNumber::error, config_sdk::SeverityNumber::error2, + config_sdk::SeverityNumber::error3, config_sdk::SeverityNumber::error4, + config_sdk::SeverityNumber::fatal, config_sdk::SeverityNumber::fatal2, + config_sdk::SeverityNumber::fatal3, config_sdk::SeverityNumber::fatal4}) { - auto rule_based_config = - std::make_unique(); - auto composite = std::make_unique(); - composite->composable_sampler = std::move(rule_based_config); - std::unique_ptr sampler_config = std::move(composite); - config_sdk::SdkBuilder builder(config_sdk::RegistryFactory::Create()); - auto sampler = builder.CreateSampler(sampler_config); - ASSERT_NE(sampler, nullptr); - - EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + builder.SetLogLevel(level, sev); + EXPECT_EQ(level, internal_log::LogLevel::Error); } } -TEST(SdkBuilder, CreatePeriodicMetricReader) +TEST(SdkBuilder, SetResourceWithDefaults) { - auto exporter = std::make_unique(); - exporter->name = "noop"; - - config_sdk::PeriodicMetricReaderConfiguration model; - model.exporter = std::move(exporter); - model.interval = 12345; - model.timeout = 678; - model.cardinality_limits = std::make_unique(); - model.cardinality_limits->default_limit = 100; - model.cardinality_limits->counter = 200; - model.cardinality_limits->gauge = 300; - model.cardinality_limits->histogram = 400; - model.cardinality_limits->observable_counter = 500; - model.cardinality_limits->observable_gauge = 600; - model.cardinality_limits->observable_up_down_counter = 700; - model.cardinality_limits->up_down_counter = 800; - - auto captured = std::make_shared(); + config_sdk::SdkBuilder builder(std::make_shared()); + opentelemetry::sdk::resource::Resource resource; - auto registry = std::make_shared(); - registry->SetExtensionPushMetricExporterBuilder( - "noop", std::make_unique()); - registry->SetPeriodicMetricReaderBuilder( - std::make_unique(captured)); + builder.SetResource(resource, nullptr); - config_sdk::SdkBuilder builder(registry); - auto reader = builder.CreatePeriodicMetricReader(&model); - ASSERT_NE(reader, nullptr); - - EXPECT_TRUE(captured->called); - EXPECT_EQ(captured->interval, model.interval); - EXPECT_EQ(captured->timeout, model.timeout); - EXPECT_TRUE(captured->exporter != nullptr); - EXPECT_EQ(reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kCounter), - 200u); - EXPECT_EQ(reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kGauge), 300u); - EXPECT_EQ(reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kHistogram), - 400u); - EXPECT_EQ( - reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kObservableCounter), - 500u); - EXPECT_EQ( - reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kObservableGauge), - 600u); - EXPECT_EQ(reader->GetCardinalityLimit( - opentelemetry::sdk::metrics::InstrumentType::kObservableUpDownCounter), - 700u); - EXPECT_EQ( - reader->GetCardinalityLimit(opentelemetry::sdk::metrics::InstrumentType::kUpDownCounter), - 800u); + const auto &attrs = resource.GetAttributes(); + EXPECT_NE(attrs.find("telemetry.sdk.name"), attrs.end()); + EXPECT_NE(attrs.find("telemetry.sdk.language"), attrs.end()); + EXPECT_NE(attrs.find("telemetry.sdk.version"), attrs.end()); } -TEST(SdkBuilder, CreateAttributesProcessor) +TEST(SdkBuilder, SetResourceWithAttributes) { - std::map attributes = {{"included", 1}, {"excluded", 2}, {"unlisted", 3}}; - opentelemetry::common::KeyValueIterableView> iterable(attributes); config_sdk::SdkBuilder builder(std::make_shared()); + opentelemetry::sdk::resource::Resource resource; - // When both lists are configured, exclusion takes precedence over inclusion. - { - auto model = std::make_unique(); - model->included = std::make_unique(); - model->included->string_array = {"included", "excluded"}; - model->excluded = std::make_unique(); - model->excluded->string_array = {"excluded"}; - - auto processor = builder.CreateAttributesProcessor(model); - ASSERT_NE(processor, nullptr); - auto filtered = processor->process(iterable); - - EXPECT_EQ(filtered.size(), 1u); - EXPECT_NE(filtered.find("included"), filtered.end()); - } + auto model = std::make_unique(); + model->attributes = std::make_unique(); - // Wildcard patterns are evaluated per key, with exclusion taking precedence. - { - std::map wildcard_attributes = { - {"foo.bar", 1}, {"foo.baz", 2}, {"question.x", 3}, {"question.xy", 4}, {"other", 5}}; - opentelemetry::common::KeyValueIterableView> wildcard_iterable( - wildcard_attributes); - - auto model = std::make_unique(); - model->included = std::make_unique(); - model->included->string_array = {"foo.*", "question.?"}; - model->excluded = std::make_unique(); - model->excluded->string_array = {"foo.bar"}; - - auto processor = builder.CreateAttributesProcessor(model); - ASSERT_NE(processor, nullptr); - auto filtered = processor->process(wildcard_iterable); - - EXPECT_EQ(filtered.size(), 2u); - EXPECT_NE(filtered.find("foo.baz"), filtered.end()); - EXPECT_NE(filtered.find("question.x"), filtered.end()); - } + auto string_attr = std::make_unique(); + string_attr->value = "test_string_value"; + model->attributes->kv_map["string_key"] = std::move(string_attr); - // An exclude-only configuration retains every key that is not excluded. - { - auto model = std::make_unique(); - model->excluded = std::make_unique(); - model->excluded->string_array = {"excluded"}; + auto boolean_attr = std::make_unique(); + boolean_attr->value = true; + model->attributes->kv_map["boolean_key"] = std::move(boolean_attr); - auto processor = builder.CreateAttributesProcessor(model); - ASSERT_NE(processor, nullptr); - auto filtered = processor->process(iterable); + auto integer_attr = std::make_unique(); + integer_attr->value = int64_t{42}; + model->attributes->kv_map["integer_key"] = std::move(integer_attr); - EXPECT_EQ(filtered.size(), 2u); - EXPECT_EQ(filtered.find("excluded"), filtered.end()); - } + auto double_attr = std::make_unique(); + double_attr->value = 3.14; + model->attributes->kv_map["double_key"] = std::move(double_attr); - // An empty include/exclude block leaves attributes unchanged. - { - auto model = std::make_unique(); - auto processor = builder.CreateAttributesProcessor(model); - ASSERT_NE(processor, nullptr); - auto filtered = processor->process(iterable); + auto string_array_attr = std::make_unique(); + string_array_attr->value = {"alpha", "beta", "gamma"}; + model->attributes->kv_map["string_array_key"] = std::move(string_array_attr); - EXPECT_EQ(filtered.size(), attributes.size()); - } -} + auto boolean_array_attr = std::make_unique(); + boolean_array_attr->value = {true, false, true}; + model->attributes->kv_map["boolean_array_key"] = std::move(boolean_array_attr); -namespace -{ + auto integer_array_attr = std::make_unique(); + integer_array_attr->value = {10, 20, 30}; + model->attributes->kv_map["integer_array_key"] = std::move(integer_array_attr); -// Builds a ViewConfiguration selecting the given instrument type, with only -// aggregation_cardinality_limit set on the stream (no explicit `aggregation` block). -std::unique_ptr MakeCardinalityOnlyViewConfig( - config_sdk::InstrumentType instrument_type, - std::size_t cardinality_limit) -{ - auto model = std::make_unique(); - model->selector = std::make_unique(); - model->selector->instrument_type = instrument_type; + auto double_array_attr = std::make_unique(); + double_array_attr->value = {1.1, 2.2, 3.3}; + model->attributes->kv_map["double_array_key"] = std::move(double_array_attr); - model->stream = std::make_unique(); - model->stream->aggregation_cardinality_limit = cardinality_limit; + builder.SetResource(resource, model); + const auto &attrs = resource.GetAttributes(); - return model; -} + ASSERT_NE(attrs.find("string_key"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("string_key")), "test_string_value"); -} // namespace + ASSERT_NE(attrs.find("boolean_key"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("boolean_key")), true); -#if OPENTELEMETRY_ABI_VERSION_NO < 2 -TEST(SdkBuilder, AddViewGaugeUnsupportedWithABIv1) -{ - auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::gauge, 42); + ASSERT_NE(attrs.find("integer_key"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("integer_key")), int64_t{42}); - auto registry = std::make_shared(); - config_sdk::SdkBuilder builder(registry); - opentelemetry::sdk::metrics::ViewRegistry view_registry; + ASSERT_NE(attrs.find("double_key"), attrs.end()); + EXPECT_DOUBLE_EQ(nostd::get(attrs.at("double_key")), 3.14); - EXPECT_THROW(builder.AddView(&view_registry, model), config_sdk::UnsupportedException); -} -#endif + ASSERT_NE(attrs.find("string_array_key"), attrs.end()); + EXPECT_EQ(nostd::get>(attrs.at("string_array_key")), + (std::vector{"alpha", "beta", "gamma"})); -TEST(SdkBuilder, AddViewEmptySelectorMatchesAllSupportedInstrumentTypes) -{ - namespace metrics_sdk = opentelemetry::sdk::metrics; + ASSERT_NE(attrs.find("boolean_array_key"), attrs.end()); + EXPECT_EQ(nostd::get>(attrs.at("boolean_array_key")), + (std::vector{true, false, true})); - auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::none, 42); + ASSERT_NE(attrs.find("integer_array_key"), attrs.end()); + EXPECT_EQ(nostd::get>(attrs.at("integer_array_key")), + (std::vector{10, 20, 30})); - auto registry = std::make_shared(); - config_sdk::SdkBuilder builder(registry); - metrics_sdk::ViewRegistry view_registry; - builder.AddView(&view_registry, model); - - auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); - std::vector supported_instrument_types{ - metrics_sdk::InstrumentType::kCounter, - metrics_sdk::InstrumentType::kHistogram, - metrics_sdk::InstrumentType::kUpDownCounter, - metrics_sdk::InstrumentType::kObservableCounter, - metrics_sdk::InstrumentType::kObservableGauge, - metrics_sdk::InstrumentType::kObservableUpDownCounter}; -#if OPENTELEMETRY_ABI_VERSION_NO >= 2 - supported_instrument_types.push_back(metrics_sdk::InstrumentType::kGauge); -#endif - - for (auto instrument_type : supported_instrument_types) - { - metrics_sdk::InstrumentDescriptor instrument_descriptor{ - "test.instrument", "test description", "units", instrument_type, - metrics_sdk::InstrumentValueType::kLong}; - int matched = 0; - view_registry.FindViews(instrument_descriptor, *instrumentation_scope, - [&](const metrics_sdk::View &view) { - auto *config = view.GetAggregationConfig(); - EXPECT_NE(config, nullptr); - if (config != nullptr) - { - EXPECT_EQ(config->cardinality_limit_, 42u); - matched++; - } - return true; - }); - EXPECT_EQ(matched, 1); - } + ASSERT_NE(attrs.find("double_array_key"), attrs.end()); + EXPECT_EQ(nostd::get>(attrs.at("double_array_key")), + (std::vector{1.1, 2.2, 3.3})); } -TEST(SdkBuilder, AddViewHistogramCardinalityLimitOnly) +TEST(SdkBuilder, SetResourceWithAttributesList) { - namespace metrics_sdk = opentelemetry::sdk::metrics; + config_sdk::SdkBuilder builder(std::make_shared()); + opentelemetry::sdk::resource::Resource resource; - auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::histogram, 42); + auto model = std::make_unique(); + model->attributes_list = "service.name=my-service,service.version=1.2.3"; - auto registry = std::make_shared(); - config_sdk::SdkBuilder builder(registry); + builder.SetResource(resource, model); - metrics_sdk::ViewRegistry view_registry; - builder.AddView(&view_registry, model); - - metrics_sdk::InstrumentDescriptor instrument_descriptor{ - "test.instrument", "test description", "units", metrics_sdk::InstrumentType::kHistogram, - metrics_sdk::InstrumentValueType::kLong}; - auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); - - int matched = 0; - view_registry.FindViews( - instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { - matched++; - // The view must not be rejected: it should carry a - // HistogramAggregationConfig (not a plain AggregationConfig), since - // the instrument's default aggregation for kHistogram is kHistogram. - auto *aggregation_config = view.GetAggregationConfig(); - EXPECT_NE(aggregation_config, nullptr); - if (aggregation_config) - { - EXPECT_EQ(aggregation_config->GetType(), metrics_sdk::AggregationType::kHistogram); - EXPECT_EQ(aggregation_config->cardinality_limit_, 42u); - - // Pin what users actually receive: building the aggregation from this config - // must keep the SDK's default bucket boundaries, not silently collapse to a - // single bucket. A default-constructed HistogramAggregationConfig has empty - // boundaries_, which the aggregation takes literally (as opposed to a null - // config pointer, which falls back to the default boundaries), so AddView() - // must populate boundaries_ explicitly. - auto aggregation = metrics_sdk::DefaultAggregation::CreateAggregation( - metrics_sdk::AggregationType::kHistogram, instrument_descriptor, aggregation_config); - EXPECT_NE(aggregation, nullptr); - if (aggregation) - { - auto histogram_data = - opentelemetry::nostd::get(aggregation->ToPoint()); - EXPECT_EQ(histogram_data.boundaries_.size(), 15u); - EXPECT_EQ(histogram_data.counts_.size(), 16u); - } - } - return true; - }); - - EXPECT_EQ(matched, 1); + const auto &attrs = resource.GetAttributes(); + ASSERT_NE(attrs.find("service.name"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("service.name")), "my-service"); + ASSERT_NE(attrs.find("service.version"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("service.version")), "1.2.3"); } -TEST(SdkBuilder, AddViewCounterCardinalityLimitOnly) +TEST(SdkBuilder, SetResourceWithSchemaUrl) { - namespace metrics_sdk = opentelemetry::sdk::metrics; + config_sdk::SdkBuilder builder(std::make_shared()); + opentelemetry::sdk::resource::Resource resource; - auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::counter, 7); + auto model = std::make_unique(); + model->schema_url = "https://opentelemetry.io/schemas/1.25.0"; - auto registry = std::make_shared(); - config_sdk::SdkBuilder builder(registry); + builder.SetResource(resource, model); - metrics_sdk::ViewRegistry view_registry; - builder.AddView(&view_registry, model); - - metrics_sdk::InstrumentDescriptor instrument_descriptor{ - "test.instrument", "test description", "units", metrics_sdk::InstrumentType::kCounter, - metrics_sdk::InstrumentValueType::kLong}; - auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); - - int matched = 0; - view_registry.FindViews( - instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { - matched++; - auto *aggregation_config = view.GetAggregationConfig(); - EXPECT_NE(aggregation_config, nullptr); - if (aggregation_config) - { - EXPECT_EQ(aggregation_config->GetType(), metrics_sdk::AggregationType::kDefault); - EXPECT_EQ(aggregation_config->cardinality_limit_, 7u); - } - return true; - }); - - EXPECT_EQ(matched, 1); + EXPECT_EQ(resource.GetSchemaURL(), "https://opentelemetry.io/schemas/1.25.0"); } -TEST(SdkBuilder, AddViewWithCardinalityLimitPreservesExplicitAggregation) +TEST(SdkBuilder, SetResourceAttributesOverrideAttributesList) { - namespace metrics_sdk = opentelemetry::sdk::metrics; + config_sdk::SdkBuilder builder(std::make_shared()); + opentelemetry::sdk::resource::Resource resource; - auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::histogram, 42); - auto aggregation = - std::make_unique(); - aggregation->boundaries = {1.0, 2.0}; - model->stream->aggregation = std::move(aggregation); + auto model = std::make_unique(); + model->attributes_list = "service.name=from-list"; + model->attributes = std::make_unique(); + auto override_attr = std::make_unique(); + override_attr->value = "from-attributes"; + model->attributes->kv_map["service.name"] = std::move(override_attr); - auto registry = std::make_shared(); - config_sdk::SdkBuilder builder(registry); + builder.SetResource(resource, model); - metrics_sdk::ViewRegistry view_registry; - builder.AddView(&view_registry, model); - - metrics_sdk::InstrumentDescriptor instrument_descriptor{ - "test.instrument", "test description", "units", metrics_sdk::InstrumentType::kHistogram, - metrics_sdk::InstrumentValueType::kLong}; - auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); - - int matched = 0; - view_registry.FindViews( - instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { - ++matched; - auto *aggregation_config = view.GetAggregationConfig(); - EXPECT_NE(aggregation_config, nullptr); - if (aggregation_config) - { - EXPECT_EQ(aggregation_config->GetType(), metrics_sdk::AggregationType::kHistogram); - EXPECT_EQ(aggregation_config->cardinality_limit_, 42u); - auto *histogram_config = - static_cast(aggregation_config); - EXPECT_EQ(histogram_config->boundaries_, (std::vector{1.0, 2.0})); - } - return true; - }); - - EXPECT_EQ(matched, 1); + const auto &attrs = resource.GetAttributes(); + ASSERT_NE(attrs.find("service.name"), attrs.end()); + EXPECT_EQ(nostd::get(attrs.at("service.name")), "from-attributes"); } //------------------------------------------------------------------------------ @@ -1241,6 +256,36 @@ class TestResourceDetector : public opentelemetry::sdk::resource::ResourceDetect std::string schema_url_; }; +class TestContainerResourceDetectorBuilder : public config_sdk::ContainerResourceDetectorBuilder +{ +public: + std::unique_ptr Build( + const config_sdk::ContainerResourceDetectorConfiguration * /* model */) const override + { + called = true; + return std::make_unique( + opentelemetry::sdk::resource::ResourceAttributes{{"container.id", "abc123"}}, + std::string{}); + } + + mutable bool called{false}; +}; + +class TestServiceResourceDetectorBuilder : public config_sdk::ServiceResourceDetectorBuilder +{ +public: + std::unique_ptr Build( + const config_sdk::ServiceResourceDetectorConfiguration * /* model */) const override + { + called = true; + return std::make_unique( + opentelemetry::sdk::resource::ResourceAttributes{{"service.name", "my-service"}}, + std::string{}); + } + + mutable bool called{false}; +}; + class TestProcessResourceDetectorBuilder : public config_sdk::ProcessResourceDetectorBuilder { public: @@ -1290,7 +335,8 @@ std::string GetStringAttribute(const opentelemetry::sdk::resource::Resource &res TEST(SdkBuilder, SetResourceWithoutModel) { - SdkBuilder builder(RegistryFactory::Create()); + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(registry); auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); std::unique_ptr model; @@ -1302,11 +348,11 @@ TEST(SdkBuilder, SetResourceWithoutModel) TEST(SdkBuilder, SetResourceDetectorDispatch) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); auto process_builder = std::make_unique(); auto *process_builder_p = process_builder.get(); registry->SetProcessResourceDetectorBuilder(std::move(process_builder)); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); @@ -1326,11 +372,11 @@ TEST(SdkBuilder, SetResourceDetectorDispatch) TEST(SdkBuilder, SetResourceExtensionDetector) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); auto extension_builder = std::make_unique(); auto *extension_builder_p = extension_builder.get(); registry->SetExtensionResourceDetectorBuilder("my_custom_detector", std::move(extension_builder)); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); @@ -1348,7 +394,8 @@ TEST(SdkBuilder, SetResourceExtensionDetector) TEST(SdkBuilder, SetResourceUnregisteredDetectorFails) { - SdkBuilder builder(RegistryFactory::Create()); + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); @@ -1360,12 +407,113 @@ TEST(SdkBuilder, SetResourceUnregisteredDetectorFails) opentelemetry::sdk::configuration::UnsupportedException); } +TEST(SdkBuilder, SetResourceUnregisteredContainerDetectorFails) +{ + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + model->detection->detectors.push_back( + std::make_unique()); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + EXPECT_THROW(builder.SetResource(resource, model), + opentelemetry::sdk::configuration::UnsupportedException); +} + +TEST(SdkBuilder, SetResourceUnregisteredProcessDetectorFails) +{ + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + model->detection->detectors.push_back( + std::make_unique()); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + EXPECT_THROW(builder.SetResource(resource, model), + opentelemetry::sdk::configuration::UnsupportedException); +} + +TEST(SdkBuilder, SetResourceUnregisteredServiceDetectorFails) +{ + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + model->detection->detectors.push_back( + std::make_unique()); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + EXPECT_THROW(builder.SetResource(resource, model), + opentelemetry::sdk::configuration::UnsupportedException); +} + +TEST(SdkBuilder, SetResourceContainerDetector) +{ + auto registry = std::make_shared(); + auto container_b = std::make_unique(); + auto *container_b_ptr = container_b.get(); + registry->SetContainerResourceDetectorBuilder(std::move(container_b)); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + model->detection->detectors.push_back( + std::make_unique()); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + builder.SetResource(resource, model); + + EXPECT_TRUE(container_b_ptr->called); + EXPECT_EQ(GetStringAttribute(resource, "container.id"), "abc123"); +} + +TEST(SdkBuilder, SetResourceServiceDetector) +{ + auto registry = std::make_shared(); + auto service_b = std::make_unique(); + auto *service_b_ptr = service_b.get(); + registry->SetServiceResourceDetectorBuilder(std::move(service_b)); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + model->detection->detectors.push_back( + std::make_unique()); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + builder.SetResource(resource, model); + + EXPECT_TRUE(service_b_ptr->called); + EXPECT_EQ(GetStringAttribute(resource, "service.name"), "my-service"); +} + +TEST(SdkBuilder, SetResourceUnregisteredExtensionDetectorFails) +{ + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); + + auto model = std::make_unique(); + model->detection = std::make_unique(); + auto ext = std::make_unique(); + ext->name = "unregistered_detector"; + model->detection->detectors.push_back(std::move(ext)); + + auto resource = opentelemetry::sdk::resource::Resource::GetEmpty(); + EXPECT_THROW(builder.SetResource(resource, model), + opentelemetry::sdk::configuration::UnsupportedException); +} + TEST(SdkBuilder, SetResourceDetectionAttributeFilter) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); registry->SetProcessResourceDetectorBuilder( std::make_unique()); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); @@ -1392,10 +540,10 @@ TEST(SdkBuilder, SetResourceDetectionAttributeFilter) TEST(SdkBuilder, SetResourceMergePriority) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); registry->SetProcessResourceDetectorBuilder( std::make_unique()); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); @@ -1424,10 +572,10 @@ TEST(SdkBuilder, SetResourceMergePriority) TEST(SdkBuilder, SetResourceFilterAppliesToDetectedAttributesOnly) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); registry->SetProcessResourceDetectorBuilder( std::make_unique()); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); model->detection = std::make_unique(); diff --git a/sdk/test/configuration/trace_builders_test.cc b/sdk/test/configuration/trace_builders_test.cc index 3a8606087..c6141fc02 100644 --- a/sdk/test/configuration/trace_builders_test.cc +++ b/sdk/test/configuration/trace_builders_test.cc @@ -2,17 +2,270 @@ // SPDX-License-Identifier: Apache-2.0 #include + +#include +#include +#include +#include #include #include +#include +#include + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_context_kv_iterable_view.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/span_metadata.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/trace/tracer_provider.h" +#include "opentelemetry/sdk/configuration/always_off_sampler_builder.h" +#include "opentelemetry/sdk/configuration/always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/always_on_sampler_builder.h" +#include "opentelemetry/sdk/configuration/always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/batch_span_processor_builder.h" +#include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composable_always_off_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composable_always_on_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composable_parent_threshold_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composable_probability_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_patterns_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_attribute_values_configuration.h" +#include "opentelemetry/sdk/configuration/composable_rule_based_sampler_rule_configuration.h" +#include "opentelemetry/sdk/configuration/composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/composite_sampler_builder.h" +#include "opentelemetry/sdk/configuration/composite_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/configuration.h" +#include "opentelemetry/sdk/configuration/configured_sdk.h" +#include "opentelemetry/sdk/configuration/console_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/console_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_composable_sampler_builder.h" +#include "opentelemetry/sdk/configuration/extension_composable_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/extension_sampler_builder.h" +#include "opentelemetry/sdk/configuration/extension_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/extension_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/extension_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/extension_span_processor_builder.h" +#include "opentelemetry/sdk/configuration/extension_span_processor_configuration.h" +#include "opentelemetry/sdk/configuration/jaeger_remote_sampler_builder.h" +#include "opentelemetry/sdk/configuration/jaeger_remote_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_file_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_file_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_grpc_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/otlp_http_span_exporter_builder.h" +#include "opentelemetry/sdk/configuration/otlp_http_span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/parent_based_sampler_builder.h" +#include "opentelemetry/sdk/configuration/parent_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/probability_sampler_builder.h" +#include "opentelemetry/sdk/configuration/probability_sampler_configuration.h" #include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/sampler_configuration.h" +#include "opentelemetry/sdk/configuration/sdk_builder.h" +#include "opentelemetry/sdk/configuration/simple_span_processor_builder.h" +#include "opentelemetry/sdk/configuration/simple_span_processor_configuration.h" +#include "opentelemetry/sdk/configuration/span_exporter_configuration.h" +#include "opentelemetry/sdk/configuration/span_limits_configuration.h" +#include "opentelemetry/sdk/configuration/span_processor_configuration.h" #include "opentelemetry/sdk/configuration/trace_builders.h" +#include "opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_builder.h" +#include "opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h" +#include "opentelemetry/sdk/configuration/tracer_configurator_builder.h" +#include "opentelemetry/sdk/configuration/tracer_configurator_configuration.h" +#include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" +#include "opentelemetry/sdk/configuration/unsupported_exception.h" + +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/samplers/composable_always_on.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/span_limits.h" +#include "opentelemetry/sdk/trace/tracer_config.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" + +#include "config_test_trace.h" + +namespace trace_api = opentelemetry::trace; +namespace scope_sdk = opentelemetry::sdk::instrumentationscope; +namespace config_sdk = opentelemetry::sdk::configuration; +namespace trace_sdk = opentelemetry::sdk::trace; + +namespace +{ + +class TestExtensionComposableSamplerBuilder : public config_sdk::ExtensionComposableSamplerBuilder +{ +public: + std::unique_ptr Build( + const config_sdk::ExtensionComposableSamplerConfiguration *model) const override + { + called = true; + name = model->name; + depth = model->depth; + return std::make_unique(); + } + + mutable bool called{false}; + mutable std::string name; + mutable std::size_t depth{0}; +}; + +class TestExtensionSamplerBuilder : public config_sdk::ExtensionSamplerBuilder +{ +public: + std::unique_ptr Build( + const config_sdk::ExtensionSamplerConfiguration *model) const override + { + called = true; + name = model->name; + // Return AlwaysOn sampler by constructing through the registered builder + return std::make_unique(); + } + + mutable bool called{false}; + mutable std::string name; +}; + +class TestExtensionSpanProcessorBuilder : public config_sdk::ExtensionSpanProcessorBuilder +{ +public: + std::unique_ptr Build( + const config_sdk::ExtensionSpanProcessorConfiguration *model) const override + { + called = true; + name = model->name; + return std::make_unique( + std::make_unique()); + } + + mutable bool called{false}; + mutable std::string name; +}; + +class TraceBuildersTest : public ::testing::Test +{ +protected: + using RuleAttrMap = std::map; -namespace configuration = opentelemetry::sdk::configuration; + trace_sdk::Decision SampleWith(trace_sdk::Sampler &sampler, + const trace_api::SpanContext &parent, + trace_api::SpanKind span_kind, + const RuleAttrMap &attrs) + { + uint8_t trace_buf[trace_api::TraceId::kSize] = {1}; + std::vector>> links; + opentelemetry::common::KeyValueIterableView attrs_view{attrs}; + trace_api::SpanContextKeyValueIterableView links_view{links}; + auto result = sampler.ShouldSample(parent, trace_api::TraceId(trace_buf), "span", span_kind, + attrs_view, links_view); + return result.decision; + } -TEST(TraceBuilders, EmptyRegistryHasNoTraceBuilders) + trace_api::SpanContext MakeRuleParent(bool sampled, bool is_remote) + { + uint8_t trace_buf[trace_api::TraceId::kSize] = {1}; + uint8_t span_buf[trace_api::SpanId::kSize] = {1}; + return trace_api::SpanContext(trace_api::TraceId(trace_buf), trace_api::SpanId(span_buf), + trace_api::TraceFlags(sampled ? 1 : 0), is_remote); + } + void SetUp() override + { + registry_ = std::make_shared(); + config_sdk::RegisterDefaultTraceBuilders(registry_.get()); + registry_->SetConsoleSpanBuilder( + std::make_unique()); + registry_->SetOtlpHttpSpanBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpGrpcSpanBuilder( + std::make_unique(nullptr)); + registry_->SetOtlpFileSpanBuilder( + std::make_unique(nullptr)); + } + + std::shared_ptr MakeTracerProvider( + std::unique_ptr model) + { + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + config_sdk::SdkBuilder builder(registry_); + return builder.CreateTracerProvider(model, resource); + } + + std::unique_ptr MakeSpanProcessor( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateSpanProcessor(model); + } + + std::unique_ptr MakeSpanExporter( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateSpanExporter(model); + } + + std::unique_ptr MakeSampler( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateSampler(model); + } + + std::unique_ptr> MakeTracerConfigurator( + std::unique_ptr model) + { + config_sdk::SdkBuilder builder(registry_); + return builder.CreateTracerConfigurator(model); + } + + std::unique_ptr MakeComposableSampler( + std::unique_ptr model) + { + return MakeSampler(std::move(model)); + } + + // Builds composite(rule_based{[rule]}) where the rule maps to always_on. + std::unique_ptr MakeRuleSampler( + std::unique_ptr rule) + { + rule->sampler = std::make_unique(); + auto rule_based_config = + std::make_unique(); + rule_based_config->rules.push_back(std::move(rule)); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based_config); + std::unique_ptr sampler_config = std::move(composite); + return MakeSampler(std::move(sampler_config)); + } + + std::shared_ptr registry_; +}; + +} // namespace + +TEST_F(TraceBuildersTest, DefaultRegistry) { - auto registry = std::make_shared(); + auto registry = std::make_shared(); EXPECT_EQ(registry->GetAlwaysOnSamplerBuilder(), nullptr); EXPECT_EQ(registry->GetAlwaysOffSamplerBuilder(), nullptr); @@ -31,11 +284,10 @@ TEST(TraceBuilders, EmptyRegistryHasNoTraceBuilders) EXPECT_EQ(registry->GetCompositeSamplerBuilder(), nullptr); } -TEST(TraceBuilders, RegisterDefaultTraceBuildersFillsAllTraceSlots) +TEST_F(TraceBuildersTest, RegisterDefaultTraceBuilders) { - auto registry = std::make_shared(); - - configuration::RegisterDefaultTraceBuilders(registry.get()); + auto registry = std::make_shared(); + config_sdk::RegisterDefaultTraceBuilders(registry.get()); EXPECT_NE(registry->GetAlwaysOnSamplerBuilder(), nullptr); EXPECT_NE(registry->GetAlwaysOffSamplerBuilder(), nullptr); @@ -53,3 +305,1029 @@ TEST(TraceBuilders, RegisterDefaultTraceBuildersFillsAllTraceSlots) EXPECT_NE(registry->GetComposableRuleBasedSamplerBuilder(), nullptr); EXPECT_NE(registry->GetCompositeSamplerBuilder(), nullptr); } + +TEST_F(TraceBuildersTest, SpanLimitsDefaults) +{ + auto model = std::make_unique(); + model->limits = nullptr; + auto provider = MakeTracerProvider(std::move(model)); + ASSERT_NE(provider, nullptr); + + const auto limits = provider->GetSpanLimits(); + const auto default_limits = trace_sdk::SpanLimits{}; + + EXPECT_EQ(limits.attribute_count_limit, default_limits.attribute_count_limit); + EXPECT_EQ(limits.event_count_limit, default_limits.event_count_limit); + EXPECT_EQ(limits.link_count_limit, default_limits.link_count_limit); + EXPECT_EQ(limits.event_attribute_count_limit, default_limits.event_attribute_count_limit); + EXPECT_EQ(limits.link_attribute_count_limit, default_limits.link_attribute_count_limit); + EXPECT_EQ(limits.attribute_value_length_limit, default_limits.attribute_value_length_limit); +} + +TEST_F(TraceBuildersTest, SpanLimitsConfiguration) +{ + auto model = std::make_unique(); + model->limits = std::make_unique(); + model->limits->attribute_value_length_limit = 1111; + model->limits->attribute_count_limit = 2222; + model->limits->event_count_limit = 3333; + model->limits->link_count_limit = 4444; + model->limits->event_attribute_count_limit = 5555; + model->limits->link_attribute_count_limit = 6666; + + auto provider = MakeTracerProvider(std::move(model)); + ASSERT_NE(provider, nullptr); + + auto limits = provider->GetSpanLimits(); + EXPECT_EQ(limits.attribute_value_length_limit, 1111); + EXPECT_EQ(limits.attribute_count_limit, 2222); + EXPECT_EQ(limits.event_count_limit, 3333); + EXPECT_EQ(limits.link_count_limit, 4444); + EXPECT_EQ(limits.event_attribute_count_limit, 5555); + EXPECT_EQ(limits.link_attribute_count_limit, 6666); +} + +TEST_F(TraceBuildersTest, BatchSpanProcessorDispatch) +{ + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + auto result = MakeSpanProcessor(std::move(model)); + EXPECT_NE(result, nullptr); +} + +TEST_F(TraceBuildersTest, SimpleSpanProcessorDispatch) +{ + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + auto result = MakeSpanProcessor(std::move(model)); + EXPECT_NE(result, nullptr); +} + +TEST_F(TraceBuildersTest, CreateParentBasedSampler) +{ + // parent based with no root configured should default to always on + { + auto parent_based_sampler_config = + std::make_unique(); + parent_based_sampler_config->root = nullptr; + + std::unique_ptr sampler_config = + std::move(parent_based_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOnSampler})"); + } + + // parent based with root always on + { + auto parent_based_sampler_config = + std::make_unique(); + parent_based_sampler_config->root = + std::make_unique(); + std::unique_ptr sampler_config = + std::move(parent_based_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOnSampler})"); + } + + // parent based with root always off + { + auto parent_based_sampler_config = + std::make_unique(); + parent_based_sampler_config->root = + std::make_unique(); + std::unique_ptr sampler_config = + std::move(parent_based_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ParentBased{AlwaysOffSampler})"); + } + + // parent based with a custom root sampler + { + auto parent_based_sampler_config = + std::make_unique(); + auto trace_id_ratio_based_sampler_config = + std::make_unique(); + trace_id_ratio_based_sampler_config->ratio = 0.5; + parent_based_sampler_config->root = std::move(trace_id_ratio_based_sampler_config); + std::unique_ptr sampler_config = + std::move(parent_based_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(ParentBased{TraceIdRatioBasedSampler{0.500000}})"); + } + + // parent based with all sub samplers set + { + auto parent_based_sampler_config = + std::make_unique(); + auto trace_id_ratio_based_sampler_config = + std::make_unique(); + trace_id_ratio_based_sampler_config->ratio = 0.25; + parent_based_sampler_config->root = std::move(trace_id_ratio_based_sampler_config); + + auto always_off_sampler_config = std::make_unique(); + parent_based_sampler_config->remote_parent_sampled = std::move(always_off_sampler_config); + + auto always_on_sampler_config = std::make_unique(); + parent_based_sampler_config->remote_parent_not_sampled = std::move(always_on_sampler_config); + + auto trace_id_ratio_based_sampler_config_2 = + std::make_unique(); + trace_id_ratio_based_sampler_config_2->ratio = 0.35; + parent_based_sampler_config->local_parent_sampled = + std::move(trace_id_ratio_based_sampler_config_2); + + auto always_off_sampler_config_2 = + std::make_unique(); + parent_based_sampler_config->local_parent_not_sampled = std::move(always_off_sampler_config_2); + + std::unique_ptr sampler_config = + std::move(parent_based_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(ParentBased{TraceIdRatioBasedSampler{0.250000}})"); + } +} + +TEST_F(TraceBuildersTest, CreateProbabilitySampler) +{ + // default ratio is 1.0 + { + auto probability_sampler_config = + std::make_unique(); + std::unique_ptr sampler_config = + std::move(probability_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ProbabilitySampler{1.000000})"); + } + + // explicit ratio, dispatched through CreateSampler + { + auto probability_sampler_config = + std::make_unique(); + probability_sampler_config->ratio = 0.5; + std::unique_ptr sampler_config = + std::move(probability_sampler_config); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, R"(ProbabilitySampler{0.500000})"); + } +} + +TEST_F(TraceBuildersTest, CreateComposableAlwaysOnSampler) +{ + auto composable_config = std::make_unique(); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(composable_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableAlwaysOnSampler})"); +} + +TEST_F(TraceBuildersTest, CreateComposableAlwaysOffSampler) +{ + auto composable_config = std::make_unique(); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(composable_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableAlwaysOffSampler})"); +} + +TEST_F(TraceBuildersTest, CreateExtensionComposableSampler) +{ + auto extension_builder = std::make_unique(); + auto *extension_builder_ptr = extension_builder.get(); + registry_->SetExtensionComposableSamplerBuilder("custom_composable", + std::move(extension_builder)); + + auto extension_config = std::make_unique(); + extension_config->name = "custom_composable"; + extension_config->depth = 2; + auto composite = std::make_unique(); + composite->composable_sampler = std::move(extension_config); + std::unique_ptr sampler_config = std::move(composite); + + auto sampler = MakeSampler(std::move(sampler_config)); + + ASSERT_NE(sampler, nullptr); + EXPECT_TRUE(extension_builder_ptr->called); + EXPECT_EQ(extension_builder_ptr->name, "custom_composable"); + EXPECT_EQ(extension_builder_ptr->depth, 2); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableAlwaysOnSampler})"); +} + +TEST_F(TraceBuildersTest, CreateUnregisteredExtensionComposableSampler) +{ + auto extension_config = std::make_unique(); + extension_config->name = "missing_composable"; + auto composite = std::make_unique(); + composite->composable_sampler = std::move(extension_config); + std::unique_ptr sampler_config = std::move(composite); + + EXPECT_THROW(MakeSampler(std::move(sampler_config)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, CreateComposableProbabilitySampler) +{ + auto composable_probability_sampler_config = + std::make_unique(); + composable_probability_sampler_config->ratio = 0.25; + auto composite = std::make_unique(); + composite->composable_sampler = std::move(composable_probability_sampler_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableProbabilitySampler{0.250000}})"); +} + +TEST_F(TraceBuildersTest, CreateComposableParentThresholdSampler) +{ + auto root_config = std::make_unique(); + root_config->ratio = 0.25; + auto parent_config = + std::make_unique(); + parent_config->root = std::move(root_config); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(parent_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ( + std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableParentThresholdSampler{ComposableProbabilitySampler{0.250000}}})"); +} + +TEST_F(TraceBuildersTest, CreateComposableParentThresholdSamplerNullRoot) +{ + auto parent_config = + std::make_unique(); + parent_config->root = nullptr; + auto composite = std::make_unique(); + composite->composable_sampler = std::move(parent_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableParentThresholdSampler{ComposableAlwaysOnSampler}})"); +} + +TEST_F(TraceBuildersTest, CreateComposableParentThresholdSamplerNestedDepth3) +{ + auto innermost_config = std::make_unique(); + innermost_config->ratio = 0.25; + + auto middle_config = + std::make_unique(); + middle_config->root = std::move(innermost_config); + + auto outer_config = std::make_unique(); + outer_config->root = std::move(middle_config); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(outer_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ(std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableParentThresholdSampler{ComposableParentThresholdSampler{)" + R"(ComposableProbabilitySampler{0.250000}}}})"); +} + +namespace +{ + +// Builds a chain of parent_threshold nodes ending in an always_on leaf. +// Total nesting depth, root included, is num_parent_nodes + 1. +std::unique_ptr MakeNestedComposableConfig( + std::size_t num_parent_nodes) +{ + std::unique_ptr node = + std::make_unique(); + for (std::size_t i = 0; i < num_parent_nodes; ++i) + { + auto parent = std::make_unique(); + parent->root = std::move(node); + node = std::move(parent); + } + auto composite = std::make_unique(); + composite->composable_sampler = std::move(node); + return composite; +} + +} // namespace + +TEST_F(TraceBuildersTest, CreateComposableSamplerAtMaxDepth) +{ + // 9 parent nodes + leaf = depth 10, the default maximum. + auto sampler_config = MakeNestedComposableConfig(9); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); +} + +TEST_F(TraceBuildersTest, CreateComposableSamplerBeyondMaxDepth) +{ + // 10 parent nodes + leaf = depth 11, exceeding the default maximum. + auto sampler_config = MakeNestedComposableConfig(10); + EXPECT_THROW(MakeSampler(std::move(sampler_config)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, CreateComposableRuleBasedSampler) +{ + auto rule_based_config = std::make_unique(); + + auto rule = std::make_unique(); + auto attribute_values = + std::make_unique(); + attribute_values->key = "http.route"; + attribute_values->values = {"/health"}; + rule->attribute_values = std::move(attribute_values); + rule->sampler = std::make_unique(); + rule_based_config->rules.push_back(std::move(rule)); + + auto fallback = std::make_unique(); + fallback->sampler = std::make_unique(); + rule_based_config->rules.push_back(std::move(fallback)); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based_config); + std::unique_ptr sampler_config = std::move(composite); + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + EXPECT_EQ( + std::string{sampler->GetDescription()}, + R"(CompositeSampler{ComposableRuleBasedSampler{ComposableAlwaysOffSampler,ComposableAlwaysOnSampler}})"); +} + +TEST_F(TraceBuildersTest, RuleBasedPredicateAttributeValues) +{ + using config_sdk::ComposableRuleBasedSamplerRuleAttributeValuesConfiguration; + using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; + using trace_sdk::Decision; + + auto parent = MakeRuleParent(true, true); + + // string attribute, exact match + { + auto rule = std::make_unique(); + auto values = std::make_unique(); + values->key = "http.route"; + values->values = {"/health", "/metrics"}; + rule->attribute_values = std::move(values); + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/health"}}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/users"}}), + Decision::DROP); + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + } + + // non-string attribute matches via string representation + { + auto rule = std::make_unique(); + auto values = std::make_unique(); + values->key = "http.response.status_code"; + values->values = {"404"}; + rule->attribute_values = std::move(values); + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, + {{"http.response.status_code", static_cast(404)}}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, + {{"http.response.status_code", static_cast(200)}}), + Decision::DROP); + } + + // array attribute matches if any element matches + { + auto rule = std::make_unique(); + auto values = std::make_unique(); + values->key = "tags"; + values->values = {"b"}; + rule->attribute_values = std::move(values); + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + std::vector matching_tags{"a", "b"}; + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, + {{"tags", opentelemetry::nostd::span( + matching_tags.data(), matching_tags.size())}}), + Decision::RECORD_AND_SAMPLE); + + std::vector non_matching_tags{"x", "z"}; + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, + {{"tags", opentelemetry::nostd::span( + non_matching_tags.data(), non_matching_tags.size())}}), + Decision::DROP); + } +} + +TEST_F(TraceBuildersTest, RuleBasedPredicateAttributePatterns) +{ + using config_sdk::ComposableRuleBasedSamplerRuleAttributePatternsConfiguration; + using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; + using trace_sdk::Decision; + + auto parent = MakeRuleParent(true, true); + + // included glob, excluded overrides + auto rule = std::make_unique(); + auto patterns = std::make_unique(); + patterns->key = "url.path"; + patterns->included = {"/api/*"}; + patterns->excluded = {"/api/health?"}; + rule->attribute_patterns = std::move(patterns); + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/api/users"}}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/api/healthz"}}), + Decision::DROP); + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"url.path", "/other"}}), + Decision::DROP); + + // excluded only: no included patterns means match-all, then filter by excluded + { + auto excluded_only_rule = std::make_unique(); + auto excluded_only_patterns = + std::make_unique(); + excluded_only_patterns->key = "url.path"; + excluded_only_patterns->excluded = {"/internal/*"}; + excluded_only_rule->attribute_patterns = std::move(excluded_only_patterns); + auto excluded_only_sampler = MakeRuleSampler(std::move(excluded_only_rule)); + ASSERT_NE(excluded_only_sampler, nullptr); + + EXPECT_EQ(SampleWith(*excluded_only_sampler, parent, trace_api::SpanKind::kServer, + {{"url.path", "/internal/x"}}), + Decision::DROP); + EXPECT_EQ(SampleWith(*excluded_only_sampler, parent, trace_api::SpanKind::kServer, + {{"url.path", "/public/x"}}), + Decision::RECORD_AND_SAMPLE); + } +} + +TEST_F(TraceBuildersTest, RuleBasedPredicateSpanKindAndParent) +{ + using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; + using trace_sdk::Decision; + + // span kind + { + auto rule = std::make_unique(); + rule->match_span_kind_server = true; + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + auto parent = MakeRuleParent(true, true); + + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kClient, {}), Decision::DROP); + } + + // parent: remote only + { + auto rule = std::make_unique(); + rule->match_parent_remote = true; + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, false), trace_api::SpanKind::kServer, {}), + Decision::DROP); + EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), + trace_api::SpanKind::kServer, {}), + Decision::DROP); + } + + // parent: local only + { + auto rule = std::make_unique(); + rule->match_parent_local = true; + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, false), trace_api::SpanKind::kServer, {}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), + Decision::DROP); + EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), + trace_api::SpanKind::kServer, {}), + Decision::DROP); + } + + // parent: none only (root spans) + { + auto rule = std::make_unique(); + rule->match_parent_none = true; + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, trace_api::SpanContext::GetInvalid(), + trace_api::SpanKind::kServer, {}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ(SampleWith(*sampler, MakeRuleParent(true, true), trace_api::SpanKind::kServer, {}), + Decision::DROP); + } + + // span kind and attribute values: both conditions must match (AND, not OR) + { + using config_sdk::ComposableRuleBasedSamplerRuleAttributeValuesConfiguration; + + auto rule = std::make_unique(); + rule->match_span_kind_server = true; + auto values = std::make_unique(); + values->key = "http.route"; + values->values = {"/health"}; + rule->attribute_values = std::move(values); + auto sampler = MakeRuleSampler(std::move(rule)); + ASSERT_NE(sampler, nullptr); + auto parent = MakeRuleParent(true, true); + + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/health"}}), + Decision::RECORD_AND_SAMPLE); + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {{"http.route", "/other"}}), + Decision::DROP); + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + EXPECT_EQ( + SampleWith(*sampler, parent, trace_api::SpanKind::kClient, {{"http.route", "/health"}}), + Decision::DROP); + } +} + +TEST_F(TraceBuildersTest, RuleBasedFirstMatchAndDefaults) +{ + using config_sdk::ComposableRuleBasedSamplerRuleConfiguration; + using trace_sdk::Decision; + + auto parent = MakeRuleParent(true, true); + + // first match wins: rule 1 (no conditions -> matches all) is always_off, rule 2 always_on + { + auto rule_based_config = + std::make_unique(); + auto first = std::make_unique(); + first->sampler = std::make_unique(); + rule_based_config->rules.push_back(std::move(first)); + auto second = std::make_unique(); + second->sampler = std::make_unique(); + rule_based_config->rules.push_back(std::move(second)); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based_config); + std::unique_ptr sampler_config = std::move(composite); + + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + } + + // empty rules -> nothing matches -> drop + { + auto rule_based_config = + std::make_unique(); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based_config); + std::unique_ptr sampler_config = std::move(composite); + + auto sampler = MakeSampler(std::move(sampler_config)); + ASSERT_NE(sampler, nullptr); + + EXPECT_EQ(SampleWith(*sampler, parent, trace_api::SpanKind::kServer, {}), Decision::DROP); + } +} + +TEST_F(TraceBuildersTest, RuleBasedSamplerIgnoresRuleWithNullSampler) +{ + auto rule = std::make_unique(); + rule->sampler = nullptr; + auto rule_based = std::make_unique(); + rule_based->rules.push_back(std::move(rule)); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based); + std::unique_ptr model = std::move(composite); + + auto sampler = MakeSampler(std::move(model)); + EXPECT_NE(sampler, nullptr); +} + +TEST_F(TraceBuildersTest, RegisteredExtensionSamplerBuilder) +{ + auto builder = std::make_unique(); + auto *builder_p = builder.get(); + registry_->SetExtensionSamplerBuilder("custom_sampler", std::move(builder)); + + auto ext = std::make_unique(); + ext->name = "custom_sampler"; + std::unique_ptr model = std::move(ext); + auto sampler = MakeSampler(std::move(model)); + + ASSERT_NE(sampler, nullptr); + EXPECT_TRUE(builder_p->called); + EXPECT_EQ(builder_p->name, "custom_sampler"); +} + +TEST_F(TraceBuildersTest, RegisteredExtensionSpanProcessorBuilder) +{ + auto builder = std::make_unique(); + auto *builder_p = builder.get(); + registry_->SetExtensionSpanProcessorBuilder("custom_processor", std::move(builder)); + + auto ext = std::make_unique(); + ext->name = "custom_processor"; + std::unique_ptr model = std::move(ext); + auto processor = MakeSpanProcessor(std::move(model)); + + ASSERT_NE(processor, nullptr); + EXPECT_TRUE(builder_p->called); + EXPECT_EQ(builder_p->name, "custom_processor"); +} + +// --------------------------------------------------------------------------- +// Unregistered builder slots: verify each sampler and configurator slot throws. + +TEST_F(TraceBuildersTest, UnregisteredAlwaysOnSamplerBuilder) +{ + registry_->SetAlwaysOnSamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredAlwaysOffSamplerBuilder) +{ + registry_->SetAlwaysOffSamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredProbabilitySamplerBuilder) +{ + registry_->SetProbabilitySamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredTraceIdRatioBasedSamplerBuilder) +{ + registry_->SetTraceIdRatioBasedSamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredParentBasedSamplerBuilder) +{ + registry_->SetParentBasedSamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredJaegerRemoteSamplerBuilder) +{ + registry_->SetJaegerRemoteSamplerBuilder(nullptr); + std::unique_ptr model = + std::make_unique(); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredCompositeSamplerBuilder) +{ + registry_->SetCompositeSamplerBuilder(nullptr); + + auto composite = std::make_unique(); + composite->composable_sampler = + std::make_unique(); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredExtensionSamplerBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + std::unique_ptr model = std::move(ext); + EXPECT_THROW(MakeSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredTracerConfiguratorBuilder) +{ + registry_->SetTracerConfiguratorBuilder(nullptr); + auto model = std::make_unique(); + EXPECT_THROW(MakeTracerConfigurator(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredExtensionSpanExporterBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + std::unique_ptr model = std::move(ext); + EXPECT_THROW(MakeSpanExporter(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredBatchSpanProcessorBuilder) +{ + registry_->SetBatchSpanProcessorBuilder(nullptr); + + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + EXPECT_THROW(MakeSpanProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredSimpleSpanProcessorBuilder) +{ + registry_->SetSimpleSpanProcessorBuilder(nullptr); + + auto exporter = std::make_unique(); + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + std::unique_ptr model = std::move(processor); + + EXPECT_THROW(MakeSpanProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredExtensionSpanProcessorBuilder) +{ + auto ext = std::make_unique(); + ext->name = "unregistered"; + std::unique_ptr model = std::move(ext); + EXPECT_THROW(MakeSpanProcessor(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredConsoleSpanExporterBuilder) +{ + registry_->SetConsoleSpanBuilder(nullptr); + EXPECT_THROW(MakeSpanExporter(std::make_unique()), + config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredOtlpHttpSpanExporterBuilder) +{ + registry_->SetOtlpHttpSpanBuilder(nullptr); + EXPECT_THROW(MakeSpanExporter(std::make_unique()), + config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredOtlpGrpcSpanExporterBuilder) +{ + registry_->SetOtlpGrpcSpanBuilder(nullptr); + EXPECT_THROW(MakeSpanExporter(std::make_unique()), + config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredOtlpFileSpanExporterBuilder) +{ + registry_->SetOtlpFileSpanBuilder(nullptr); + EXPECT_THROW(MakeSpanExporter(std::make_unique()), + config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableAlwaysOnSamplerBuilder) +{ + registry_->SetComposableAlwaysOnSamplerBuilder(nullptr); + + auto composite = std::make_unique(); + composite->composable_sampler = + std::make_unique(); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableAlwaysOffSamplerBuilder) +{ + registry_->SetComposableAlwaysOffSamplerBuilder(nullptr); + + auto composite = std::make_unique(); + composite->composable_sampler = + std::make_unique(); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableProbabilitySamplerBuilder) +{ + registry_->SetComposableProbabilitySamplerBuilder(nullptr); + + auto composite = std::make_unique(); + composite->composable_sampler = + std::make_unique(); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableParentThresholdSamplerBuilder) +{ + registry_->SetComposableParentThresholdSamplerBuilder(nullptr); + + auto parent_config = + std::make_unique(); + parent_config->root = std::make_unique(); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(parent_config); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableRuleBasedSamplerBuilder) +{ + registry_->SetComposableRuleBasedSamplerBuilder(nullptr); + + auto rule_based = std::make_unique(); + auto composite = std::make_unique(); + composite->composable_sampler = std::move(rule_based); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +TEST_F(TraceBuildersTest, UnregisteredComposableAlwaysOnBuilderInParentThreshold) +{ + // null root triggers a default AlwaysOn child; clearing that builder must throw. + registry_->SetComposableAlwaysOnSamplerBuilder(nullptr); + + auto parent = std::make_unique(); + parent->root = nullptr; + auto composite = std::make_unique(); + composite->composable_sampler = std::move(parent); + std::unique_ptr model = std::move(composite); + + EXPECT_THROW(MakeComposableSampler(std::move(model)), config_sdk::UnsupportedException); +} + +// --------------------------------------------------------------------------- +// TracerProviderConfigTest: Use ConfiguredSdk::Create to verify tracer provider configuration and +// installation. + +namespace +{ + +class TracerProviderConfigTest : public ::testing::Test +{ +protected: + void SetUp() override + { + registry_ = std::make_shared(); + span_buffer_ = std::make_shared(); + config_sdk::RegisterDefaultTraceBuilders(registry_.get()); + } + + void TearDown() override + { + if (sdk_) + { + sdk_->UnInstall(); + } + } + + static std::unique_ptr MakeTracerProviderConfig( + std::unique_ptr exporter) + { + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + return MakeTracerProviderConfig(std::move(processor)); + } + + static std::unique_ptr MakeTracerProviderConfig( + std::unique_ptr processor) + { + auto tp = std::make_unique(); + tp->processors.emplace_back(std::move(processor)); + auto model = std::make_unique(); + model->tracer_provider = std::move(tp); + return model; + } + + void BuildAndInstall(const std::unique_ptr &model) + { + sdk_ = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk_, nullptr); + sdk_->Install(); + } + + void EmitAndVerify() + { + ASSERT_NE(sdk_->tracer_provider, nullptr); + ASSERT_EQ(sdk_->meter_provider, nullptr); + ASSERT_EQ(sdk_->logger_provider, nullptr); + auto provider = trace_api::Provider::GetTracerProvider(); + ASSERT_NE(provider, nullptr); + provider->GetTracer("tracer")->StartSpan("span")->End(); + ASSERT_TRUE(sdk_->tracer_provider->ForceFlush(std::chrono::milliseconds(5000))); + ASSERT_TRUE(sdk_->tracer_provider->Shutdown(std::chrono::milliseconds(5000))); + EXPECT_EQ(span_buffer_->size(), 1u); + } + + std::shared_ptr registry_; + std::shared_ptr span_buffer_; + std::unique_ptr sdk_; +}; + +} // namespace + +TEST_F(TracerProviderConfigTest, TracerProviderWithExtensionSpanExporter) +{ + registry_->SetExtensionSpanExporterBuilder( + "recording", std::make_unique(span_buffer_)); + + auto exporter = std::make_unique(); + exporter->name = "recording"; + auto model = MakeTracerProviderConfig(std::move(exporter)); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(TracerProviderConfigTest, TracerProviderWithConsoleSpanExporter) +{ + registry_->SetConsoleSpanBuilder( + std::make_unique(span_buffer_)); + auto model = + MakeTracerProviderConfig(std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(TracerProviderConfigTest, TracerProviderWithOtlpHttpSpanExporter) +{ + registry_->SetOtlpHttpSpanBuilder( + std::make_unique(span_buffer_)); + auto model = + MakeTracerProviderConfig(std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(TracerProviderConfigTest, TracerProviderWithOtlpGrpcSpanExporter) +{ + registry_->SetOtlpGrpcSpanBuilder( + std::make_unique(span_buffer_)); + auto model = + MakeTracerProviderConfig(std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(TracerProviderConfigTest, TracerProviderWithOtlpFileSpanExporter) +{ + registry_->SetOtlpFileSpanBuilder( + std::make_unique(span_buffer_)); + auto model = + MakeTracerProviderConfig(std::make_unique()); + BuildAndInstall(model); + EmitAndVerify(); +} + +TEST_F(TracerProviderConfigTest, TracerProviderWithBatchSpanProcessor) +{ + registry_->SetBatchSpanProcessorBuilder( + std::make_unique()); + registry_->SetExtensionSpanExporterBuilder( + "recording", std::make_unique(span_buffer_)); + + auto exporter = std::make_unique(); + exporter->name = "recording"; + auto processor = std::make_unique(); + processor->exporter = std::move(exporter); + auto model = MakeTracerProviderConfig(std::move(processor)); + BuildAndInstall(model); + EmitAndVerify(); +} From 52ffdb99589162b09e8dd8f2e9fe4b206dda7409 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 15:51:17 -0400 Subject: [PATCH 05/11] remove sdk builder use in the yaml metrics test --- sdk/test/configuration/yaml_metrics_test.cc | 47 --------------------- 1 file changed, 47 deletions(-) diff --git a/sdk/test/configuration/yaml_metrics_test.cc b/sdk/test/configuration/yaml_metrics_test.cc index d1119662b..778c716ac 100644 --- a/sdk/test/configuration/yaml_metrics_test.cc +++ b/sdk/test/configuration/yaml_metrics_test.cc @@ -7,8 +7,6 @@ #include #include -#include "opentelemetry/common/key_value_iterable_view.h" -#include "opentelemetry/nostd/utility.h" #include "opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h" #include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" @@ -31,8 +29,6 @@ #include "opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h" #include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h" #include "opentelemetry/sdk/configuration/pull_metric_reader_configuration.h" -#include "opentelemetry/sdk/configuration/registry.h" -#include "opentelemetry/sdk/configuration/sdk_builder.h" #include "opentelemetry/sdk/configuration/string_array_configuration.h" #include "opentelemetry/sdk/configuration/temporality_preference.h" #include "opentelemetry/sdk/configuration/translation_strategy.h" @@ -40,7 +36,6 @@ #include "opentelemetry/sdk/configuration/view_selector_configuration.h" #include "opentelemetry/sdk/configuration/view_stream_configuration.h" #include "opentelemetry/sdk/configuration/yaml_configuration_parser.h" -#include "opentelemetry/sdk/metrics/view/attributes_processor.h" static std::unique_ptr DoParse( const std::string &yaml) @@ -1405,48 +1400,6 @@ file_format: "1.0-metrics" ASSERT_EQ(view->stream->attribute_keys->excluded->string_array[1], "bar.ex"); } -class YamlMetricsEmptyIncluded : public ::testing::TestWithParam -{}; - -TEST_P(YamlMetricsEmptyIncluded, RetainsAllAttributes) -{ - std::string yaml = R"( -file_format: "1.0-metrics" -meter_provider: - readers: - - periodic: - exporter: - console: - views: - - selector: - stream: - attribute_keys: - included: )"; - yaml.append(GetParam()); - yaml.push_back('\n'); - - auto config = DoParse(yaml); - ASSERT_NE(config, nullptr); - ASSERT_NE(config->meter_provider, nullptr); - ASSERT_EQ(config->meter_provider->views.size(), 1u); - auto &model = config->meter_provider->views[0]->stream->attribute_keys; - ASSERT_NE(model, nullptr); - ASSERT_NE(model->included, nullptr); - ASSERT_TRUE(model->included->string_array.empty()); - - std::map attributes = {{"first", 1}, {"second", 2}}; - opentelemetry::common::KeyValueIterableView> iterable(attributes); - opentelemetry::sdk::configuration::SdkBuilder builder( - std::make_shared()); - auto processor = builder.CreateAttributesProcessor(model); - ASSERT_NE(processor, nullptr); - - auto filtered = processor->process(iterable); - EXPECT_EQ(filtered.size(), attributes.size()); -} - -INSTANTIATE_TEST_SUITE_P(EmptyIncludedForms, YamlMetricsEmptyIncluded, ::testing::Values("[]", "")); - TEST(YamlMetrics, no_meter_configurator) { std::string yaml = R"( From 5f69e57eaef36acc0fd8359c31f3f760a218a51f Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 16:00:23 -0400 Subject: [PATCH 06/11] add a propagator builders test --- .../configuration/propagator_builders_test.cc | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 sdk/test/configuration/propagator_builders_test.cc diff --git a/sdk/test/configuration/propagator_builders_test.cc b/sdk/test/configuration/propagator_builders_test.cc new file mode 100644 index 000000000..df4b1830e --- /dev/null +++ b/sdk/test/configuration/propagator_builders_test.cc @@ -0,0 +1,151 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include "opentelemetry/baggage/baggage_context.h" +#include "opentelemetry/context/propagation/text_map_propagator.h" +#include "opentelemetry/nostd/function_ref.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/propagation/b3_propagator.h" +#include "opentelemetry/trace/propagation/http_trace_context.h" +#include "opentelemetry/trace/propagation/jaeger.h" + +#include "opentelemetry/sdk/configuration/configuration.h" +#include "opentelemetry/sdk/configuration/configured_sdk.h" +#include "opentelemetry/sdk/configuration/propagator_configuration.h" +#include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/text_map_propagator_builder.h" + +namespace config_sdk = opentelemetry::sdk::configuration; + +namespace +{ +// --------------------------------------------------------------------------- +// PropagatorBuilderTest: Test that builders are registerd and match the expected propagator type + +class PropagatorBuilderTest : public ::testing::Test +{ +protected: + void SetUp() override { registry_ = std::make_shared(); } + + static std::unique_ptr MakePropagatorConfig(const std::string &name) + { + auto model = std::make_unique(); + model->propagator = std::make_unique(); + model->propagator->composite_list = name; + return model; + } + + std::shared_ptr registry_; + + static bool HasField(const opentelemetry::context::propagation::TextMapPropagator *propagator, + opentelemetry::nostd::string_view key) + { + bool found = false; + propagator->Fields([&](opentelemetry::nostd::string_view f) { + if (f == key) + { + found = true; + } + return !found; + }); + return found; + } +}; + +} // namespace + +TEST(PropagatorBuilders, DefaultRegistry) +{ + auto registry = std::make_shared(); + EXPECT_NE(registry->GetTextMapPropagatorBuilder("tracecontext"), nullptr); + EXPECT_NE(registry->GetTextMapPropagatorBuilder("baggage"), nullptr); + EXPECT_NE(registry->GetTextMapPropagatorBuilder("b3"), nullptr); + EXPECT_NE(registry->GetTextMapPropagatorBuilder("b3multi"), nullptr); + EXPECT_NE(registry->GetTextMapPropagatorBuilder("jaeger"), nullptr); +} + +TEST_F(PropagatorBuilderTest, TraceContextPropagatorBuilder) +{ + auto model = MakePropagatorConfig("tracecontext"); + auto sdk = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk, nullptr); + ASSERT_NE(sdk->propagator, nullptr); + EXPECT_TRUE(HasField(sdk->propagator.get(), opentelemetry::trace::propagation::kTraceParent)); +} + +TEST_F(PropagatorBuilderTest, BaggagePropagatorBuilder) +{ + auto model = MakePropagatorConfig("baggage"); + auto sdk = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk, nullptr); + ASSERT_NE(sdk->propagator, nullptr); + EXPECT_TRUE(HasField(sdk->propagator.get(), opentelemetry::baggage::kBaggageHeader)); +} + +TEST_F(PropagatorBuilderTest, B3PropagatorBuilder) +{ + auto model = MakePropagatorConfig("b3"); + auto sdk = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk, nullptr); + ASSERT_NE(sdk->propagator, nullptr); + EXPECT_TRUE( + HasField(sdk->propagator.get(), opentelemetry::trace::propagation::kB3CombinedHeader)); +} + +TEST_F(PropagatorBuilderTest, B3MultiPropagatorBuilder) +{ + auto model = MakePropagatorConfig("b3multi"); + auto sdk = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk, nullptr); + ASSERT_NE(sdk->propagator, nullptr); + EXPECT_TRUE(HasField(sdk->propagator.get(), opentelemetry::trace::propagation::kB3TraceIdHeader)); +} + +TEST_F(PropagatorBuilderTest, JaegerPropagatorBuilder) +{ + auto model = MakePropagatorConfig("jaeger"); + auto sdk = config_sdk::ConfiguredSdk::Create(registry_, model); + ASSERT_NE(sdk, nullptr); + ASSERT_NE(sdk->propagator, nullptr); + EXPECT_TRUE( + HasField(sdk->propagator.get(), opentelemetry::trace::propagation::kJaegerTraceHeader)); +} + +TEST_F(PropagatorBuilderTest, UnregisteredTraceContextPropagatorBuilder) +{ + registry_->SetTextMapPropagatorBuilder("tracecontext", nullptr); + auto model = MakePropagatorConfig("tracecontext"); + EXPECT_EQ(config_sdk::ConfiguredSdk::Create(registry_, model), nullptr); +} + +TEST_F(PropagatorBuilderTest, UnregisteredBaggagePropagatorBuilder) +{ + registry_->SetTextMapPropagatorBuilder("baggage", nullptr); + auto model = MakePropagatorConfig("baggage"); + EXPECT_EQ(config_sdk::ConfiguredSdk::Create(registry_, model), nullptr); +} + +TEST_F(PropagatorBuilderTest, UnregisteredB3PropagatorBuilder) +{ + registry_->SetTextMapPropagatorBuilder("b3", nullptr); + auto model = MakePropagatorConfig("b3"); + EXPECT_EQ(config_sdk::ConfiguredSdk::Create(registry_, model), nullptr); +} + +TEST_F(PropagatorBuilderTest, UnregisteredB3MultiPropagatorBuilder) +{ + registry_->SetTextMapPropagatorBuilder("b3multi", nullptr); + auto model = MakePropagatorConfig("b3multi"); + EXPECT_EQ(config_sdk::ConfiguredSdk::Create(registry_, model), nullptr); +} + +TEST_F(PropagatorBuilderTest, UnregisteredJaegerPropagatorBuilder) +{ + registry_->SetTextMapPropagatorBuilder("jaeger", nullptr); + auto model = MakePropagatorConfig("jaeger"); + EXPECT_EQ(config_sdk::ConfiguredSdk::Create(registry_, model), nullptr); +} From dcf87cb8ca5284f6603830a0eb41154de7336184 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 16:06:00 -0400 Subject: [PATCH 07/11] update cmake and bazel build files --- sdk/test/configuration/BUILD | 30 ++++++++++++++++++++++++--- sdk/test/configuration/CMakeLists.txt | 12 ++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/sdk/test/configuration/BUILD b/sdk/test/configuration/BUILD index 67fa143c3..62feed5c3 100644 --- a/sdk/test/configuration/BUILD +++ b/sdk/test/configuration/BUILD @@ -26,7 +26,6 @@ cc_test( cc_test( name = "sdk_builder_test", srcs = [ - "config_test_common.h", "sdk_builder_test.cc", ], tags = [ @@ -48,7 +47,9 @@ cc_test( cc_test( name = "configured_sdk_test", srcs = [ - "config_test_common.h", + "config_test_logs.h", + "config_test_metrics.h", + "config_test_trace.h", "configured_sdk_test.cc", ], tags = [ @@ -69,7 +70,10 @@ cc_test( cc_test( name = "programmatic_configuration_test", srcs = [ - "config_test_common.h", + "config_test_logs.h", + "config_test_metrics.h", + "config_test_propagators.h", + "config_test_trace.h", "programmatic_configuration_test.cc", ], tags = [ @@ -90,6 +94,7 @@ cc_test( cc_test( name = "trace_builders_test", srcs = [ + "config_test_trace.h", "trace_builders_test.cc", ], tags = [ @@ -107,6 +112,7 @@ cc_test( cc_test( name = "logs_builders_test", srcs = [ + "config_test_logs.h", "logs_builders_test.cc", ], tags = [ @@ -124,6 +130,7 @@ cc_test( cc_test( name = "metrics_builders_test", srcs = [ + "config_test_metrics.h", "metrics_builders_test.cc", ], tags = [ @@ -134,6 +141,23 @@ cc_test( "//sdk:headers", "//sdk/src/configuration:configuration_metrics_builders", "//sdk/src/metrics", + "//test_common:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "propagator_builders_test", + srcs = [ + "propagator_builders_test.cc", + ], + tags = [ + "config", + "test", + ], + deps = [ + "//sdk:headers", + "//sdk/src/configuration:configuration_core", "@com_google_googletest//:gtest_main", ], ) diff --git a/sdk/test/configuration/CMakeLists.txt b/sdk/test/configuration/CMakeLists.txt index 51db62486..0d1c495fb 100644 --- a/sdk/test/configuration/CMakeLists.txt +++ b/sdk/test/configuration/CMakeLists.txt @@ -37,12 +37,22 @@ target_link_libraries( metrics_builders_test PRIVATE ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_configuration_metrics_builders - opentelemetry_configuration_core) + opentelemetry_configuration_core opentelemetry_test_common) gtest_add_tests( TARGET metrics_builders_test TEST_PREFIX config. TEST_LIST metrics_builders_test) +add_executable(propagator_builders_test propagator_builders_test.cc) +target_link_libraries( + propagator_builders_test + PRIVATE ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_configuration_core) +gtest_add_tests( + TARGET propagator_builders_test + TEST_PREFIX config. + TEST_LIST propagator_builders_test) + foreach(testname sdk_builder_test configured_sdk_test programmatic_configuration_test) add_executable(${testname} "${testname}.cc") From 5fcef5ac7d7aab85d6f0a9d59fa81ba58826e089 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 16:06:19 -0400 Subject: [PATCH 08/11] remove the common test header which is no longer used and cleanup --- sdk/test/configuration/config_test_common.h | 485 -------------------- 1 file changed, 485 deletions(-) delete mode 100644 sdk/test/configuration/config_test_common.h diff --git a/sdk/test/configuration/config_test_common.h b/sdk/test/configuration/config_test_common.h deleted file mode 100644 index a8db8544d..000000000 --- a/sdk/test/configuration/config_test_common.h +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -// Shared test helpers for the SDK configuration tests -// -// Provides: -// Noop Exporters and Builders: -// - NoopSpanExporter / NoopSpanExporterBuilder -// - NoopLogRecordExporter / NoopLogRecordExporterBuilder -// - NoopPushMetricExporter / NoopPeriodicMetricReaderBuilder -// -// Recording Exporters and Builders: (data collection with no threading) -// - RecordingLogRecordExporter / RecordingLogRecordExporterBuilder -// - RecordingSpanExporter / RecordingSpanExporterBuilder -// - SyncMetricReader / SyncPeriodicMetricReaderBuilder -// - CapturedPeriodicReaderArgs / CapturingPeriodicMetricReaderBuilder -// -// Propagator: -// - MapCarrier - -#pragma once - -#include -#include -#include -#include - -#include "opentelemetry/context/propagation/text_map_propagator.h" -#include "opentelemetry/nostd/span.h" -#include "opentelemetry/sdk/common/exporter_utils.h" -#include "opentelemetry/sdk/configuration/batch_log_record_processor_builder.h" -#include "opentelemetry/sdk/configuration/batch_log_record_processor_configuration.h" -#include "opentelemetry/sdk/configuration/batch_span_processor_builder.h" -#include "opentelemetry/sdk/configuration/batch_span_processor_configuration.h" -#include "opentelemetry/sdk/configuration/extension_log_record_exporter_builder.h" -#include "opentelemetry/sdk/configuration/extension_log_record_exporter_configuration.h" -#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_builder.h" -#include "opentelemetry/sdk/configuration/extension_push_metric_exporter_configuration.h" -#include "opentelemetry/sdk/configuration/extension_span_exporter_builder.h" -#include "opentelemetry/sdk/configuration/extension_span_exporter_configuration.h" -#include "opentelemetry/sdk/configuration/periodic_metric_reader_builder.h" -#include "opentelemetry/sdk/configuration/periodic_metric_reader_configuration.h" -#include "opentelemetry/sdk/configuration/simple_log_record_processor_configuration.h" -#include "opentelemetry/sdk/configuration/simple_span_processor_configuration.h" -#include "opentelemetry/sdk/logs/exporter.h" -#include "opentelemetry/sdk/logs/read_write_log_record.h" -#include "opentelemetry/sdk/logs/simple_log_record_processor.h" -#include "opentelemetry/sdk/metrics/data/metric_data.h" -#include "opentelemetry/sdk/metrics/export/metric_producer.h" -#include "opentelemetry/sdk/metrics/instruments.h" -#include "opentelemetry/sdk/metrics/metric_reader.h" -#include "opentelemetry/sdk/metrics/push_metric_exporter.h" -#include "opentelemetry/sdk/trace/exporter.h" -#include "opentelemetry/sdk/trace/simple_processor.h" -#include "opentelemetry/sdk/trace/span_data.h" - -namespace config_test -{ - -// --------------------------------------------------------------------------- -// Export buffer type aliases - -using SpanBuffer = std::vector>; -using LogRecordBuffer = std::vector>; -using MetricBuffer = std::vector; - -// --------------------------------------------------------------------------- -// No-op exporters - -class NoopSpanExporter : public opentelemetry::sdk::trace::SpanExporter -{ -public: - std::unique_ptr MakeRecordable() noexcept override - { - return std::make_unique(); - } - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::nostd::span> - &) noexcept override - { - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } -}; - -class NoopLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExporter -{ -public: - std::unique_ptr MakeRecordable() noexcept override - { - return std::make_unique(); - } - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::nostd::span> - &) noexcept override - { - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } -}; - -class NoopPushMetricExporter : public opentelemetry::sdk::metrics::PushMetricExporter -{ -public: - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::sdk::metrics::ResourceMetrics &) noexcept override - { - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( - opentelemetry::sdk::metrics::InstrumentType) const noexcept override - { - return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; - } - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } -}; - -// --------------------------------------------------------------------------- -// No-op extension builders - -class NoopSpanExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionSpanExporterBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionSpanExporterConfiguration *) const override - { - return std::make_unique(); - } -}; - -class NoopLogRecordExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionLogRecordExporterBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionLogRecordExporterConfiguration *) - const override - { - return std::make_unique(); - } -}; - -class NoopPushMetricExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionPushMetricExporterBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionPushMetricExporterConfiguration *) - const override - { - return std::make_unique(); - } -}; - -// --------------------------------------------------------------------------- -// No-op metric reader - -class NoopMetricReader : public opentelemetry::sdk::metrics::MetricReader -{ -public: - opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( - opentelemetry::sdk::metrics::InstrumentType) const noexcept override - { - return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; - } - -private: - bool OnForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool OnShutDown(std::chrono::microseconds) noexcept override { return true; } -}; - -class NoopPeriodicMetricReaderBuilder - : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *, - std::unique_ptr &&exporter) const override - { - auto unused = std::move(exporter); - return std::make_unique(); - } -}; - -// --------------------------------------------------------------------------- -// Recording exporters: capture exported data into shared buffers for -// inspection in integration tests. - -class RecordingSpanExporter : public opentelemetry::sdk::trace::SpanExporter -{ -public: - explicit RecordingSpanExporter(std::shared_ptr buffer) : buffer_(std::move(buffer)) {} - - std::unique_ptr MakeRecordable() noexcept override - { - return std::make_unique(); - } - - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::nostd::span> - &spans) noexcept override - { - for (auto &span : spans) - { - buffer_->emplace_back(static_cast(span.release())); - } - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } - -private: - std::shared_ptr buffer_; -}; - -class RecordingLogRecordExporter : public opentelemetry::sdk::logs::LogRecordExporter -{ -public: - explicit RecordingLogRecordExporter(std::shared_ptr buffer) - : buffer_(std::move(buffer)) - {} - - std::unique_ptr MakeRecordable() noexcept override - { - return std::make_unique(); - } - - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::nostd::span> - &records) noexcept override - { - for (auto &rec : records) - { - buffer_->emplace_back( - static_cast(rec.release())); - } - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - - bool RecordableEnforcesLogRecordLimits() const noexcept override { return true; } - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } - -private: - std::shared_ptr buffer_; -}; - -class RecordingPushMetricExporter : public opentelemetry::sdk::metrics::PushMetricExporter -{ -public: - explicit RecordingPushMetricExporter(std::shared_ptr buffer) - : buffer_(std::move(buffer)) - {} - - opentelemetry::sdk::common::ExportResult Export( - const opentelemetry::sdk::metrics::ResourceMetrics &resource_metrics) noexcept override - { - for (const auto &scope : resource_metrics.scope_metric_data_) - { - for (const auto &metric : scope.metric_data_) - { - buffer_->emplace_back(metric); - } - } - return opentelemetry::sdk::common::ExportResult::kSuccess; - } - - opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( - opentelemetry::sdk::metrics::InstrumentType) const noexcept override - { - return opentelemetry::sdk::metrics::AggregationTemporality::kCumulative; - } - - bool ForceFlush(std::chrono::microseconds) noexcept override { return true; } - bool Shutdown(std::chrono::microseconds) noexcept override { return true; } - -private: - std::shared_ptr buffer_; -}; - -// --------------------------------------------------------------------------- -// Recording extension builders - -class RecordingSpanExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionSpanExporterBuilder -{ -public: - explicit RecordingSpanExporterBuilder(std::shared_ptr buffer) - : buffer_(std::move(buffer)) - {} - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionSpanExporterConfiguration *) const override - { - return std::make_unique(buffer_); - } - -private: - std::shared_ptr buffer_; -}; - -class RecordingLogRecordExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionLogRecordExporterBuilder -{ -public: - explicit RecordingLogRecordExporterBuilder(std::shared_ptr buffer) - : buffer_(std::move(buffer)) - {} - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionLogRecordExporterConfiguration *) - const override - { - return std::make_unique(buffer_); - } - -private: - std::shared_ptr buffer_; -}; - -class RecordingPushMetricExporterBuilder - : public opentelemetry::sdk::configuration::ExtensionPushMetricExporterBuilder -{ -public: - explicit RecordingPushMetricExporterBuilder(std::shared_ptr buffer) - : buffer_(std::move(buffer)) - {} - std::unique_ptr Build( - const opentelemetry::sdk::configuration::ExtensionPushMetricExporterConfiguration *) - const override - { - return std::make_unique(buffer_); - } - -private: - std::shared_ptr buffer_; -}; - -// --------------------------------------------------------------------------- -// Synchronous metric reader: collects and exports on ForceFlush in -// the calling thread. -class SyncMetricReader : public opentelemetry::sdk::metrics::MetricReader -{ -public: - explicit SyncMetricReader( - std::unique_ptr exporter) - : exporter_(std::move(exporter)) - {} - - opentelemetry::sdk::metrics::AggregationTemporality GetAggregationTemporality( - opentelemetry::sdk::metrics::InstrumentType instrument_type) const noexcept override - { - return exporter_->GetAggregationTemporality(instrument_type); - } - -private: - bool CollectAndExport() noexcept - { - const bool success = Collect([this](opentelemetry::sdk::metrics::ResourceMetrics &metric_data) { - return (exporter_->Export(metric_data) == opentelemetry::sdk::common::ExportResult::kSuccess); - }); - return success; - } - - bool OnForceFlush(std::chrono::microseconds timeout) noexcept override - { - const bool collect_result = CollectAndExport(); - const bool flush_result = exporter_->ForceFlush(timeout); - return collect_result && flush_result; - } - - bool OnShutDown(std::chrono::microseconds timeout) noexcept override - { - return exporter_->Shutdown(timeout); - } - - std::unique_ptr exporter_; -}; - -class SyncPeriodicMetricReaderBuilder - : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *, - std::unique_ptr &&exporter) const override - { - return std::make_unique(std::move(exporter)); - } -}; - -// --------------------------------------------------------------------------- -// Capturing periodic metric reader builder. Records the configuration -// arguments passed to Build() - -struct CapturedPeriodicReaderArgs -{ - std::size_t interval{0}; - std::size_t timeout{0}; - std::unique_ptr exporter; - // TODO: add cardinality limits and producers when we support them in the builder - bool called{false}; -}; - -class CapturingPeriodicMetricReaderBuilder - : public opentelemetry::sdk::configuration::PeriodicMetricReaderBuilder -{ -public: - explicit CapturingPeriodicMetricReaderBuilder( - std::shared_ptr captured) - : captured_(std::move(captured)) - {} - - std::unique_ptr Build( - const opentelemetry::sdk::configuration::PeriodicMetricReaderConfiguration *model, - std::unique_ptr &&exporter) const override - { - captured_->called = true; - captured_->interval = model->interval; - captured_->timeout = model->timeout; - captured_->exporter = std::move(exporter); - return std::make_unique(); - } - -private: - std::shared_ptr captured_; -}; - -// --------------------------------------------------------------------------- -// Mock batch processor builders: satisfy the batch builder interface but -// create simple (synchronous) processors to avoid background-thread races in tests. - -class MockBatchSpanProcessorBuilder - : public opentelemetry::sdk::configuration::BatchSpanProcessorBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::BatchSpanProcessorConfiguration *, - std::unique_ptr &&exporter) const override - { - return std::make_unique(std::move(exporter)); - } -}; - -class MockBatchLogRecordProcessorBuilder - : public opentelemetry::sdk::configuration::BatchLogRecordProcessorBuilder -{ -public: - std::unique_ptr Build( - const opentelemetry::sdk::configuration::BatchLogRecordProcessorConfiguration *, - std::unique_ptr &&exporter) const override - { - return std::make_unique( - std::move(exporter)); - } -}; - -// --------------------------------------------------------------------------- -// TextMapCarrier for propagator tests. - -class MapCarrier : public opentelemetry::context::propagation::TextMapCarrier -{ -public: - opentelemetry::nostd::string_view Get( - opentelemetry::nostd::string_view key) const noexcept override - { - auto it = map_.find(std::string(key)); - return it != map_.end() ? opentelemetry::nostd::string_view(it->second) : ""; - } - void Set(opentelemetry::nostd::string_view key, - opentelemetry::nostd::string_view value) noexcept override - { - map_[std::string(key)] = std::string(value); - } - - const std::map &map() const { return map_; } - -private: - std::map map_; -}; - -} // namespace config_test From c53c33973390fd8828538a1674bf79ba2f787dd5 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Sun, 16 Aug 2026 22:40:06 +0000 Subject: [PATCH 09/11] fix spelling --- sdk/test/configuration/propagator_builders_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/test/configuration/propagator_builders_test.cc b/sdk/test/configuration/propagator_builders_test.cc index df4b1830e..1a84e1e4f 100644 --- a/sdk/test/configuration/propagator_builders_test.cc +++ b/sdk/test/configuration/propagator_builders_test.cc @@ -24,7 +24,7 @@ namespace config_sdk = opentelemetry::sdk::configuration; namespace { // --------------------------------------------------------------------------- -// PropagatorBuilderTest: Test that builders are registerd and match the expected propagator type +// PropagatorBuilderTest: Test that builders are registered and match the expected propagator type class PropagatorBuilderTest : public ::testing::Test { From 359a2c0e8a25de0327c722896424686d34373eb2 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Mon, 17 Aug 2026 10:07:44 -0400 Subject: [PATCH 10/11] cleanup metrics builders test: add missing to fixture name. add missing aggregation for view config tests. --- .../configuration/metrics_builders_test.cc | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/sdk/test/configuration/metrics_builders_test.cc b/sdk/test/configuration/metrics_builders_test.cc index 21b318e16..bdc3e3a4f 100644 --- a/sdk/test/configuration/metrics_builders_test.cc +++ b/sdk/test/configuration/metrics_builders_test.cc @@ -32,6 +32,7 @@ #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/configuration/aggregation_configuration.h" +#include "opentelemetry/sdk/configuration/base2_exponential_bucket_histogram_aggregation_configuration.h" #include "opentelemetry/sdk/configuration/cardinality_limits_configuration.h" #include "opentelemetry/sdk/configuration/configuration.h" #include "opentelemetry/sdk/configuration/configured_sdk.h" @@ -77,6 +78,7 @@ #include "opentelemetry/sdk/configuration/view_stream_configuration.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" + #include "opentelemetry/sdk/metrics/aggregation/aggregation.h" #include "opentelemetry/sdk/metrics/aggregation/aggregation_config.h" #include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" @@ -101,7 +103,7 @@ namespace config_sdk = opentelemetry::sdk::configuration; namespace { -class MetricsBuilderTest : public ::testing::Test +class MetricsBuildersTest : public ::testing::Test { protected: void SetUp() override @@ -195,7 +197,7 @@ class MetricsBuilderTest : public ::testing::Test sdk_type, metrics_sdk::InstrumentValueType::kLong}; auto scope = scope_sdk::InstrumentationScope::Create(""); - int matched = 0; + std::size_t matched = 0; view_registry.FindViews(descriptor, *scope, [&](const metrics_sdk::View &view) { matched++; auto *config = view.GetAggregationConfig(); @@ -218,9 +220,10 @@ class MetricsBuilderTest : public ::testing::Test AddView(&view_registry, model); auto scope = scope_sdk::InstrumentationScope::Create(""); - metrics_sdk::InstrumentDescriptor descriptor{"m", "", "", metrics_sdk::InstrumentType::kCounter, + metrics_sdk::InstrumentDescriptor descriptor{"test.instrument", "test description", "units", + metrics_sdk::InstrumentType::kCounter, metrics_sdk::InstrumentValueType::kLong}; - int matched = 0; + std::size_t matched = 0; view_registry.FindViews(descriptor, *scope, [&](const metrics_sdk::View &view) { EXPECT_EQ(view.GetAggregationType(), expected_type); matched++; @@ -233,14 +236,14 @@ class MetricsBuilderTest : public ::testing::Test }; } // namespace -TEST_F(MetricsBuilderTest, DefaultRegistry) +TEST_F(MetricsBuildersTest, DefaultRegistry) { auto registry = std::make_shared(); EXPECT_EQ(registry->GetPeriodicMetricReaderBuilder(), nullptr); EXPECT_EQ(registry->GetMeterConfiguratorBuilder(), nullptr); } -TEST_F(MetricsBuilderTest, RegisterDefaultMetricsBuilders) +TEST_F(MetricsBuildersTest, RegisterDefaultMetricsBuilders) { auto registry = std::make_shared(); config_sdk::RegisterDefaultMetricsBuilders(registry.get()); @@ -261,7 +264,7 @@ using opentelemetry::sdk::common::unsetenv; # endif } // namespace -TEST_F(MetricsBuilderTest, DeclarativeExemplarFilterDoesNotReadEnvironment) +TEST_F(MetricsBuildersTest, DeclarativeExemplarFilterDoesNotReadEnvironment) { unsetenv(kMetricsExemplarFilterEnv); opentelemetry::test_common::ScopedTestLogHandler log_handler{ @@ -279,82 +282,93 @@ TEST_F(MetricsBuilderTest, DeclarativeExemplarFilterDoesNotReadEnvironment) } #endif -TEST_F(MetricsBuilderTest, AggregationTypeDefault) +TEST_F(MetricsBuildersTest, AggregationTypeDefault) { CheckAggregationType(std::make_unique(), metrics_sdk::AggregationType::kDefault); } -TEST_F(MetricsBuilderTest, AggregationTypeSumAggregation) +TEST_F(MetricsBuildersTest, AggregationTypeSumAggregation) { CheckAggregationType(std::make_unique(), metrics_sdk::AggregationType::kSum); } -TEST_F(MetricsBuilderTest, AggregationTypeLastValueAggregation) +TEST_F(MetricsBuildersTest, AggregationTypeLastValueAggregation) { CheckAggregationType(std::make_unique(), metrics_sdk::AggregationType::kLastValue); } -TEST_F(MetricsBuilderTest, AggregationTypeDropAggregation) +TEST_F(MetricsBuildersTest, AggregationTypeDropAggregation) { CheckAggregationType(std::make_unique(), metrics_sdk::AggregationType::kDrop); } -TEST_F(MetricsBuilderTest, InstrumentTypeCounter) +TEST_F(MetricsBuildersTest, AggregationTypeExplicitBucketHistogramAggregation) +{ + CheckAggregationType( + std::make_unique(), + metrics_sdk::AggregationType::kHistogram); +} + +TEST_F(MetricsBuildersTest, AggregationTypeBase2ExponentialHistogramAggregation) +{ + CheckAggregationType( + std::make_unique(), + metrics_sdk::AggregationType::kBase2ExponentialHistogram); +} + +TEST_F(MetricsBuildersTest, InstrumentTypeCounter) { CheckInstrumentType(config_sdk::InstrumentType::counter, metrics_sdk::InstrumentType::kCounter); } -TEST_F(MetricsBuilderTest, InstrumentTypeUpDownCounter) +TEST_F(MetricsBuildersTest, InstrumentTypeUpDownCounter) { CheckInstrumentType(config_sdk::InstrumentType::up_down_counter, metrics_sdk::InstrumentType::kUpDownCounter); } -TEST_F(MetricsBuilderTest, InstrumentTypeObservableCounter) +TEST_F(MetricsBuildersTest, InstrumentTypeObservableCounter) { CheckInstrumentType(config_sdk::InstrumentType::observable_counter, metrics_sdk::InstrumentType::kObservableCounter); } -TEST_F(MetricsBuilderTest, InstrumentTypeObservableGauge) +TEST_F(MetricsBuildersTest, InstrumentTypeObservableGauge) { CheckInstrumentType(config_sdk::InstrumentType::observable_gauge, metrics_sdk::InstrumentType::kObservableGauge); } -TEST_F(MetricsBuilderTest, InstrumentTypeObservableUpDownCounter) +TEST_F(MetricsBuildersTest, InstrumentTypeObservableUpDownCounter) { CheckInstrumentType(config_sdk::InstrumentType::observable_up_down_counter, metrics_sdk::InstrumentType::kObservableUpDownCounter); } -TEST_F(MetricsBuilderTest, InstrumentTypeHistogram) +TEST_F(MetricsBuildersTest, InstrumentTypeHistogram) { CheckInstrumentType(config_sdk::InstrumentType::histogram, metrics_sdk::InstrumentType::kHistogram, metrics_sdk::AggregationType::kHistogram); } -#if OPENTELEMETRY_ABI_VERSION_NO < 2 -// No CheckInstrumentType test for gauge: gauge is unsupported in ABI v1 and throws instead. -TEST_F(MetricsBuilderTest, InstrumentTypeGaugeABIv1) +// Gauge is only supported in ABI v2. +TEST_F(MetricsBuildersTest, InstrumentTypeGauge) { +#if OPENTELEMETRY_ABI_VERSION_NO < 2 auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::gauge, 42); metrics_sdk::ViewRegistry view_registry; EXPECT_THROW(AddView(&view_registry, model), config_sdk::UnsupportedException); -} #else -TEST_F(MetricsBuilderTest, InstrumentTypeGaugeABIv2) -{ CheckInstrumentType(config_sdk::InstrumentType::gauge, metrics_sdk::InstrumentType::kGauge); -} #endif +} -TEST_F(MetricsBuilderTest, ViewSelectorInstrumentTypeNone) +TEST_F(MetricsBuildersTest, ViewSelectorInstrumentTypeNone) { namespace metrics_sdk = metrics_sdk; @@ -380,7 +394,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorInstrumentTypeNone) metrics_sdk::InstrumentDescriptor instrument_descriptor{ "test.instrument", "test description", "units", instrument_type, metrics_sdk::InstrumentValueType::kLong}; - int matched = 0; + std::size_t matched = 0; view_registry.FindViews(instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { auto *config = view.GetAggregationConfig(); @@ -396,7 +410,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorInstrumentTypeNone) } } -TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramCardinalityLimit) +TEST_F(MetricsBuildersTest, ViewSelectorWithHistogramCardinalityLimit) { // Verify that AddView populates default bucket boundaries on a cardinality-only // histogram view, rather than leaving boundaries_ empty (which would produce a @@ -430,7 +444,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramCardinalityLimit) }); } -TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramExplicitAggregation) +TEST_F(MetricsBuildersTest, ViewSelectorWithHistogramExplicitAggregation) { auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::histogram, 42); auto aggregation = @@ -446,7 +460,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramExplicitAggregation) metrics_sdk::InstrumentValueType::kLong}; auto instrumentation_scope = scope_sdk::InstrumentationScope::Create(""); - int matched = 0; + std::size_t matched = 0; view_registry.FindViews( instrument_descriptor, *instrumentation_scope, [&](const metrics_sdk::View &view) { ++matched; @@ -466,7 +480,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorWithHistogramExplicitAggregation) EXPECT_EQ(matched, 1); } -TEST_F(MetricsBuilderTest, ViewSelectorWithAttributesProcessor) +TEST_F(MetricsBuildersTest, ViewSelectorWithAttributesProcessor) { auto model = std::make_unique(); @@ -501,7 +515,7 @@ TEST_F(MetricsBuilderTest, ViewSelectorWithAttributesProcessor) }); } -TEST_F(MetricsBuilderTest, CreateBuiltInPeriodicMetricReader) +TEST_F(MetricsBuildersTest, CreateBuiltInPeriodicMetricReader) { auto model = std::make_unique(); model->interval = 2000; @@ -515,7 +529,7 @@ TEST_F(MetricsBuilderTest, CreateBuiltInPeriodicMetricReader) EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); } -TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithExtensionExporter) +TEST_F(MetricsBuildersTest, CreatePullMetricReaderWithExtensionExporter) { registry_->SetExtensionPullMetricExporterBuilder( "test_pull", std::make_unique()); @@ -531,7 +545,7 @@ TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithExtensionExporter) EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); } -TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithProducer) +TEST_F(MetricsBuildersTest, CreatePullMetricReaderWithProducer) { registry_->SetExtensionPullMetricExporterBuilder( "test_pull", std::make_unique()); @@ -548,7 +562,7 @@ TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithProducer) EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); } -TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithCardinalityLimits) +TEST_F(MetricsBuildersTest, CreatePullMetricReaderWithCardinalityLimits) { registry_->SetExtensionPullMetricExporterBuilder( "test_pull", std::make_unique()); @@ -567,7 +581,7 @@ TEST_F(MetricsBuilderTest, CreatePullMetricReaderWithCardinalityLimits) EXPECT_TRUE(reader->Shutdown(std::chrono::seconds(5))); } -TEST_F(MetricsBuilderTest, CreatePeriodicMetricReader) +TEST_F(MetricsBuildersTest, CreatePeriodicMetricReader) { auto exporter = std::make_unique(); exporter->name = "noop"; @@ -613,7 +627,7 @@ TEST_F(MetricsBuilderTest, CreatePeriodicMetricReader) EXPECT_EQ(reader->GetCardinalityLimit(metrics_sdk::InstrumentType::kUpDownCounter), 800u); } -TEST_F(MetricsBuilderTest, CreateAttributesProcessor) +TEST_F(MetricsBuildersTest, CreateAttributesProcessor) { std::map attributes = {{"included", 1}, {"excluded", 2}, {"unlisted", 3}}; opentelemetry::common::KeyValueIterableView> iterable(attributes); @@ -695,16 +709,16 @@ TEST_F(MetricsBuilderTest, CreateAttributesProcessor) } // --------------------------------------------------------------------------- -// MetricsBuilderTest: Verify that unregistered builders throw UnsupportedException. +// MetricsBuildersTest: Verify that unregistered builders throw UnsupportedException. -TEST_F(MetricsBuilderTest, UnregisteredMeterConfiguratorBuilder) +TEST_F(MetricsBuildersTest, UnregisteredMeterConfiguratorBuilder) { registry_->SetMeterConfiguratorBuilder(nullptr); auto model = std::make_unique(); EXPECT_THROW(MakeMeterConfigurator(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredExtensionPushMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredExtensionPushMetricExporterBuilder) { auto ext = std::make_unique(); ext->name = "unregistered"; @@ -714,7 +728,7 @@ TEST_F(MetricsBuilderTest, UnregisteredExtensionPushMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredExtensionPullMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredExtensionPullMetricExporterBuilder) { auto ext = std::make_unique(); ext->name = "unregistered"; @@ -724,7 +738,7 @@ TEST_F(MetricsBuilderTest, UnregisteredExtensionPullMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredPeriodicMetricReaderBuilder) +TEST_F(MetricsBuildersTest, UnregisteredPeriodicMetricReaderBuilder) { registry_->SetPeriodicMetricReaderBuilder(nullptr); @@ -736,7 +750,7 @@ TEST_F(MetricsBuilderTest, UnregisteredPeriodicMetricReaderBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredConsolePushMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredConsolePushMetricExporterBuilder) { registry_->SetConsolePushMetricExporterBuilder(nullptr); @@ -748,7 +762,7 @@ TEST_F(MetricsBuilderTest, UnregisteredConsolePushMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredOtlpHttpPushMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredOtlpHttpPushMetricExporterBuilder) { registry_->SetOtlpHttpPushMetricExporterBuilder(nullptr); @@ -760,7 +774,7 @@ TEST_F(MetricsBuilderTest, UnregisteredOtlpHttpPushMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredOtlpGrpcPushMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredOtlpGrpcPushMetricExporterBuilder) { registry_->SetOtlpGrpcPushMetricExporterBuilder(nullptr); @@ -772,7 +786,7 @@ TEST_F(MetricsBuilderTest, UnregisteredOtlpGrpcPushMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredOtlpFilePushMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredOtlpFilePushMetricExporterBuilder) { registry_->SetOtlpFilePushMetricExporterBuilder(nullptr); @@ -784,7 +798,7 @@ TEST_F(MetricsBuilderTest, UnregisteredOtlpFilePushMetricExporterBuilder) EXPECT_THROW(MakeMetricReader(std::move(model)), config_sdk::UnsupportedException); } -TEST_F(MetricsBuilderTest, UnregisteredPrometheusPullMetricExporterBuilder) +TEST_F(MetricsBuildersTest, UnregisteredPrometheusPullMetricExporterBuilder) { registry_->SetPrometheusPullMetricExporterBuilder(nullptr); From c3d8d3b82f88c7a7b6eb172302756bd52e9ffd00 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Tue, 25 Aug 2026 16:54:53 -0400 Subject: [PATCH 11/11] fix sdk builder test compliation after merge --- sdk/test/configuration/sdk_builder_test.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/test/configuration/sdk_builder_test.cc b/sdk/test/configuration/sdk_builder_test.cc index 213ddeb45..7f2616772 100644 --- a/sdk/test/configuration/sdk_builder_test.cc +++ b/sdk/test/configuration/sdk_builder_test.cc @@ -354,7 +354,7 @@ TEST(SdkBuilder, SetResourceWithoutModel) TEST(SdkBuilder, SetResourceAttributesListPercentDecodesValues) { - SdkBuilder builder(RegistryFactory::Create()); + config_sdk::SdkBuilder builder(std::make_shared()); auto model = std::make_unique(); model->attributes_list = "key1=hello%20world,key2=a%2Cb,key3=100%25,bad=50%z"; @@ -394,7 +394,7 @@ TEST(SdkBuilder, SetResourceDetectorDispatch) TEST(SdkBuilder, SetResourceExtensionDetector) { - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); auto extension_builder = std::make_unique( opentelemetry::sdk::resource::ResourceAttributes{{"custom.key", "custom-value"}}); auto *extension_builder_p = extension_builder.get(); @@ -630,8 +630,8 @@ TEST(SdkBuilder, SetResourceFilterAppliesToDetectedAttributesOnly) TEST(SdkBuilder, SetResourceNullAttributeValueSkipped) { - auto registry = RegistryFactory::Create(); - SdkBuilder builder(std::move(registry)); + auto registry = std::make_shared(); + config_sdk::SdkBuilder builder(std::move(registry)); auto typed_value = std::make_unique(); typed_value->value = "kept"; @@ -670,14 +670,14 @@ TEST(SdkBuilder, SetResourceAttributesNullValueSkipped) // detectors: // - test_service_detector # detects service.{name, version, namespace} - auto registry = RegistryFactory::Create(); + auto registry = std::make_shared(); auto ext_detector = std::make_unique( opentelemetry::sdk::resource::ResourceAttributes{ {"service.name", "name-from-detector"}, {"service.namespace", "namespace-from-detector"}, {"service.version", "version-from-detector"}}); registry->SetExtensionResourceDetectorBuilder("test_service_detector", std::move(ext_detector)); - SdkBuilder builder(std::move(registry)); + config_sdk::SdkBuilder builder(std::move(registry)); auto model = std::make_unique(); auto detector_config = std::make_unique();