Skip to content

Commit

Permalink
impl(otel): convert HistogramDataPoint to protos (#13924)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbolduc authored Apr 5, 2024
1 parent 0d2f4b9 commit 5adda4a
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 0 deletions.
32 changes: 32 additions & 0 deletions google/cloud/opentelemetry/internal/time_series.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ google::api::MetricDescriptor::ValueType ToValueType(
return google::api::MetricDescriptor::INT64;
}

double AsDouble(opentelemetry::sdk::metrics::ValueType const& v) {
return absl::holds_alternative<double>(v)
? absl::get<double>(v)
: static_cast<double>(absl::get<std::int64_t>(v));
}

} // namespace

google::api::Metric ToMetric(
Expand Down Expand Up @@ -134,6 +140,32 @@ google::monitoring::v3::TimeSeries ToTimeSeries(
return ts;
}

google::monitoring::v3::TimeSeries ToTimeSeries(
opentelemetry::sdk::metrics::MetricData const& metric_data,
opentelemetry::sdk::metrics::HistogramPointData const& histogram_data) {
google::monitoring::v3::TimeSeries ts;
ts.set_unit(metric_data.instrument_descriptor.unit_);
ts.set_metric_kind(google::api::MetricDescriptor::CUMULATIVE);
ts.set_value_type(google::api::MetricDescriptor::DISTRIBUTION);

auto& p = *ts.add_points();
*p.mutable_interval() = ToNonGaugeTimeInterval(metric_data);
auto& d = *p.mutable_value()->mutable_distribution_value();
d.set_count(histogram_data.count_);
if (histogram_data.count_ > 0) {
d.set_mean(AsDouble(histogram_data.sum_) /
static_cast<double>(histogram_data.count_));
}
auto& bounds =
*d.mutable_bucket_options()->mutable_explicit_buckets()->mutable_bounds();
bounds.Reserve(static_cast<int>(histogram_data.boundaries_.size()));
for (auto d : histogram_data.boundaries_) bounds.Add(d);
auto& counts = *d.mutable_bucket_counts();
counts.Reserve(static_cast<int>(histogram_data.counts_.size()));
for (auto c : histogram_data.counts_) counts.Add(c);
return ts;
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace otel_internal
} // namespace cloud
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/opentelemetry/internal/time_series.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ google::monitoring::v3::TimeSeries ToTimeSeries(
opentelemetry::sdk::metrics::MetricData const& metric_data,
opentelemetry::sdk::metrics::LastValuePointData const& gauge_data);

google::monitoring::v3::TimeSeries ToTimeSeries(
opentelemetry::sdk::metrics::MetricData const& metric_data,
opentelemetry::sdk::metrics::HistogramPointData const& histogram_data);

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace otel_internal
} // namespace cloud
Expand Down
108 changes: 108 additions & 0 deletions google/cloud/opentelemetry/internal/time_series_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace otel_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
namespace {

using ::testing::_;
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
Expand Down Expand Up @@ -66,6 +67,38 @@ auto Int64TypedValue(std::int64_t v) {
Eq(v));
}

auto distribution = [](auto count, auto mean, auto bucket_counts_matcher,
auto bounds_matcher) {
return AllOf(ResultOf(
"distribution count",
[](google::monitoring::v3::Point const& p) {
return p.value().distribution_value().count();
},
Eq(count)),
ResultOf(
"distribution mean",
[](google::monitoring::v3::Point const& p) {
return p.value().distribution_value().mean();
},
Eq(mean)),
ResultOf(
"distribution bucket_counts",
[](google::monitoring::v3::Point const& p) {
return p.value().distribution_value().bucket_counts();
},
bucket_counts_matcher),
ResultOf(
"distribution bounds",
[](google::monitoring::v3::Point const& p) {
return p.value()
.distribution_value()
.bucket_options()
.explicit_buckets()
.bounds();
},
bounds_matcher));
};

auto Interval(std::chrono::system_clock::time_point start,
std::chrono::system_clock::time_point end) {
return AllOf(
Expand Down Expand Up @@ -261,6 +294,81 @@ TEST(LastValuePointData, DoubleValueTypes) {
}
}

TEST(HistogramPointData, SimpleWithInt64Sum) {
auto const start = std::chrono::system_clock::now();
auto const end = start + std::chrono::seconds(5);

opentelemetry::sdk::metrics::MetricData md;
md.instrument_descriptor.unit_ = "unit";
md.instrument_descriptor.value_type_ =
opentelemetry::sdk::metrics::InstrumentValueType::kInt;
md.start_ts = start;
md.end_ts = end;

opentelemetry::sdk::metrics::HistogramPointData point;
point.sum_ = 64L;
point.boundaries_ = {0.0, 1.0, 2.0, 3.0, 10.0, 30.0};
point.counts_ = {0, 1, 4, 6, 4, 1, 0};
point.count_ = 16;

auto ts = ToTimeSeries(md, point);
EXPECT_EQ(ts.unit(), "unit");
EXPECT_EQ(ts.metric_kind(), google::api::MetricDescriptor::CUMULATIVE);
EXPECT_THAT(
ts.points(),
ElementsAre(AllOf(
distribution(
/*count=*/16, /*mean=*/4.0,
/*bucket_counts_matcher=*/ElementsAre(0, 1, 4, 6, 4, 1, 0),
/*bounds_matcher=*/ElementsAre(0.0, 1.0, 2.0, 3.0, 10.0, 30.0)),
Interval(start, end))));
}

TEST(HistogramPointData, DoubleSum) {
opentelemetry::sdk::metrics::HistogramPointData point;
point.sum_ = 64.0;
point.boundaries_ = {0.0, 1.0, 2.0, 3.0, 10.0, 30.0};
point.counts_ = {0, 1, 4, 6, 4, 1, 0};
point.count_ = 16;

auto ts = ToTimeSeries(opentelemetry::sdk::metrics::MetricData{}, point);
EXPECT_THAT(ts.points(), ElementsAre(distribution(
/*count=*/16, /*mean=*/4.0,
/*bucket_counts_matcher=*/_,
/*bounds_matcher=*/_)));
}

TEST(HistogramPointData, EmptyMean) {
opentelemetry::sdk::metrics::HistogramPointData point;
point.sum_ = 0L;
point.boundaries_ = {0.0, 1.0, 2.0, 3.0, 10.0, 30.0};
point.counts_ = {0, 0, 0, 0, 0, 0, 0};
point.count_ = 0;

auto ts = ToTimeSeries(opentelemetry::sdk::metrics::MetricData{}, point);
EXPECT_THAT(ts.points(), ElementsAre(distribution(
/*count=*/0, /*mean=*/0,
/*bucket_counts_matcher=*/_,
/*bounds_matcher=*/_)));
}

TEST(HistogramPointData, NonEmptyInterval) {
auto const start = std::chrono::system_clock::now();
auto const end = start - std::chrono::seconds(5);
EXPECT_LE(end - start, std::chrono::seconds::zero());

// The spec says to drop the end timestamp, and use the start timestamp plus
// 1ms as the end timestamp.
auto const expected_end = start + std::chrono::milliseconds(1);

opentelemetry::sdk::metrics::MetricData md;
md.start_ts = start;
md.end_ts = end;

auto ts = ToTimeSeries(md, opentelemetry::sdk::metrics::HistogramPointData{});
EXPECT_THAT(ts.points(), ElementsAre(Interval(start, expected_end)));
}

} // namespace
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace otel_internal
Expand Down

0 comments on commit 5adda4a

Please sign in to comment.