From 55171460a7954721a9aae4a002ebddef188c43ff Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Mon, 10 Nov 2025 10:53:27 +0000 Subject: [PATCH 1/6] add GrpcMetricsExcludedLabelsOption for filtering --- .../internal/monitoring_exporter.cc | 32 ++++++--- .../internal/monitoring_exporter.h | 29 +++----- .../opentelemetry/internal/time_series.cc | 6 +- .../opentelemetry/internal/time_series.h | 6 +- .../cloud/opentelemetry/monitoring_exporter.h | 33 +++++++++ .../opentelemetry/monitoring_exporter_test.cc | 53 ++++++++++++++ google/cloud/storage/grpc_plugin.h | 21 ++++++ google/cloud/storage/grpc_plugin_test.cc | 34 +++++++++ .../internal/grpc/metrics_exporter_impl.cc | 22 ++++++ .../grpc/metrics_exporter_impl_test.cc | 71 +++++++++++++++++++ 10 files changed, 271 insertions(+), 36 deletions(-) diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.cc b/google/cloud/opentelemetry/internal/monitoring_exporter.cc index 36533a9d2a8e5..5a1a0af1401ee 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.cc @@ -35,9 +35,8 @@ std::string FormatProjectFullName(std::string const& project) { MonitoringExporter::MonitoringExporter( std::shared_ptr conn, - otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn, - otel_internal::ResourceFilterDataFn resource_filter_fn, - Options const& options) + otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, Options const& options) : client_(std::move(conn)), formatter_(options.get()), use_service_time_series_(options.get()), @@ -49,7 +48,10 @@ MonitoringExporter::MonitoringExporter( Project project, std::shared_ptr conn, Options const& options) - : MonitoringExporter(std::move(conn), nullptr, nullptr, options) { + : MonitoringExporter(std::move(conn), + options.get(), + options.get(), + options) { project_ = std::move(project); } @@ -98,7 +100,19 @@ opentelemetry::sdk::common::ExportResult MonitoringExporter::ExportImpl( } std::vector requests; - if (dynamic_resource_fn_) { + if (dynamic_resource_fn_ || resource_filter_fn_) { + // If `resource_filter_fn_` is provided, we must use + // `ToTimeSeriesWithResources`, which requires a dynamic resource + // function. If `dynamic_resource_fn_` is not provided, create a + // default implementation. + if (!dynamic_resource_fn_) { + auto mr = otel_internal::ToMonitoredResource(data, mr_proto_); + dynamic_resource_fn_ = + [mr, p = project_->project_id()]( + opentelemetry::sdk::metrics::PointDataAttributes const&) { + return std::make_pair(p, mr); + }; + } auto tss_map = otel_internal::ToTimeSeriesWithResources( data, formatter_, resource_filter_fn_, dynamic_resource_fn_); for (auto& tss : tss_map) { @@ -126,8 +140,8 @@ Options DefaultOptions(Options o) { } std::unique_ptr -MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn, - ResourceFilterDataFn resource_filter_fn, +MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, Options options) { // TODO(#15321): Determine which options, if any, should be passed along from // the options parameter to MakeMetricServiceConnection. @@ -140,8 +154,8 @@ MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn, std::unique_ptr MakeMonitoringExporter( - MonitoredResourceFromDataFn dynamic_resource_fn, - ResourceFilterDataFn resource_filter_fn, + otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, std::shared_ptr conn, Options options) { options = DefaultOptions(std::move(options)); diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.h b/google/cloud/opentelemetry/internal/monitoring_exporter.h index f8597776b4c83..a6e83434ecc98 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.h +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.h @@ -33,26 +33,13 @@ namespace cloud { namespace otel_internal { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -// For use with dynamic monitored resources, this function constructs the -// correct MonitoredResource from the PointDataAttributes passed in. This -// function is called in ToTimeSeriesWithResources. -using MonitoredResourceFromDataFn = - std::function( - opentelemetry::sdk::metrics::PointDataAttributes const&)>; - -// For use with dynamic monitored resources, this function is used in ToMetric -// to indicate which labels should be skipped when populating the labels field -// of the google::api::Metric proto. -using ResourceFilterDataFn = std::function; - class MonitoringExporter final : public opentelemetry::sdk::metrics::PushMetricExporter { public: MonitoringExporter( std::shared_ptr conn, - otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn, - otel_internal::ResourceFilterDataFn resource_filter_fn, - Options const& options); + otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, Options const& options); MonitoringExporter( Project project, @@ -84,21 +71,21 @@ class MonitoringExporter final otel::MetricNameFormatterOption::Type formatter_; bool use_service_time_series_; absl::optional mr_proto_; - otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn_; - otel_internal::ResourceFilterDataFn resource_filter_fn_; + otel::MonitoredResourceFromDataFn dynamic_resource_fn_; + otel::ResourceFilterDataFn resource_filter_fn_; }; Options DefaultOptions(Options o); std::unique_ptr -MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn, - ResourceFilterDataFn resource_filter_fn, +MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, Options options = {}); std::unique_ptr MakeMonitoringExporter( - MonitoredResourceFromDataFn dynamic_resource_fn, - ResourceFilterDataFn resource_filter_fn, + otel::MonitoredResourceFromDataFn dynamic_resource_fn, + otel::ResourceFilterDataFn resource_filter_fn, std::shared_ptr conn, Options options = {}); diff --git a/google/cloud/opentelemetry/internal/time_series.cc b/google/cloud/opentelemetry/internal/time_series.cc index effd72a807b3d..d7910542bfdca 100644 --- a/google/cloud/opentelemetry/internal/time_series.cc +++ b/google/cloud/opentelemetry/internal/time_series.cc @@ -133,7 +133,7 @@ google::api::Metric ToMetric( opentelemetry::sdk::metrics::PointAttributes const& attributes, opentelemetry::sdk::resource::Resource const* resource, std::function const& name_formatter, - ResourceFilterDataFn const& resource_filter_fn) { + otel::ResourceFilterDataFn const& resource_filter_fn) { auto add_label = [&resource_filter_fn](auto& labels, auto key, auto const& value) { // GCM labels match on the regex: R"([a-zA-Z_][a-zA-Z0-9_]*)". @@ -304,8 +304,8 @@ std::unordered_map> ToTimeSeriesWithResources( opentelemetry::sdk::metrics::ResourceMetrics const& data, std::function const& metrics_name_formatter, - ResourceFilterDataFn const& resource_filter_fn, - MonitoredResourceFromDataFn const& dynamic_resource_fn) { + otel::ResourceFilterDataFn const& resource_filter_fn, + otel::MonitoredResourceFromDataFn const& dynamic_resource_fn) { std::unordered_map> tss_map; diff --git a/google/cloud/opentelemetry/internal/time_series.h b/google/cloud/opentelemetry/internal/time_series.h index 6dc40b375b8a1..d3970aff5e51f 100644 --- a/google/cloud/opentelemetry/internal/time_series.h +++ b/google/cloud/opentelemetry/internal/time_series.h @@ -41,7 +41,7 @@ google::api::Metric ToMetric( opentelemetry::sdk::metrics::PointAttributes const& attributes, opentelemetry::sdk::resource::Resource const* resource, std::function const& metrics_name_formatter, - ResourceFilterDataFn const& resource_filter_fn); + otel::ResourceFilterDataFn const& resource_filter_fn); google::api::Metric ToMetric( opentelemetry::sdk::metrics::MetricData const& metric_data, @@ -94,8 +94,8 @@ std::unordered_map> ToTimeSeriesWithResources( opentelemetry::sdk::metrics::ResourceMetrics const& data, std::function const& metrics_name_formatter, - ResourceFilterDataFn const& resource_filter_fn, - MonitoredResourceFromDataFn const& resource_fn); + otel::ResourceFilterDataFn const& resource_filter_fn, + otel::MonitoredResourceFromDataFn const& resource_fn); bool IsEmptyTimeSeries( opentelemetry::sdk::metrics::ResourceMetrics const& data); diff --git a/google/cloud/opentelemetry/monitoring_exporter.h b/google/cloud/opentelemetry/monitoring_exporter.h index 170ad3323dcac..11a9fcf9cc50a 100644 --- a/google/cloud/opentelemetry/monitoring_exporter.h +++ b/google/cloud/opentelemetry/monitoring_exporter.h @@ -19,6 +19,7 @@ #include "google/cloud/opentelemetry/internal/recordable.h" #include "google/cloud/project.h" #include "google/cloud/version.h" +#include #include #include #include @@ -29,6 +30,18 @@ namespace cloud { namespace otel { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +// For use with dynamic monitored resources, this function constructs the +// correct MonitoredResource from the PointDataAttributes passed in. This +// function is called in ToTimeSeriesWithResources. +using MonitoredResourceFromDataFn = + std::function( + opentelemetry::sdk::metrics::PointDataAttributes const&)>; + +// For use with dynamic monitored resources, this function is used in ToMetric +// to indicate which labels should be skipped when populating the labels field +// of the google::api::Metric proto. +using ResourceFilterDataFn = std::function; + /** * Change formatting for metric names. * @@ -75,6 +88,26 @@ struct MonitoredResourceOption { using Type = google::api::MonitoredResource; }; +/** + * Override the monitored resource builder. + * + * This option is primarily relevant to Google applications and libraries. It + * can be ignored by external developers. + */ +struct MonitoredResourceFromDataFnOption { + using Type = MonitoredResourceFromDataFn; +}; + +/** + * Filter resource labels. + * + * This option is primarily relevant to Google applications and libraries. It + * can be ignored by external developers. + */ +struct ResourceFilterDataFnOption { + using Type = ResourceFilterDataFn; +}; + std::unique_ptr MakeMonitoringExporter( Project project, diff --git a/google/cloud/opentelemetry/monitoring_exporter_test.cc b/google/cloud/opentelemetry/monitoring_exporter_test.cc index b413ddee55499..118a05a4d403e 100644 --- a/google/cloud/opentelemetry/monitoring_exporter_test.cc +++ b/google/cloud/opentelemetry/monitoring_exporter_test.cc @@ -264,6 +264,59 @@ TEST(MonitoringExporter, CustomMonitoredResource) { EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); } +TEST(MonitoringExporter, CustomMonitoredResourceFromDataFunction) { + auto mock = + std::make_shared(); + EXPECT_CALL(*mock, CreateTimeSeries) + .WillOnce( + [](google::monitoring::v3::CreateTimeSeriesRequest const& request) { + EXPECT_THAT(request.name(), "projects/test-project"); + EXPECT_THAT(request.time_series(), SizeIs(2)); + EXPECT_THAT(request.time_series(), + Each(ResourceType("test_resource"))); + return Status(); + }); + + // Define a function that maps PointDataAttributes to a MonitoredResource. + auto resource_fn = [](opentelemetry::sdk::metrics::PointDataAttributes const&) + -> std::pair { + google::api::MonitoredResource resource; + resource.set_type("test_resource"); + return {"test-project", resource}; + }; + + auto options = Options{}.set(resource_fn); + auto exporter = + MakeMonitoringExporter(Project("test-project"), std::move(mock), options); + auto data = MakeResourceMetrics(/*expected_time_series_count=*/2); + auto result = exporter->Export(data); + EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); +} + +TEST(MonitoringExporter, CustomResourceFilterDataFunction) { + auto mock = + std::make_shared(); + EXPECT_CALL(*mock, CreateTimeSeries) + .WillOnce( + [](google::monitoring::v3::CreateTimeSeriesRequest const& request) { + EXPECT_THAT(request.name(), "projects/test-project"); + EXPECT_THAT(request.time_series(), SizeIs(2)); + return Status(); + }); + + // Define a function that filters out labels starting with "internal_". + auto filter_fn = [](std::string const& label) -> bool { + return label.rfind("internal_", 0) == 0; + }; + + auto options = Options{}.set(filter_fn); + auto exporter = + MakeMonitoringExporter(Project("test-project"), std::move(mock), options); + auto data = MakeResourceMetrics(/*expected_time_series_count=*/2); + auto result = exporter->Export(data); + EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); +} + TEST(MonitoringExporter, CreateServiceTimeSeries) { auto mock = std::make_shared(); diff --git a/google/cloud/storage/grpc_plugin.h b/google/cloud/storage/grpc_plugin.h index 167ecb8dcb609..ec2a49ed9a394 100644 --- a/google/cloud/storage/grpc_plugin.h +++ b/google/cloud/storage/grpc_plugin.h @@ -131,6 +131,27 @@ struct GrpcMetricsExportTimeoutOption { using Type = std::chrono::seconds; }; +/** + * gRPC telemetry excluded labels. + * + * A set of OpenTelemetry resource attribute keys to exclude from metric labels + * when exporting gRPC telemetry. For example, to exclude the `service.name` + * label, configure the option with `{"service.name"}`. + * + * @par Example: Exclude specific labels from telemetry + * @code + * namespace gcs_ex = google::cloud::storage_experimental; + * auto client = google::cloud::storage::MakeGrpcClient( + * google::cloud::Options{} + * .set(true) + * .set( + * std::set{"service_name", "service_version"})); + * @endcode + */ +struct GrpcMetricsExcludedLabelsOption { + using Type = std::set; +}; + GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_experimental } // namespace cloud diff --git a/google/cloud/storage/grpc_plugin_test.cc b/google/cloud/storage/grpc_plugin_test.cc index 602bd7ab577ef..8be88c86aae45 100644 --- a/google/cloud/storage/grpc_plugin_test.cc +++ b/google/cloud/storage/grpc_plugin_test.cc @@ -138,6 +138,40 @@ TEST(GrpcPluginTest, BackwardsCompatibilityShims) { } #include "google/cloud/internal/diagnostics_pop.inc" +TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOption) { + auto const expected = + std::set{"service_name", "service_version", "custom_label"}; + auto opts = + google::cloud::Options{} + .set(expected); + + EXPECT_EQ(expected, + opts.get()); +} + +TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOptionEmpty) { + auto const expected = std::set{}; + auto opts = + google::cloud::Options{} + .set(expected); + + EXPECT_TRUE(opts.get() + .empty()); +} + +TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOptionSingle) { + auto const expected = std::set{"service_name"}; + auto opts = + google::cloud::Options{} + .set(expected); + + EXPECT_EQ( + 1, + opts.get().size()); + EXPECT_EQ(expected, + opts.get()); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc index 2e6ae92ba5a6f..f202b8aa375b4 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc @@ -79,6 +79,26 @@ class ExporterRegistry { std::mutex mu_; }; +otel::ResourceFilterDataFn MakeFilter(Options const& options) { + // Check for explicit filter function first. + if (options.has()) { + return options.get(); + } + + // Check for excluded labels list. + if (!options.has()) { + return nullptr; + } + auto const& excluded = + options.get(); + if (excluded.empty()) return nullptr; + + // Capture by value to avoid dangling reference in the lambda. + return [excluded](std::string const& key) -> bool { + return excluded.count(key) > 0; + }; +} + } // namespace absl::optional MakeMeterProviderConfig( @@ -91,6 +111,8 @@ absl::optional MakeMeterProviderConfig( if (!project) return absl::nullopt; auto exporter_options = MetricsExporterOptions(*project, resource); + exporter_options.set(MakeFilter(options)); + auto exporter_connection_options = MetricsExporterConnectionOptions(options); return ExporterConfig{std::move(*project), std::move(exporter_options), std::move(exporter_connection_options), diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc index b266f262c61e9..4d431aa9a83e2 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc @@ -156,6 +156,77 @@ TEST(GrpcMetricsExporter, ReaderOptionsAreSetFromConfig) { std::chrono::milliseconds(expected_timeout)); } +TEST(GrpcMetricsExporter, MakeFilterWithServiceName) { + auto excluded_labels = std::set{"service_name"}; + auto options = + TestOptions().set( + excluded_labels); + + auto config = MakeMeterProviderConfig(FullResource(), options); + ASSERT_TRUE(config.has_value()); + + // Test that filter option is set. + ASSERT_TRUE(config->exporter_options.has()); + auto filter_fn = + config->exporter_options.get(); + ASSERT_NE(filter_fn, nullptr); + + // Test filtering behavior. + EXPECT_TRUE(filter_fn("service_name")); + EXPECT_FALSE(filter_fn("service_version")); +} + +TEST(GrpcMetricsExporter, MakeFilterWithExplicitFilterFunction) { + auto explicit_filter = [](std::string const& key) { + return key == "explicit_test"; + }; + auto excluded_labels = std::set{"service_name"}; + + // Test when explicit filter function takes precedence. + auto options = + TestOptions() + .set(explicit_filter) + .set( + excluded_labels); + auto config = MakeMeterProviderConfig(FullResource(), options); + ASSERT_TRUE(config.has_value()); + + auto filter_fn = + config->exporter_options.get(); + ASSERT_NE(filter_fn, nullptr); + + // Explicit filter should take precedence + EXPECT_TRUE(filter_fn("explicit_test")); + EXPECT_FALSE(filter_fn("service_name")); +} + +TEST(GrpcMetricsExporter, MakeFilterWithEmptyExcludedLabels) { + auto excluded_labels = std::set{}; + + // Test when empty excluded labels returns nullptr. + auto options = + TestOptions().set( + excluded_labels); + auto config = MakeMeterProviderConfig(FullResource(), options); + ASSERT_TRUE(config.has_value()); + + auto filter_fn = + config->exporter_options.get(); + EXPECT_EQ(filter_fn, nullptr); +} + +TEST(GrpcMetricsExporter, MakeFilterWithNoExcludedLabelsOption) { + auto options = TestOptions(); + + // Test when no excluded labels option returns nullptr. + auto config = MakeMeterProviderConfig(FullResource(), options); + ASSERT_TRUE(config.has_value()); + + auto filter_fn = + config->exporter_options.get(); + EXPECT_EQ(filter_fn, nullptr); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_internal From 08d67415d85de7d15afff186b9955e1756f14713 Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Tue, 11 Nov 2025 12:11:01 +0000 Subject: [PATCH 2/6] resolving comments --- .../internal/monitoring_exporter.cc | 32 ++++++--- .../internal/monitoring_exporter.h | 28 +++++--- .../opentelemetry/internal/time_series.cc | 6 +- .../opentelemetry/internal/time_series.h | 6 +- .../cloud/opentelemetry/monitoring_exporter.h | 25 +------ .../opentelemetry/monitoring_exporter_test.cc | 36 +--------- .../internal/grpc/metrics_exporter_impl.cc | 25 ++----- .../grpc/metrics_exporter_impl_test.cc | 71 ------------------- 8 files changed, 55 insertions(+), 174 deletions(-) diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.cc b/google/cloud/opentelemetry/internal/monitoring_exporter.cc index 5a1a0af1401ee..54f60eb8f5074 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.cc @@ -31,12 +31,28 @@ std::string FormatProjectFullName(std::string const& project) { return absl::StrCat("projects/", project); } +otel_internal::ResourceFilterDataFn MakeFilter(Options const& options) { + if (!options.has()) { + return nullptr; + } + + // Get the excluded labels list. + auto const& excluded = options.get(); + if (excluded.empty()) return nullptr; + + // Capture by value to avoid dangling reference in the lambda. + return [excluded = std::move(excluded)](std::string const& key) -> bool { + return excluded.count(key) > 0; + }; +} + } // namespace MonitoringExporter::MonitoringExporter( std::shared_ptr conn, - otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, Options const& options) + otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn, + otel_internal::ResourceFilterDataFn resource_filter_fn, + Options const& options) : client_(std::move(conn)), formatter_(options.get()), use_service_time_series_(options.get()), @@ -48,9 +64,7 @@ MonitoringExporter::MonitoringExporter( Project project, std::shared_ptr conn, Options const& options) - : MonitoringExporter(std::move(conn), - options.get(), - options.get(), + : MonitoringExporter(std::move(conn), nullptr, MakeFilter(options), options) { project_ = std::move(project); } @@ -140,8 +154,8 @@ Options DefaultOptions(Options o) { } std::unique_ptr -MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, +MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn, + ResourceFilterDataFn resource_filter_fn, Options options) { // TODO(#15321): Determine which options, if any, should be passed along from // the options parameter to MakeMetricServiceConnection. @@ -154,8 +168,8 @@ MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn, std::unique_ptr MakeMonitoringExporter( - otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, + MonitoredResourceFromDataFn dynamic_resource_fn, + ResourceFilterDataFn resource_filter_fn, std::shared_ptr conn, Options options) { options = DefaultOptions(std::move(options)); diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.h b/google/cloud/opentelemetry/internal/monitoring_exporter.h index a6e83434ecc98..9cbf0135fdc0d 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.h +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.h @@ -33,13 +33,25 @@ namespace cloud { namespace otel_internal { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +// For use with dynamic monitored resources, this function constructs the +// correct MonitoredResource from the PointDataAttributes passed in. This +// function is called in ToTimeSeriesWithResources. +using MonitoredResourceFromDataFn = + std::function( + opentelemetry::sdk::metrics::PointDataAttributes const&)>; + +// For use with dynamic monitored resources, this function is used in ToMetric +// to indicate which labels should be skipped when populating the labels field +// of the google::api::Metric proto. +using ResourceFilterDataFn = std::function; + class MonitoringExporter final : public opentelemetry::sdk::metrics::PushMetricExporter { public: MonitoringExporter( std::shared_ptr conn, - otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, Options const& options); + otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn, + otel_internal::ResourceFilterDataFn resource_filter_fn, Options const& options); MonitoringExporter( Project project, @@ -71,21 +83,21 @@ class MonitoringExporter final otel::MetricNameFormatterOption::Type formatter_; bool use_service_time_series_; absl::optional mr_proto_; - otel::MonitoredResourceFromDataFn dynamic_resource_fn_; - otel::ResourceFilterDataFn resource_filter_fn_; + otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn_; + otel_internal::ResourceFilterDataFn resource_filter_fn_; }; Options DefaultOptions(Options o); std::unique_ptr -MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, +MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn, + ResourceFilterDataFn resource_filter_fn, Options options = {}); std::unique_ptr MakeMonitoringExporter( - otel::MonitoredResourceFromDataFn dynamic_resource_fn, - otel::ResourceFilterDataFn resource_filter_fn, + MonitoredResourceFromDataFn dynamic_resource_fn, + ResourceFilterDataFn resource_filter_fn, std::shared_ptr conn, Options options = {}); diff --git a/google/cloud/opentelemetry/internal/time_series.cc b/google/cloud/opentelemetry/internal/time_series.cc index d7910542bfdca..effd72a807b3d 100644 --- a/google/cloud/opentelemetry/internal/time_series.cc +++ b/google/cloud/opentelemetry/internal/time_series.cc @@ -133,7 +133,7 @@ google::api::Metric ToMetric( opentelemetry::sdk::metrics::PointAttributes const& attributes, opentelemetry::sdk::resource::Resource const* resource, std::function const& name_formatter, - otel::ResourceFilterDataFn const& resource_filter_fn) { + ResourceFilterDataFn const& resource_filter_fn) { auto add_label = [&resource_filter_fn](auto& labels, auto key, auto const& value) { // GCM labels match on the regex: R"([a-zA-Z_][a-zA-Z0-9_]*)". @@ -304,8 +304,8 @@ std::unordered_map> ToTimeSeriesWithResources( opentelemetry::sdk::metrics::ResourceMetrics const& data, std::function const& metrics_name_formatter, - otel::ResourceFilterDataFn const& resource_filter_fn, - otel::MonitoredResourceFromDataFn const& dynamic_resource_fn) { + ResourceFilterDataFn const& resource_filter_fn, + MonitoredResourceFromDataFn const& dynamic_resource_fn) { std::unordered_map> tss_map; diff --git a/google/cloud/opentelemetry/internal/time_series.h b/google/cloud/opentelemetry/internal/time_series.h index d3970aff5e51f..6dc40b375b8a1 100644 --- a/google/cloud/opentelemetry/internal/time_series.h +++ b/google/cloud/opentelemetry/internal/time_series.h @@ -41,7 +41,7 @@ google::api::Metric ToMetric( opentelemetry::sdk::metrics::PointAttributes const& attributes, opentelemetry::sdk::resource::Resource const* resource, std::function const& metrics_name_formatter, - otel::ResourceFilterDataFn const& resource_filter_fn); + ResourceFilterDataFn const& resource_filter_fn); google::api::Metric ToMetric( opentelemetry::sdk::metrics::MetricData const& metric_data, @@ -94,8 +94,8 @@ std::unordered_map> ToTimeSeriesWithResources( opentelemetry::sdk::metrics::ResourceMetrics const& data, std::function const& metrics_name_formatter, - otel::ResourceFilterDataFn const& resource_filter_fn, - otel::MonitoredResourceFromDataFn const& resource_fn); + ResourceFilterDataFn const& resource_filter_fn, + MonitoredResourceFromDataFn const& resource_fn); bool IsEmptyTimeSeries( opentelemetry::sdk::metrics::ResourceMetrics const& data); diff --git a/google/cloud/opentelemetry/monitoring_exporter.h b/google/cloud/opentelemetry/monitoring_exporter.h index 11a9fcf9cc50a..0bb21538f8ccd 100644 --- a/google/cloud/opentelemetry/monitoring_exporter.h +++ b/google/cloud/opentelemetry/monitoring_exporter.h @@ -19,7 +19,6 @@ #include "google/cloud/opentelemetry/internal/recordable.h" #include "google/cloud/project.h" #include "google/cloud/version.h" -#include #include #include #include @@ -30,18 +29,6 @@ namespace cloud { namespace otel { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -// For use with dynamic monitored resources, this function constructs the -// correct MonitoredResource from the PointDataAttributes passed in. This -// function is called in ToTimeSeriesWithResources. -using MonitoredResourceFromDataFn = - std::function( - opentelemetry::sdk::metrics::PointDataAttributes const&)>; - -// For use with dynamic monitored resources, this function is used in ToMetric -// to indicate which labels should be skipped when populating the labels field -// of the google::api::Metric proto. -using ResourceFilterDataFn = std::function; - /** * Change formatting for metric names. * @@ -88,16 +75,6 @@ struct MonitoredResourceOption { using Type = google::api::MonitoredResource; }; -/** - * Override the monitored resource builder. - * - * This option is primarily relevant to Google applications and libraries. It - * can be ignored by external developers. - */ -struct MonitoredResourceFromDataFnOption { - using Type = MonitoredResourceFromDataFn; -}; - /** * Filter resource labels. * @@ -105,7 +82,7 @@ struct MonitoredResourceFromDataFnOption { * can be ignored by external developers. */ struct ResourceFilterDataFnOption { - using Type = ResourceFilterDataFn; + using Type = std::set; }; std::unique_ptr diff --git a/google/cloud/opentelemetry/monitoring_exporter_test.cc b/google/cloud/opentelemetry/monitoring_exporter_test.cc index 118a05a4d403e..970469e4ab4e6 100644 --- a/google/cloud/opentelemetry/monitoring_exporter_test.cc +++ b/google/cloud/opentelemetry/monitoring_exporter_test.cc @@ -264,35 +264,6 @@ TEST(MonitoringExporter, CustomMonitoredResource) { EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); } -TEST(MonitoringExporter, CustomMonitoredResourceFromDataFunction) { - auto mock = - std::make_shared(); - EXPECT_CALL(*mock, CreateTimeSeries) - .WillOnce( - [](google::monitoring::v3::CreateTimeSeriesRequest const& request) { - EXPECT_THAT(request.name(), "projects/test-project"); - EXPECT_THAT(request.time_series(), SizeIs(2)); - EXPECT_THAT(request.time_series(), - Each(ResourceType("test_resource"))); - return Status(); - }); - - // Define a function that maps PointDataAttributes to a MonitoredResource. - auto resource_fn = [](opentelemetry::sdk::metrics::PointDataAttributes const&) - -> std::pair { - google::api::MonitoredResource resource; - resource.set_type("test_resource"); - return {"test-project", resource}; - }; - - auto options = Options{}.set(resource_fn); - auto exporter = - MakeMonitoringExporter(Project("test-project"), std::move(mock), options); - auto data = MakeResourceMetrics(/*expected_time_series_count=*/2); - auto result = exporter->Export(data); - EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); -} - TEST(MonitoringExporter, CustomResourceFilterDataFunction) { auto mock = std::make_shared(); @@ -304,12 +275,7 @@ TEST(MonitoringExporter, CustomResourceFilterDataFunction) { return Status(); }); - // Define a function that filters out labels starting with "internal_". - auto filter_fn = [](std::string const& label) -> bool { - return label.rfind("internal_", 0) == 0; - }; - - auto options = Options{}.set(filter_fn); + auto options = Options{}.set({"test_label"}); auto exporter = MakeMonitoringExporter(Project("test-project"), std::move(mock), options); auto data = MakeResourceMetrics(/*expected_time_series_count=*/2); diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc index f202b8aa375b4..55c60e22c373a 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc @@ -79,26 +79,6 @@ class ExporterRegistry { std::mutex mu_; }; -otel::ResourceFilterDataFn MakeFilter(Options const& options) { - // Check for explicit filter function first. - if (options.has()) { - return options.get(); - } - - // Check for excluded labels list. - if (!options.has()) { - return nullptr; - } - auto const& excluded = - options.get(); - if (excluded.empty()) return nullptr; - - // Capture by value to avoid dangling reference in the lambda. - return [excluded](std::string const& key) -> bool { - return excluded.count(key) > 0; - }; -} - } // namespace absl::optional MakeMeterProviderConfig( @@ -111,7 +91,10 @@ absl::optional MakeMeterProviderConfig( if (!project) return absl::nullopt; auto exporter_options = MetricsExporterOptions(*project, resource); - exporter_options.set(MakeFilter(options)); + if (options.has()) { + exporter_options.set( + options.get()); + } auto exporter_connection_options = MetricsExporterConnectionOptions(options); return ExporterConfig{std::move(*project), std::move(exporter_options), diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc index 4d431aa9a83e2..b266f262c61e9 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc @@ -156,77 +156,6 @@ TEST(GrpcMetricsExporter, ReaderOptionsAreSetFromConfig) { std::chrono::milliseconds(expected_timeout)); } -TEST(GrpcMetricsExporter, MakeFilterWithServiceName) { - auto excluded_labels = std::set{"service_name"}; - auto options = - TestOptions().set( - excluded_labels); - - auto config = MakeMeterProviderConfig(FullResource(), options); - ASSERT_TRUE(config.has_value()); - - // Test that filter option is set. - ASSERT_TRUE(config->exporter_options.has()); - auto filter_fn = - config->exporter_options.get(); - ASSERT_NE(filter_fn, nullptr); - - // Test filtering behavior. - EXPECT_TRUE(filter_fn("service_name")); - EXPECT_FALSE(filter_fn("service_version")); -} - -TEST(GrpcMetricsExporter, MakeFilterWithExplicitFilterFunction) { - auto explicit_filter = [](std::string const& key) { - return key == "explicit_test"; - }; - auto excluded_labels = std::set{"service_name"}; - - // Test when explicit filter function takes precedence. - auto options = - TestOptions() - .set(explicit_filter) - .set( - excluded_labels); - auto config = MakeMeterProviderConfig(FullResource(), options); - ASSERT_TRUE(config.has_value()); - - auto filter_fn = - config->exporter_options.get(); - ASSERT_NE(filter_fn, nullptr); - - // Explicit filter should take precedence - EXPECT_TRUE(filter_fn("explicit_test")); - EXPECT_FALSE(filter_fn("service_name")); -} - -TEST(GrpcMetricsExporter, MakeFilterWithEmptyExcludedLabels) { - auto excluded_labels = std::set{}; - - // Test when empty excluded labels returns nullptr. - auto options = - TestOptions().set( - excluded_labels); - auto config = MakeMeterProviderConfig(FullResource(), options); - ASSERT_TRUE(config.has_value()); - - auto filter_fn = - config->exporter_options.get(); - EXPECT_EQ(filter_fn, nullptr); -} - -TEST(GrpcMetricsExporter, MakeFilterWithNoExcludedLabelsOption) { - auto options = TestOptions(); - - // Test when no excluded labels option returns nullptr. - auto config = MakeMeterProviderConfig(FullResource(), options); - ASSERT_TRUE(config.has_value()); - - auto filter_fn = - config->exporter_options.get(); - EXPECT_EQ(filter_fn, nullptr); -} - } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_internal From 13efa8339c80cc0deefd8d7689b0e2a7958c316c Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Tue, 11 Nov 2025 12:41:38 +0000 Subject: [PATCH 3/6] adding tests --- .../internal/monitoring_exporter.cc | 2 +- .../internal/monitoring_exporter.h | 3 +- .../internal/monitoring_exporter_test.cc | 33 +++++++++++++++++++ google/cloud/storage/grpc_plugin.h | 2 +- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.cc b/google/cloud/opentelemetry/internal/monitoring_exporter.cc index 54f60eb8f5074..9d1f80b5ce0d7 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.cc @@ -36,7 +36,7 @@ otel_internal::ResourceFilterDataFn MakeFilter(Options const& options) { return nullptr; } - // Get the excluded labels list. + // Get the metric labels set to be excluded. auto const& excluded = options.get(); if (excluded.empty()) return nullptr; diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.h b/google/cloud/opentelemetry/internal/monitoring_exporter.h index 9cbf0135fdc0d..f8597776b4c83 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.h +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.h @@ -51,7 +51,8 @@ class MonitoringExporter final MonitoringExporter( std::shared_ptr conn, otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn, - otel_internal::ResourceFilterDataFn resource_filter_fn, Options const& options); + otel_internal::ResourceFilterDataFn resource_filter_fn, + Options const& options); MonitoringExporter( Project project, diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc b/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc index 897dc266a8ded..7e400e1a77349 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc @@ -159,6 +159,39 @@ TEST(MonitoringExporter, ExportSuccess) { EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); } +TEST(MonitoringExporterTest, MakeFilterNoOption) { + auto mock = + std::make_shared(); + Options options; + + auto exporter = std::make_unique(Project("test-project"), + mock, options); + EXPECT_NE(exporter, nullptr); +} + +TEST(MonitoringExporterTest, MakeFilterEmptySet) { + auto mock = + std::make_shared(); + Options options; + options.set(std::set{}); + + auto exporter = std::make_unique(Project("test-project"), + mock, options); + EXPECT_NE(exporter, nullptr); +} + +TEST(MonitoringExporterTest, MakeFilterWithExcludedKeys) { + auto mock = + std::make_shared(); + Options options; + std::set excluded{"service_name", "service_version"}; + options.set(excluded); + + auto exporter = std::make_unique(Project("test-project"), + mock, options); + EXPECT_NE(exporter, nullptr); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace otel_internal diff --git a/google/cloud/storage/grpc_plugin.h b/google/cloud/storage/grpc_plugin.h index ec2a49ed9a394..ddc24ae55ab0c 100644 --- a/google/cloud/storage/grpc_plugin.h +++ b/google/cloud/storage/grpc_plugin.h @@ -136,7 +136,7 @@ struct GrpcMetricsExportTimeoutOption { * * A set of OpenTelemetry resource attribute keys to exclude from metric labels * when exporting gRPC telemetry. For example, to exclude the `service.name` - * label, configure the option with `{"service.name"}`. + * label, configure the option with `{"service_name"}`. * * @par Example: Exclude specific labels from telemetry * @code From b4550a998816b507237f40551cbdcff403a8731e Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Wed, 12 Nov 2025 05:09:49 +0000 Subject: [PATCH 4/6] moving ResourceFilterDataFnOption to internal namespace --- .../internal/monitoring_exporter.cc | 5 +++-- .../internal/monitoring_exporter.h | 6 ++++++ .../internal/monitoring_exporter_test.cc | 5 +++-- .../cloud/opentelemetry/monitoring_exporter.h | 10 ---------- .../opentelemetry/monitoring_exporter_test.cc | 19 ------------------- .../internal/grpc/metrics_exporter_impl.cc | 4 ++-- 6 files changed, 14 insertions(+), 35 deletions(-) diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.cc b/google/cloud/opentelemetry/internal/monitoring_exporter.cc index 9d1f80b5ce0d7..03ede5af85ea6 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.cc @@ -32,12 +32,13 @@ std::string FormatProjectFullName(std::string const& project) { } otel_internal::ResourceFilterDataFn MakeFilter(Options const& options) { - if (!options.has()) { + if (!options.has()) { return nullptr; } // Get the metric labels set to be excluded. - auto const& excluded = options.get(); + auto const& excluded = + options.get(); if (excluded.empty()) return nullptr; // Capture by value to avoid dangling reference in the lambda. diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.h b/google/cloud/opentelemetry/internal/monitoring_exporter.h index f8597776b4c83..360a12cac399f 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.h +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.h @@ -45,6 +45,12 @@ using MonitoredResourceFromDataFn = // of the google::api::Metric proto. using ResourceFilterDataFn = std::function; +// Filter resource labels. A set of OpenTelemetry resource attribute keys to +// exclude from metric labels when exporting metrics. +struct ResourceFilterDataFnOption { + using Type = std::set; +}; + class MonitoringExporter final : public opentelemetry::sdk::metrics::PushMetricExporter { public: diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc b/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc index 7e400e1a77349..7602f35539626 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter_test.cc @@ -173,7 +173,8 @@ TEST(MonitoringExporterTest, MakeFilterEmptySet) { auto mock = std::make_shared(); Options options; - options.set(std::set{}); + options.set( + std::set{}); auto exporter = std::make_unique(Project("test-project"), mock, options); @@ -185,7 +186,7 @@ TEST(MonitoringExporterTest, MakeFilterWithExcludedKeys) { std::make_shared(); Options options; std::set excluded{"service_name", "service_version"}; - options.set(excluded); + options.set(excluded); auto exporter = std::make_unique(Project("test-project"), mock, options); diff --git a/google/cloud/opentelemetry/monitoring_exporter.h b/google/cloud/opentelemetry/monitoring_exporter.h index 0bb21538f8ccd..170ad3323dcac 100644 --- a/google/cloud/opentelemetry/monitoring_exporter.h +++ b/google/cloud/opentelemetry/monitoring_exporter.h @@ -75,16 +75,6 @@ struct MonitoredResourceOption { using Type = google::api::MonitoredResource; }; -/** - * Filter resource labels. - * - * This option is primarily relevant to Google applications and libraries. It - * can be ignored by external developers. - */ -struct ResourceFilterDataFnOption { - using Type = std::set; -}; - std::unique_ptr MakeMonitoringExporter( Project project, diff --git a/google/cloud/opentelemetry/monitoring_exporter_test.cc b/google/cloud/opentelemetry/monitoring_exporter_test.cc index 970469e4ab4e6..b413ddee55499 100644 --- a/google/cloud/opentelemetry/monitoring_exporter_test.cc +++ b/google/cloud/opentelemetry/monitoring_exporter_test.cc @@ -264,25 +264,6 @@ TEST(MonitoringExporter, CustomMonitoredResource) { EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); } -TEST(MonitoringExporter, CustomResourceFilterDataFunction) { - auto mock = - std::make_shared(); - EXPECT_CALL(*mock, CreateTimeSeries) - .WillOnce( - [](google::monitoring::v3::CreateTimeSeriesRequest const& request) { - EXPECT_THAT(request.name(), "projects/test-project"); - EXPECT_THAT(request.time_series(), SizeIs(2)); - return Status(); - }); - - auto options = Options{}.set({"test_label"}); - auto exporter = - MakeMonitoringExporter(Project("test-project"), std::move(mock), options); - auto data = MakeResourceMetrics(/*expected_time_series_count=*/2); - auto result = exporter->Export(data); - EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess); -} - TEST(MonitoringExporter, CreateServiceTimeSeries) { auto mock = std::make_shared(); diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc index 55c60e22c373a..e7b8a36bbc785 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl.cc @@ -16,7 +16,7 @@ #include "google/cloud/storage/internal/grpc/metrics_exporter_impl.h" #include "google/cloud/monitoring/v3/metric_connection.h" -#include "google/cloud/opentelemetry/monitoring_exporter.h" +#include "google/cloud/opentelemetry/internal/monitoring_exporter.h" #include "google/cloud/storage/grpc_plugin.h" #include "google/cloud/storage/internal/grpc/metrics_exporter_options.h" #include "google/cloud/storage/internal/grpc/metrics_meter_provider.h" @@ -92,7 +92,7 @@ absl::optional MakeMeterProviderConfig( auto exporter_options = MetricsExporterOptions(*project, resource); if (options.has()) { - exporter_options.set( + exporter_options.set( options.get()); } From ea2086f8bdd77b714fafe54797fb9a0bef22953f Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Wed, 12 Nov 2025 05:22:35 +0000 Subject: [PATCH 5/6] adding tests --- .../grpc/metrics_exporter_impl_test.cc | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc index b266f262c61e9..405ab48cf1e86 100644 --- a/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc +++ b/google/cloud/storage/internal/grpc/metrics_exporter_impl_test.cc @@ -15,6 +15,7 @@ #ifdef GOOGLE_CLOUD_CPP_STORAGE_WITH_OTEL_METRICS #include "google/cloud/storage/internal/grpc/metrics_exporter_impl.h" +#include "google/cloud/opentelemetry/internal/monitoring_exporter.h" #include "google/cloud/opentelemetry/monitoring_exporter.h" #include "google/cloud/storage/grpc_plugin.h" #include "google/cloud/storage/internal/grpc/default_options.h" @@ -156,6 +157,64 @@ TEST(GrpcMetricsExporter, ReaderOptionsAreSetFromConfig) { std::chrono::milliseconds(expected_timeout)); } +TEST(MakeMeterProviderConfigTest, NoExcludedLabels) { + auto resource = opentelemetry::sdk::resource::Resource::Create( + {{"service.name", "test-service"}, {"service.version", "1.0.0"}}); + + Options options; + options.set(true); + options.set("test-project"); + + auto config = MakeMeterProviderConfig(resource, options); + + ASSERT_TRUE(config.has_value()); + EXPECT_FALSE(config->exporter_options + .has()); +} + +TEST(MakeMeterProviderConfigTest, WithExcludedLabels) { + auto resource = opentelemetry::sdk::resource::Resource::Create( + {{"service.name", "test-service"}, {"service.version", "1.0.0"}}); + + std::set excluded_labels{"service_name", "service_version"}; + Options options; + options.set(true); + options.set("test-project"); + options.set( + excluded_labels); + + auto config = MakeMeterProviderConfig(resource, options); + + ASSERT_TRUE(config.has_value()); + EXPECT_TRUE(config->exporter_options + .has()); + + auto actual_excluded = + config->exporter_options.get(); + EXPECT_EQ(excluded_labels, actual_excluded); +} + +TEST(MakeMeterProviderConfigTest, EmptyExcludedLabels) { + auto resource = opentelemetry::sdk::resource::Resource::Create( + {{"service.name", "test-service"}}); + + Options options; + options.set(true); + options.set("test-project"); + options.set( + std::set{}); + + auto config = MakeMeterProviderConfig(resource, options); + + ASSERT_TRUE(config.has_value()); + EXPECT_TRUE(config->exporter_options + .has()); + + auto actual_excluded = + config->exporter_options.get(); + EXPECT_TRUE(actual_excluded.empty()); +} + } // namespace GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END } // namespace storage_internal From 668676dc100a1da3c06d25205ced49e2161a0e00 Mon Sep 17 00:00:00 2001 From: Shubham Kaushal Date: Fri, 14 Nov 2025 07:48:38 +0000 Subject: [PATCH 6/6] resolving comments --- .../internal/monitoring_exporter.cc | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/google/cloud/opentelemetry/internal/monitoring_exporter.cc b/google/cloud/opentelemetry/internal/monitoring_exporter.cc index 03ede5af85ea6..48dd990fd87c5 100644 --- a/google/cloud/opentelemetry/internal/monitoring_exporter.cc +++ b/google/cloud/opentelemetry/internal/monitoring_exporter.cc @@ -31,7 +31,8 @@ std::string FormatProjectFullName(std::string const& project) { return absl::StrCat("projects/", project); } -otel_internal::ResourceFilterDataFn MakeFilter(Options const& options) { +otel_internal::ResourceFilterDataFn MakeResourceFilterFn( + Options const& options) { if (!options.has()) { return nullptr; } @@ -47,6 +48,24 @@ otel_internal::ResourceFilterDataFn MakeFilter(Options const& options) { }; } +otel_internal::MonitoredResourceFromDataFn MakeDynamicResourceFn( + Options const& options, absl::optional const& project, + absl::optional const& mr_proto) { + if (!options.has()) { + return nullptr; + } + + // `resource_filter_fn_` and `dynamic_resource_fn_` are meant to be used as a + // pair. Here we have a filter but no dynamic function, create a default one + // that returns the same project and monitored resource for all data points. + auto project_id = project->project_id(); + auto monitored_resource = mr_proto.value_or(google::api::MonitoredResource{}); + return [project_id, monitored_resource]( + opentelemetry::sdk::metrics::PointDataAttributes const&) { + return std::make_pair(project_id, monitored_resource); + }; +} + } // namespace MonitoringExporter::MonitoringExporter( @@ -65,9 +84,10 @@ MonitoringExporter::MonitoringExporter( Project project, std::shared_ptr conn, Options const& options) - : MonitoringExporter(std::move(conn), nullptr, MakeFilter(options), - options) { + : MonitoringExporter(std::move(conn), nullptr, nullptr, options) { project_ = std::move(project); + resource_filter_fn_ = MakeResourceFilterFn(options); + dynamic_resource_fn_ = MakeDynamicResourceFn(options, project_, mr_proto_); } opentelemetry::sdk::common::ExportResult MonitoringExporter::Export( @@ -115,19 +135,7 @@ opentelemetry::sdk::common::ExportResult MonitoringExporter::ExportImpl( } std::vector requests; - if (dynamic_resource_fn_ || resource_filter_fn_) { - // If `resource_filter_fn_` is provided, we must use - // `ToTimeSeriesWithResources`, which requires a dynamic resource - // function. If `dynamic_resource_fn_` is not provided, create a - // default implementation. - if (!dynamic_resource_fn_) { - auto mr = otel_internal::ToMonitoredResource(data, mr_proto_); - dynamic_resource_fn_ = - [mr, p = project_->project_id()]( - opentelemetry::sdk::metrics::PointDataAttributes const&) { - return std::make_pair(p, mr); - }; - } + if (dynamic_resource_fn_) { auto tss_map = otel_internal::ToTimeSeriesWithResources( data, formatter_, resource_filter_fn_, dynamic_resource_fn_); for (auto& tss : tss_map) {