Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Increment the:

* [TEST] Remove workaround for metrics cardinality limit test
[#3663](https://github.com/open-telemetry/opentelemetry-cpp/pull/3663)
* [METRICS] Allow registering one callback for multiple instruments
[#3667](https://github.com/open-telemetry/opentelemetry-cpp/pull/3667)

* [SDK] Fix typo in hashmap method GetEnteries
[#3680](https://github.com/open-telemetry/opentelemetry-cpp/pull/3680)
Expand Down
31 changes: 31 additions & 0 deletions api/include/opentelemetry/metrics/meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

#pragma once

#include <cstdint>

#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/span.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/version.h"
Expand All @@ -25,6 +28,8 @@ template <typename T>
class Gauge;

class ObservableInstrument;
class MultiObserverResult;
using MultiObservableCallbackPtr = void (*)(MultiObserverResult &, void *);

/**
* Handles instrument creation and provides a facility for batch recording.
Expand Down Expand Up @@ -169,6 +174,32 @@ class Meter
nostd::string_view name,
nostd::string_view description = "",
nostd::string_view unit = "") noexcept = 0;

#if OPENTELEMETRY_ABI_VERSION_NO >= 2

/**
* Registers a callback to be invoked when metrics are collected by this meter. The callback will
* be passed a MultiObserverResult which it can use to make observations for any or all of the
* instruments provided in this registration. Any measurements recorded for instruments _not_ in
* the initial RegisterCallback call will be discarded.
*
* @param callback the callback to be invoked.
* @param state the state to be passed to the callback.
* @param instruments the instruments to be observed.
* @return a unique identifier for the registered callback, which can be used to unregister the
* callback in DeregisterCallback.
*/
virtual uintptr_t RegisterCallback(MultiObservableCallbackPtr callback,
void *state,
nostd::span<ObservableInstrument *> instruments) noexcept = 0;

/**
* Unregisters a callback previously registered with RegisterCallback.
*
* @param callback_id the unique identifier returned by RegisterCallback.
*/
virtual void DeregisterCallback(uintptr_t callback_id) noexcept = 0;
#endif
};
} // namespace metrics
OPENTELEMETRY_END_NAMESPACE
57 changes: 57 additions & 0 deletions api/include/opentelemetry/metrics/multi_observer_result.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstdint>

#include "opentelemetry/metrics/async_instruments.h"
#include "opentelemetry/metrics/observer_result.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace metrics
{

class MultiObserverResult
{

public:
virtual ~MultiObserverResult() = default;

/**
* Obtain an ObserverResultT<T> for the given instrument, that can be used to record
* a measurement on said instrument from a multi-observer callback registered with
* Meter::RegisterCallback. The instrument _must_ have been included in the original
* call to Meter::RegisterCallback; any data points set on other instruments will be
* discarded.
*
* @param instrument The instrument for which to obtain an ObserverResult.
* @return An ObserverResultT<T> for the given instrument.
*/
template <typename T>
ObserverResultT<T> &ForInstrument(const ObservableInstrument *instrument) = delete;

protected:
// You can't have a virtual template, and you can't overload on return type, so we need to
// enumerate the options for the observer result type as separate methods to override.
virtual ObserverResultT<double> &ForInstrumentDouble(const ObservableInstrument *instrument) = 0;
virtual ObserverResultT<int64_t> &ForInstrumentInt64(const ObservableInstrument *instrument) = 0;
};

template <>
inline ObserverResultT<double> &MultiObserverResult::ForInstrument<double>(
const ObservableInstrument *instrument)
{
return ForInstrumentDouble(instrument);
}

template <>
inline ObserverResultT<int64_t> &MultiObserverResult::ForInstrument<int64_t>(
const ObservableInstrument *instrument)
{
return ForInstrumentInt64(instrument);
}

} // namespace metrics
OPENTELEMETRY_END_NAMESPACE
12 changes: 12 additions & 0 deletions api/include/opentelemetry/metrics/noop.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ class NoopMeter final : public Meter
return nostd::shared_ptr<ObservableInstrument>(
new NoopObservableInstrument(name, description, unit));
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
uintptr_t RegisterCallback(
MultiObservableCallbackPtr /* callback */,
void * /* state */,
nostd::span<ObservableInstrument *> /* instruments */) noexcept override
{
return 0;
}

void DeregisterCallback(uintptr_t /* callback_id */) noexcept override {}
#endif
};

/**
Expand Down
8 changes: 8 additions & 0 deletions sdk/include/opentelemetry/sdk/metrics/meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ class Meter final : public opentelemetry::metrics::Meter
std::vector<MetricData> Collect(CollectorHandle *collector,
opentelemetry::common::SystemTimestamp collect_ts) noexcept;

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
uintptr_t RegisterCallback(
opentelemetry::metrics::MultiObservableCallbackPtr callback,
void *state,
nostd::span<opentelemetry::metrics::ObservableInstrument *> instruments) noexcept override;

void DeregisterCallback(uintptr_t callback_id) noexcept override;
#endif
private:
// order of declaration is important here - instrumentation scope should destroy after
// meter-context.
Expand Down
52 changes: 52 additions & 0 deletions sdk/include/opentelemetry/sdk/metrics/multi_observer_result.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <unordered_map>

#include "opentelemetry/metrics/async_instruments.h"
#include "opentelemetry/metrics/multi_observer_result.h"
#include "opentelemetry/metrics/observer_result.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/metrics/observer_result.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace metrics
{
class OPENTELEMETRY_EXPORT MultiObserverResult final
: public opentelemetry::metrics::MultiObserverResult
{
public:
void RegisterInstrument(opentelemetry::metrics::ObservableInstrument *instrument);
void DeregisterInstrument(opentelemetry::metrics::ObservableInstrument *instrument);
size_t InstrumentCount() const;
bool HasInstrument(const opentelemetry::metrics::ObservableInstrument *instrument) const;
void GetInstruments(
nostd::function_ref<void(opentelemetry::metrics::ObservableInstrument *)> callback);
void Reset();
void StoreResults(opentelemetry::common::SystemTimestamp collection_ts);

protected:
opentelemetry::metrics::ObserverResultT<double> &ForInstrumentDouble(
const opentelemetry::metrics::ObservableInstrument *instrument) override;
opentelemetry::metrics::ObserverResultT<int64_t> &ForInstrumentInt64(
const opentelemetry::metrics::ObservableInstrument *instrument) override;

private:
// This is _different_ to opentelemetry::metrics::ObserverResult because this variant is
// a variant directly of ObserverResultT, not of _pointers_ to ObserverResultT.
// This allows us to avoid an unnescessary layer of inderection and a bunch of allocations.
using ObserverResultDirect =
nostd::variant<nostd::monostate, ObserverResultT<double>, ObserverResultT<int64_t>>;
std::unordered_map<opentelemetry::metrics::ObservableInstrument *, ObserverResultDirect>
observer_results_;
};
} // namespace metrics
} // namespace sdk

OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,52 @@

#pragma once

#include <cstdint>
#include <memory>
#include <mutex>
#include <vector>

#include "opentelemetry/common/timestamp.h"
#include "opentelemetry/metrics/async_instruments.h"
#include "opentelemetry/metrics/meter.h"
#include "opentelemetry/sdk/metrics/multi_observer_result.h"
#include "opentelemetry/version.h"

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
# include <unordered_map>
# include "opentelemetry/nostd/span.h"
#endif

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace metrics
{

struct ObservableCallbackRecord
{
opentelemetry::metrics::ObservableCallbackPtr callback;
void *state;
opentelemetry::metrics::ObservableInstrument *instrument;
};
struct ObservableCallbackRecord;

class ObservableRegistry
{
public:
// Constructor & destructor need to be defined in the observable_registry.cc TU, rather
// than implicitly defaulted here, so that we can have a unique_ptr to an incomplete
// class as a member.
ObservableRegistry();
~ObservableRegistry();

// Add a callback of the single-instrument form
void AddCallback(opentelemetry::metrics::ObservableCallbackPtr callback,
void *state,
opentelemetry::metrics::ObservableInstrument *instrument);

// Add a callback with the multi-instrument signature
uintptr_t AddCallback(opentelemetry::metrics::MultiObservableCallbackPtr callback,
void *state,
nostd::span<opentelemetry::metrics::ObservableInstrument *> instruments);
// Callbacks added with Meter::RegisterCallback have can be removed by passing back the handle
// returned
void RemoveCallback(uintptr_t id);
// Callbacks added with ObservableInstrument::AddCallback can be removed by passing back the
// original (callback function, state, instrument).
void RemoveCallback(opentelemetry::metrics::ObservableCallbackPtr callback,
void *state,
opentelemetry::metrics::ObservableInstrument *instrument);
Expand All @@ -40,7 +58,7 @@ class ObservableRegistry
void Observe(opentelemetry::common::SystemTimestamp collection_ts);

private:
std::vector<std::unique_ptr<ObservableCallbackRecord>> callbacks_;
std::unordered_map<uintptr_t, std::unique_ptr<ObservableCallbackRecord>> callbacks_;
std::mutex callbacks_m_;
};

Expand Down
1 change: 1 addition & 0 deletions sdk/src/metrics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_library(
meter_context.cc
meter_context_factory.cc
metric_reader.cc
multi_observer_result.cc
instrument_metadata_validator.cc
export/periodic_exporting_metric_reader.cc
export/periodic_exporting_metric_reader_factory.cc
Expand Down
19 changes: 19 additions & 0 deletions sdk/src/metrics/meter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
# include "opentelemetry/sdk/metrics/exemplar/reservoir_utils.h"
#endif

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
# include "opentelemetry/metrics/meter.h"
#endif

namespace
{

Expand Down Expand Up @@ -663,6 +667,21 @@ std::vector<MetricData> Meter::Collect(CollectorHandle *collector,
return metric_data_list;
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
uintptr_t Meter::RegisterCallback(
opentelemetry::metrics::MultiObservableCallbackPtr callback,
void *state,
nostd::span<opentelemetry::metrics::ObservableInstrument *> instruments) noexcept
{
return observable_registry_->AddCallback(callback, state, instruments);
}

void Meter::DeregisterCallback(uintptr_t callback_id) noexcept
{
observable_registry_->RemoveCallback(callback_id);
}
#endif

// Implementation of the log message recommended by the SDK specification for duplicate instruments.
// See
// https://github.com/open-telemetry/opentelemetry-specification/blob/9c8c30631b0e288de93df7452f91ed47f6fba330/specification/metrics/sdk.md?plain=1#L882
Expand Down
Loading
Loading