Skip to content

Commit 5517146

Browse files
committed
add GrpcMetricsExcludedLabelsOption for filtering
1 parent 320bd49 commit 5517146

File tree

10 files changed

+271
-36
lines changed

10 files changed

+271
-36
lines changed

google/cloud/opentelemetry/internal/monitoring_exporter.cc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,8 @@ std::string FormatProjectFullName(std::string const& project) {
3535

3636
MonitoringExporter::MonitoringExporter(
3737
std::shared_ptr<monitoring_v3::MetricServiceConnection> conn,
38-
otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn,
39-
otel_internal::ResourceFilterDataFn resource_filter_fn,
40-
Options const& options)
38+
otel::MonitoredResourceFromDataFn dynamic_resource_fn,
39+
otel::ResourceFilterDataFn resource_filter_fn, Options const& options)
4140
: client_(std::move(conn)),
4241
formatter_(options.get<otel::MetricNameFormatterOption>()),
4342
use_service_time_series_(options.get<otel::ServiceTimeSeriesOption>()),
@@ -49,7 +48,10 @@ MonitoringExporter::MonitoringExporter(
4948
Project project,
5049
std::shared_ptr<monitoring_v3::MetricServiceConnection> conn,
5150
Options const& options)
52-
: MonitoringExporter(std::move(conn), nullptr, nullptr, options) {
51+
: MonitoringExporter(std::move(conn),
52+
options.get<otel::MonitoredResourceFromDataFnOption>(),
53+
options.get<otel::ResourceFilterDataFnOption>(),
54+
options) {
5355
project_ = std::move(project);
5456
}
5557

@@ -98,7 +100,19 @@ opentelemetry::sdk::common::ExportResult MonitoringExporter::ExportImpl(
98100
}
99101

100102
std::vector<google::monitoring::v3::CreateTimeSeriesRequest> requests;
101-
if (dynamic_resource_fn_) {
103+
if (dynamic_resource_fn_ || resource_filter_fn_) {
104+
// If `resource_filter_fn_` is provided, we must use
105+
// `ToTimeSeriesWithResources`, which requires a dynamic resource
106+
// function. If `dynamic_resource_fn_` is not provided, create a
107+
// default implementation.
108+
if (!dynamic_resource_fn_) {
109+
auto mr = otel_internal::ToMonitoredResource(data, mr_proto_);
110+
dynamic_resource_fn_ =
111+
[mr, p = project_->project_id()](
112+
opentelemetry::sdk::metrics::PointDataAttributes const&) {
113+
return std::make_pair(p, mr);
114+
};
115+
}
102116
auto tss_map = otel_internal::ToTimeSeriesWithResources(
103117
data, formatter_, resource_filter_fn_, dynamic_resource_fn_);
104118
for (auto& tss : tss_map) {
@@ -126,8 +140,8 @@ Options DefaultOptions(Options o) {
126140
}
127141

128142
std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter>
129-
MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn,
130-
ResourceFilterDataFn resource_filter_fn,
143+
MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn,
144+
otel::ResourceFilterDataFn resource_filter_fn,
131145
Options options) {
132146
// TODO(#15321): Determine which options, if any, should be passed along from
133147
// the options parameter to MakeMetricServiceConnection.
@@ -140,8 +154,8 @@ MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn,
140154

141155
std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter>
142156
MakeMonitoringExporter(
143-
MonitoredResourceFromDataFn dynamic_resource_fn,
144-
ResourceFilterDataFn resource_filter_fn,
157+
otel::MonitoredResourceFromDataFn dynamic_resource_fn,
158+
otel::ResourceFilterDataFn resource_filter_fn,
145159
std::shared_ptr<monitoring_v3::MetricServiceConnection> conn,
146160
Options options) {
147161
options = DefaultOptions(std::move(options));

google/cloud/opentelemetry/internal/monitoring_exporter.h

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,13 @@ namespace cloud {
3333
namespace otel_internal {
3434
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3535

36-
// For use with dynamic monitored resources, this function constructs the
37-
// correct MonitoredResource from the PointDataAttributes passed in. This
38-
// function is called in ToTimeSeriesWithResources.
39-
using MonitoredResourceFromDataFn =
40-
std::function<std::pair<std::string, google::api::MonitoredResource>(
41-
opentelemetry::sdk::metrics::PointDataAttributes const&)>;
42-
43-
// For use with dynamic monitored resources, this function is used in ToMetric
44-
// to indicate which labels should be skipped when populating the labels field
45-
// of the google::api::Metric proto.
46-
using ResourceFilterDataFn = std::function<bool(std::string const&)>;
47-
4836
class MonitoringExporter final
4937
: public opentelemetry::sdk::metrics::PushMetricExporter {
5038
public:
5139
MonitoringExporter(
5240
std::shared_ptr<monitoring_v3::MetricServiceConnection> conn,
53-
otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn,
54-
otel_internal::ResourceFilterDataFn resource_filter_fn,
55-
Options const& options);
41+
otel::MonitoredResourceFromDataFn dynamic_resource_fn,
42+
otel::ResourceFilterDataFn resource_filter_fn, Options const& options);
5643

5744
MonitoringExporter(
5845
Project project,
@@ -84,21 +71,21 @@ class MonitoringExporter final
8471
otel::MetricNameFormatterOption::Type formatter_;
8572
bool use_service_time_series_;
8673
absl::optional<google::api::MonitoredResource> mr_proto_;
87-
otel_internal::MonitoredResourceFromDataFn dynamic_resource_fn_;
88-
otel_internal::ResourceFilterDataFn resource_filter_fn_;
74+
otel::MonitoredResourceFromDataFn dynamic_resource_fn_;
75+
otel::ResourceFilterDataFn resource_filter_fn_;
8976
};
9077

9178
Options DefaultOptions(Options o);
9279

9380
std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter>
94-
MakeMonitoringExporter(MonitoredResourceFromDataFn dynamic_resource_fn,
95-
ResourceFilterDataFn resource_filter_fn,
81+
MakeMonitoringExporter(otel::MonitoredResourceFromDataFn dynamic_resource_fn,
82+
otel::ResourceFilterDataFn resource_filter_fn,
9683
Options options = {});
9784

9885
std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter>
9986
MakeMonitoringExporter(
100-
MonitoredResourceFromDataFn dynamic_resource_fn,
101-
ResourceFilterDataFn resource_filter_fn,
87+
otel::MonitoredResourceFromDataFn dynamic_resource_fn,
88+
otel::ResourceFilterDataFn resource_filter_fn,
10289
std::shared_ptr<monitoring_v3::MetricServiceConnection> conn,
10390
Options options = {});
10491

google/cloud/opentelemetry/internal/time_series.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ google::api::Metric ToMetric(
133133
opentelemetry::sdk::metrics::PointAttributes const& attributes,
134134
opentelemetry::sdk::resource::Resource const* resource,
135135
std::function<std::string(std::string)> const& name_formatter,
136-
ResourceFilterDataFn const& resource_filter_fn) {
136+
otel::ResourceFilterDataFn const& resource_filter_fn) {
137137
auto add_label = [&resource_filter_fn](auto& labels, auto key,
138138
auto const& value) {
139139
// GCM labels match on the regex: R"([a-zA-Z_][a-zA-Z0-9_]*)".
@@ -304,8 +304,8 @@ std::unordered_map<std::string, std::vector<google::monitoring::v3::TimeSeries>>
304304
ToTimeSeriesWithResources(
305305
opentelemetry::sdk::metrics::ResourceMetrics const& data,
306306
std::function<std::string(std::string)> const& metrics_name_formatter,
307-
ResourceFilterDataFn const& resource_filter_fn,
308-
MonitoredResourceFromDataFn const& dynamic_resource_fn) {
307+
otel::ResourceFilterDataFn const& resource_filter_fn,
308+
otel::MonitoredResourceFromDataFn const& dynamic_resource_fn) {
309309
std::unordered_map<std::string,
310310
std::vector<google::monitoring::v3::TimeSeries>>
311311
tss_map;

google/cloud/opentelemetry/internal/time_series.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ google::api::Metric ToMetric(
4141
opentelemetry::sdk::metrics::PointAttributes const& attributes,
4242
opentelemetry::sdk::resource::Resource const* resource,
4343
std::function<std::string(std::string)> const& metrics_name_formatter,
44-
ResourceFilterDataFn const& resource_filter_fn);
44+
otel::ResourceFilterDataFn const& resource_filter_fn);
4545

4646
google::api::Metric ToMetric(
4747
opentelemetry::sdk::metrics::MetricData const& metric_data,
@@ -94,8 +94,8 @@ std::unordered_map<std::string, std::vector<google::monitoring::v3::TimeSeries>>
9494
ToTimeSeriesWithResources(
9595
opentelemetry::sdk::metrics::ResourceMetrics const& data,
9696
std::function<std::string(std::string)> const& metrics_name_formatter,
97-
ResourceFilterDataFn const& resource_filter_fn,
98-
MonitoredResourceFromDataFn const& resource_fn);
97+
otel::ResourceFilterDataFn const& resource_filter_fn,
98+
otel::MonitoredResourceFromDataFn const& resource_fn);
9999

100100
bool IsEmptyTimeSeries(
101101
opentelemetry::sdk::metrics::ResourceMetrics const& data);

google/cloud/opentelemetry/monitoring_exporter.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "google/cloud/opentelemetry/internal/recordable.h"
2020
#include "google/cloud/project.h"
2121
#include "google/cloud/version.h"
22+
#include <opentelemetry/sdk/metrics/data/metric_data.h>
2223
#include <opentelemetry/sdk/metrics/push_metric_exporter.h>
2324
#include <functional>
2425
#include <memory>
@@ -29,6 +30,18 @@ namespace cloud {
2930
namespace otel {
3031
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3132

33+
// For use with dynamic monitored resources, this function constructs the
34+
// correct MonitoredResource from the PointDataAttributes passed in. This
35+
// function is called in ToTimeSeriesWithResources.
36+
using MonitoredResourceFromDataFn =
37+
std::function<std::pair<std::string, google::api::MonitoredResource>(
38+
opentelemetry::sdk::metrics::PointDataAttributes const&)>;
39+
40+
// For use with dynamic monitored resources, this function is used in ToMetric
41+
// to indicate which labels should be skipped when populating the labels field
42+
// of the google::api::Metric proto.
43+
using ResourceFilterDataFn = std::function<bool(std::string const&)>;
44+
3245
/**
3346
* Change formatting for metric names.
3447
*
@@ -75,6 +88,26 @@ struct MonitoredResourceOption {
7588
using Type = google::api::MonitoredResource;
7689
};
7790

91+
/**
92+
* Override the monitored resource builder.
93+
*
94+
* This option is primarily relevant to Google applications and libraries. It
95+
* can be ignored by external developers.
96+
*/
97+
struct MonitoredResourceFromDataFnOption {
98+
using Type = MonitoredResourceFromDataFn;
99+
};
100+
101+
/**
102+
* Filter resource labels.
103+
*
104+
* This option is primarily relevant to Google applications and libraries. It
105+
* can be ignored by external developers.
106+
*/
107+
struct ResourceFilterDataFnOption {
108+
using Type = ResourceFilterDataFn;
109+
};
110+
78111
std::unique_ptr<opentelemetry::sdk::metrics::PushMetricExporter>
79112
MakeMonitoringExporter(
80113
Project project,

google/cloud/opentelemetry/monitoring_exporter_test.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,59 @@ TEST(MonitoringExporter, CustomMonitoredResource) {
264264
EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
265265
}
266266

267+
TEST(MonitoringExporter, CustomMonitoredResourceFromDataFunction) {
268+
auto mock =
269+
std::make_shared<monitoring_v3_mocks::MockMetricServiceConnection>();
270+
EXPECT_CALL(*mock, CreateTimeSeries)
271+
.WillOnce(
272+
[](google::monitoring::v3::CreateTimeSeriesRequest const& request) {
273+
EXPECT_THAT(request.name(), "projects/test-project");
274+
EXPECT_THAT(request.time_series(), SizeIs(2));
275+
EXPECT_THAT(request.time_series(),
276+
Each(ResourceType("test_resource")));
277+
return Status();
278+
});
279+
280+
// Define a function that maps PointDataAttributes to a MonitoredResource.
281+
auto resource_fn = [](opentelemetry::sdk::metrics::PointDataAttributes const&)
282+
-> std::pair<std::string, google::api::MonitoredResource> {
283+
google::api::MonitoredResource resource;
284+
resource.set_type("test_resource");
285+
return {"test-project", resource};
286+
};
287+
288+
auto options = Options{}.set<MonitoredResourceFromDataFnOption>(resource_fn);
289+
auto exporter =
290+
MakeMonitoringExporter(Project("test-project"), std::move(mock), options);
291+
auto data = MakeResourceMetrics(/*expected_time_series_count=*/2);
292+
auto result = exporter->Export(data);
293+
EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
294+
}
295+
296+
TEST(MonitoringExporter, CustomResourceFilterDataFunction) {
297+
auto mock =
298+
std::make_shared<monitoring_v3_mocks::MockMetricServiceConnection>();
299+
EXPECT_CALL(*mock, CreateTimeSeries)
300+
.WillOnce(
301+
[](google::monitoring::v3::CreateTimeSeriesRequest const& request) {
302+
EXPECT_THAT(request.name(), "projects/test-project");
303+
EXPECT_THAT(request.time_series(), SizeIs(2));
304+
return Status();
305+
});
306+
307+
// Define a function that filters out labels starting with "internal_".
308+
auto filter_fn = [](std::string const& label) -> bool {
309+
return label.rfind("internal_", 0) == 0;
310+
};
311+
312+
auto options = Options{}.set<ResourceFilterDataFnOption>(filter_fn);
313+
auto exporter =
314+
MakeMonitoringExporter(Project("test-project"), std::move(mock), options);
315+
auto data = MakeResourceMetrics(/*expected_time_series_count=*/2);
316+
auto result = exporter->Export(data);
317+
EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
318+
}
319+
267320
TEST(MonitoringExporter, CreateServiceTimeSeries) {
268321
auto mock =
269322
std::make_shared<monitoring_v3_mocks::MockMetricServiceConnection>();

google/cloud/storage/grpc_plugin.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,27 @@ struct GrpcMetricsExportTimeoutOption {
131131
using Type = std::chrono::seconds;
132132
};
133133

134+
/**
135+
* gRPC telemetry excluded labels.
136+
*
137+
* A set of OpenTelemetry resource attribute keys to exclude from metric labels
138+
* when exporting gRPC telemetry. For example, to exclude the `service.name`
139+
* label, configure the option with `{"service.name"}`.
140+
*
141+
* @par Example: Exclude specific labels from telemetry
142+
* @code
143+
* namespace gcs_ex = google::cloud::storage_experimental;
144+
* auto client = google::cloud::storage::MakeGrpcClient(
145+
* google::cloud::Options{}
146+
* .set<gcs_ex::EnableGrpcMetricsOption>(true)
147+
* .set<gcs_ex::GrpcMetricsExcludedLabelsOption>(
148+
* std::set<std::string>{"service_name", "service_version"}));
149+
* @endcode
150+
*/
151+
struct GrpcMetricsExcludedLabelsOption {
152+
using Type = std::set<std::string>;
153+
};
154+
134155
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
135156
} // namespace storage_experimental
136157
} // namespace cloud

google/cloud/storage/grpc_plugin_test.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,40 @@ TEST(GrpcPluginTest, BackwardsCompatibilityShims) {
138138
}
139139
#include "google/cloud/internal/diagnostics_pop.inc"
140140

141+
TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOption) {
142+
auto const expected =
143+
std::set<std::string>{"service_name", "service_version", "custom_label"};
144+
auto opts =
145+
google::cloud::Options{}
146+
.set<storage_experimental::GrpcMetricsExcludedLabelsOption>(expected);
147+
148+
EXPECT_EQ(expected,
149+
opts.get<storage_experimental::GrpcMetricsExcludedLabelsOption>());
150+
}
151+
152+
TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOptionEmpty) {
153+
auto const expected = std::set<std::string>{};
154+
auto opts =
155+
google::cloud::Options{}
156+
.set<storage_experimental::GrpcMetricsExcludedLabelsOption>(expected);
157+
158+
EXPECT_TRUE(opts.get<storage_experimental::GrpcMetricsExcludedLabelsOption>()
159+
.empty());
160+
}
161+
162+
TEST(GrpcPluginTest, GrpcMetricsExcludedLabelsOptionSingle) {
163+
auto const expected = std::set<std::string>{"service_name"};
164+
auto opts =
165+
google::cloud::Options{}
166+
.set<storage_experimental::GrpcMetricsExcludedLabelsOption>(expected);
167+
168+
EXPECT_EQ(
169+
1,
170+
opts.get<storage_experimental::GrpcMetricsExcludedLabelsOption>().size());
171+
EXPECT_EQ(expected,
172+
opts.get<storage_experimental::GrpcMetricsExcludedLabelsOption>());
173+
}
174+
141175
} // namespace
142176
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
143177
} // namespace storage

google/cloud/storage/internal/grpc/metrics_exporter_impl.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ class ExporterRegistry {
7979
std::mutex mu_;
8080
};
8181

82+
otel::ResourceFilterDataFn MakeFilter(Options const& options) {
83+
// Check for explicit filter function first.
84+
if (options.has<otel::ResourceFilterDataFnOption>()) {
85+
return options.get<otel::ResourceFilterDataFnOption>();
86+
}
87+
88+
// Check for excluded labels list.
89+
if (!options.has<storage_experimental::GrpcMetricsExcludedLabelsOption>()) {
90+
return nullptr;
91+
}
92+
auto const& excluded =
93+
options.get<storage_experimental::GrpcMetricsExcludedLabelsOption>();
94+
if (excluded.empty()) return nullptr;
95+
96+
// Capture by value to avoid dangling reference in the lambda.
97+
return [excluded](std::string const& key) -> bool {
98+
return excluded.count(key) > 0;
99+
};
100+
}
101+
82102
} // namespace
83103

84104
absl::optional<ExporterConfig> MakeMeterProviderConfig(
@@ -91,6 +111,8 @@ absl::optional<ExporterConfig> MakeMeterProviderConfig(
91111
if (!project) return absl::nullopt;
92112

93113
auto exporter_options = MetricsExporterOptions(*project, resource);
114+
exporter_options.set<otel::ResourceFilterDataFnOption>(MakeFilter(options));
115+
94116
auto exporter_connection_options = MetricsExporterConnectionOptions(options);
95117
return ExporterConfig{std::move(*project), std::move(exporter_options),
96118
std::move(exporter_connection_options),

0 commit comments

Comments
 (0)