mirror of https://github.com/mongodb/mongo
SERVER-114949 Create otel::metrics::Histogram<T> type (#45065)
GitOrigin-RevId: 87b2773a909e88ecd30da0ca96fd281af552fc5e
This commit is contained in:
parent
61a3f84ed8
commit
de71b57854
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue