Skip to content

Commit 0d58faa

Browse files
authored
test(otel): add GCM exporter integration test (#14097)
1 parent 56b4c94 commit 0d58faa

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed

google/cloud/opentelemetry/integration_tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ find_package(GTest CONFIG REQUIRED)
2323
set(opentelemetry_integration_tests
2424
# cmake-format: sort
2525
configure_basic_tracing_integration_test.cc
26-
trace_exporter_integration_test.cc)
26+
monitoring_exporter_integration_test.cc trace_exporter_integration_test.cc)
2727

2828
# Export the list of unit tests to a .bzl file so we do not need to maintain the
2929
# list in two places.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/monitoring/v3/metric_client.h"
16+
#include "google/cloud/opentelemetry/monitoring_exporter.h"
17+
#include "google/cloud/opentelemetry/resource_detector.h"
18+
#include "google/cloud/internal/getenv.h"
19+
#include "google/cloud/internal/time_utils.h"
20+
#include "google/cloud/testing_util/status_matchers.h"
21+
#include "absl/strings/str_format.h"
22+
#include <gmock/gmock.h>
23+
#include <opentelemetry/metrics/provider.h>
24+
#include <opentelemetry/sdk/metrics/aggregation/default_aggregation.h>
25+
#include <opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h>
26+
#include <opentelemetry/sdk/metrics/meter.h>
27+
#include <opentelemetry/sdk/metrics/meter_provider.h>
28+
#include <opentelemetry/sdk/metrics/push_metric_exporter.h>
29+
#include <opentelemetry/sdk/metrics/view/view_registry.h>
30+
#include <opentelemetry/sdk/resource/resource.h>
31+
#include <opentelemetry/sdk/resource/semantic_conventions.h>
32+
33+
namespace google {
34+
namespace cloud {
35+
namespace otel {
36+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
37+
namespace {
38+
39+
using ::testing::IsEmpty;
40+
using ::testing::Not;
41+
namespace metrics_api = ::opentelemetry::metrics;
42+
namespace metrics_sdk = ::opentelemetry::sdk::metrics;
43+
namespace sc = opentelemetry::sdk::resource::SemanticConventions;
44+
45+
auto constexpr kJobName = "monitoring_exporter_integration_test";
46+
auto constexpr kMeterName =
47+
"gl-cpp/testing/monitoring_exporter_integration_test";
48+
49+
std::string RandomId() {
50+
auto generator = internal::MakeDefaultPRNG();
51+
return internal::Sample(generator, 8, "0123456789");
52+
}
53+
54+
// If this code is unfamiliar, see the "simple" metrics example:
55+
// https://github.com/open-telemetry/opentelemetry-cpp/tree/2d077f8ec5315e0979a236554c81f621eb61f5b3/examples/metrics_simple
56+
void InstallExporter(std::unique_ptr<metrics_sdk::PushMetricExporter> exporter,
57+
std::string const& task_id) {
58+
// GCM requires that metrics be tied to a Monitored Resource. We set
59+
// attributes which will map to a `generic_task`, which seems apt for this
60+
// workflow.
61+
auto resource = opentelemetry::sdk::resource::Resource::Create(
62+
{{sc::kServiceNamespace, "gl-cpp"},
63+
{sc::kServiceName, kJobName},
64+
{sc::kServiceInstanceId, task_id}});
65+
66+
// Initialize and set the global MeterProvider
67+
metrics_sdk::PeriodicExportingMetricReaderOptions options;
68+
options.export_interval_millis = std::chrono::milliseconds(5000);
69+
options.export_timeout_millis = std::chrono::milliseconds(500);
70+
71+
// `PeriodicExportingMetricReaderFactory::Create(...)` was added in
72+
// opentelemetry-cpp v1.10.0. We support >= 1.9.1.
73+
std::unique_ptr<metrics_sdk::MetricReader> reader{
74+
new metrics_sdk::PeriodicExportingMetricReader(std::move(exporter),
75+
options)};
76+
77+
// `MetricProviderFactory::Create(...)` was added in opentelemetry-cpp
78+
// v1.10.0. We support >= 1.9.1.
79+
std::unique_ptr<opentelemetry::metrics::MeterProvider> u_provider(
80+
new metrics_sdk::MeterProvider(
81+
std::make_unique<opentelemetry::sdk::metrics::ViewRegistry>(),
82+
resource));
83+
auto* p = static_cast<metrics_sdk::MeterProvider*>(u_provider.get());
84+
85+
p->AddMetricReader(std::move(reader));
86+
87+
std::shared_ptr<opentelemetry::metrics::MeterProvider> provider(
88+
std::move(u_provider));
89+
metrics_api::Provider::SetMeterProvider(provider);
90+
}
91+
92+
void DoWork() {
93+
auto provider = metrics_api::Provider::GetMeterProvider();
94+
opentelemetry::nostd::shared_ptr<metrics_api::Meter> meter =
95+
provider->GetMeter(kMeterName);
96+
auto double_counter = meter->CreateDoubleCounter(kMeterName);
97+
98+
// This takes 10s to run. That is unfortunate, but necessary because GCM has a
99+
// minimum update period of 5s.
100+
for (std::uint32_t i = 0; i < 20; ++i) {
101+
double_counter->Add(i);
102+
std::this_thread::sleep_for(std::chrono::milliseconds(500));
103+
}
104+
}
105+
106+
TEST(MonitoringExporter, Basic) {
107+
auto project_id = internal::GetEnv("GOOGLE_CLOUD_PROJECT").value_or("");
108+
ASSERT_THAT(project_id, Not(IsEmpty()));
109+
110+
// Uniquely identify the telemetry produced by this run of the test.
111+
auto const task_id = RandomId();
112+
113+
// Create and install the GCM exporter.
114+
auto project = Project(project_id);
115+
auto conn = monitoring_v3::MakeMetricServiceConnection();
116+
auto client = monitoring_v3::MetricServiceClient(conn);
117+
auto exporter =
118+
otel_internal::MakeMonitoringExporter(project, std::move(conn));
119+
InstallExporter(std::move(exporter), task_id);
120+
121+
// Perform work which creates telemetry. An export should happen.
122+
DoWork();
123+
124+
// Verify that the metrics were exported to GCM, by retrieving TimeSeries.
125+
auto const now = std::chrono::system_clock::now();
126+
auto const then = now - std::chrono::minutes(10);
127+
google::monitoring::v3::TimeInterval interval;
128+
*interval.mutable_end_time() = internal::ToProtoTimestamp(now);
129+
*interval.mutable_start_time() = internal::ToProtoTimestamp(then);
130+
auto filter = absl::StrFormat(R"(metric.type = "workload.googleapis.com/%s"
131+
AND resource.labels.job = "%s"
132+
AND resource.labels.task_id = "%s")",
133+
kMeterName, kJobName, task_id);
134+
auto sr = client.ListTimeSeries(
135+
project.FullName(), filter, interval,
136+
google::monitoring::v3::ListTimeSeriesRequest_TimeSeriesView_HEADERS);
137+
std::vector<google::monitoring::v3::TimeSeries> results;
138+
for (auto& ts : sr) {
139+
ASSERT_STATUS_OK(ts);
140+
results.push_back(*std::move(ts));
141+
}
142+
EXPECT_THAT(results, Not(IsEmpty()));
143+
}
144+
145+
} // namespace
146+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
147+
} // namespace otel
148+
} // namespace cloud
149+
} // namespace google

google/cloud/opentelemetry/integration_tests/tests.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818

1919
opentelemetry_integration_tests = [
2020
"configure_basic_tracing_integration_test.cc",
21+
"monitoring_exporter_integration_test.cc",
2122
"trace_exporter_integration_test.cc",
2223
]

0 commit comments

Comments
 (0)