SERVER-114949 Create otel::metrics::Histogram<T> type (#45065)

GitOrigin-RevId: 87b2773a909e88ecd30da0ca96fd281af552fc5e
This commit is contained in:
Cedric Sirianni 2025-12-11 12:51:24 -05:00 committed by MongoDB Bot
parent 61a3f84ed8
commit de71b57854
3 changed files with 232 additions and 0 deletions

View File

@ -98,3 +98,15 @@ mongo_cc_unit_test(
"//src/third_party/opentelemetry-cpp/sdk/src/metrics",
],
)
mongo_cc_unit_test(
name = "metrics_histogram_test",
srcs = [
"metrics_histogram_test.cpp",
],
tags = ["mongo_unittest_sixth_group"],
target_compatible_with = OTEL_TARGET_COMPATIBLE_WITH,
deps = [
"//src/third_party/opentelemetry-cpp/api",
],
)

View File

@ -0,0 +1,137 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/base/error_codes.h"
#include "mongo/util/assert_util.h"
#include <opentelemetry/context/context.h>
#include <opentelemetry/metrics/meter.h>
namespace mongo::otel::metrics {
/**
* Concept that restricts histogram types to int64_t and double only.
*/
template <typename T>
concept HistogramValueType = std::same_as<T, int64_t> || std::same_as<T, double>;
template <HistogramValueType T>
class MONGO_MOD_PUBLIC Histogram {
/**
* Records a value.
*
* The value must be nonnegative.
*/
virtual void record(T value) = 0;
virtual const std::string& name() const = 0;
virtual const std::string& description() const = 0;
virtual const std::string& unit() const = 0;
};
/**
* Thin wrapper around OpenTelemetry Histogram for recording distributions of values.
*
* Supported types: int64_t and double.
*
* WARNING: The underlying OpenTelemetry library acquires locks during Record() operations.
* Avoid using histograms in performance-sensitive code paths where lock contention could
* impact latency/throughput.
*/
template <HistogramValueType T>
class HistogramImpl : public Histogram<T> {
public:
/**
* Creates a new Histogram instance.
*
* Meter must be non-null and remain valid for the lifetime of the Histogram instance.
*/
HistogramImpl(opentelemetry::metrics::Meter* meter,
std::string name,
std::string description,
std::string unit);
/**
* Records a value.
*
* The value must be nonnegative.
*/
void record(T value) override;
const std::string& name() const override {
return _name;
}
const std::string& description() const override {
return _description;
}
const std::string& unit() const override {
return _unit;
}
private:
std::string _name;
std::string _description;
std::string _unit;
using UnderlyingType = std::conditional_t<std::is_same_v<T, int64_t>, uint64_t, double>;
// The underlying OpenTelemety histogram implementation.
std::unique_ptr<opentelemetry::metrics::Histogram<UnderlyingType>> _histogram;
};
template <HistogramValueType T>
HistogramImpl<T>::HistogramImpl(opentelemetry::metrics::Meter* meter,
std::string name,
std::string description,
std::string unit)
: _name(std::move(name)), _description(std::move(description)), _unit(std::move(unit)) {
invariant(meter);
if constexpr (std::is_same_v<T, int64_t>) {
// The OpenTelemetry library provides histogram implementations for uint64_t and double.
// If a negative integer is passed to `HistogramImpl<uint64_t>::record`, it wraps to an
// extremely large positive value due to unsigned conversion. To prevent this unintended
// behavior, we use int64_t as our API type but store an uint64_t histogram, allowing
// us to explicitly reject negative inputs to the record member function.
_histogram = meter->CreateUInt64Histogram(name, description, unit);
} else {
_histogram = meter->CreateDoubleHistogram(name, description, unit);
}
}
template <HistogramValueType T>
void HistogramImpl<T>::record(T value) {
massert(ErrorCodes::BadValue, "Histogram values must be nonnegative", value >= 0);
_histogram->Record(value, opentelemetry::context::Context{});
}
} // namespace mongo::otel::metrics

View File

@ -0,0 +1,83 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/otel/metrics/metrics_histogram.h"
#include "mongo/unittest/unittest.h"
#include <opentelemetry/metrics/noop.h>
#include <opentelemetry/metrics/provider.h>
namespace mongo::otel::metrics {
namespace {
class OtelMetricsHistogramTest : public unittest::Test {
public:
void setUp() override {
opentelemetry::metrics::Provider::SetMeterProvider(
std::make_shared<opentelemetry::metrics::NoopMeterProvider>());
}
void tearDown() override {
opentelemetry::metrics::Provider::SetMeterProvider({});
}
};
} // namespace
TEST_F(OtelMetricsHistogramTest, Int64Histogram) {
auto histogram = HistogramImpl<int64_t>(
opentelemetry::metrics::Provider::GetMeterProvider()->GetMeter("test_meter").get(),
"name",
"description",
"unit");
histogram.record(0);
histogram.record(std::numeric_limits<int64_t>::max());
// Implicit cast to int
histogram.record(1.0);
ASSERT_THROWS_CODE(histogram.record(-1), DBException, ErrorCodes::BadValue);
// Using two's complement, 0xFFFFFFFFFFFFFFFF interpreted as int64_t is -1.
ASSERT_THROWS_CODE(
histogram.record(std::numeric_limits<uint64_t>::max()), DBException, ErrorCodes::BadValue);
}
TEST_F(OtelMetricsHistogramTest, DoubleHistogram) {
auto histogram = HistogramImpl<double>(
opentelemetry::metrics::Provider::GetMeterProvider()->GetMeter("test_meter").get(),
"name",
"description",
"unit");
histogram.record(0.0);
histogram.record(std::numeric_limits<double>::max());
// Implicit cast to double.
histogram.record(1);
ASSERT_THROWS_CODE(histogram.record(-1), DBException, ErrorCodes::BadValue);
}
} // namespace mongo::otel::metrics