Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Increment the:

## [Unreleased]

* [API/SDK] Add `Enabled()` to synchronous metric instruments (ABI v2). It
returns `false` when the Meter is disabled or every matching view uses the
Drop aggregation
[#4721](https://github.com/open-telemetry/opentelemetry-cpp/pull/4721)

* [API/SDK] Add ASCII helpers and remove regex from InstrumentMetaDataValidator
[#4715](https://github.com/open-telemetry/opentelemetry-cpp/pull/4715)

Expand Down
4 changes: 4 additions & 0 deletions api/include/opentelemetry/common/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ point.

# define OPENTELEMETRY_ATOMIC_READ_8(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST)
# define OPENTELEMETRY_ATOMIC_WRITE_8(ptr, value) __atomic_store_n(ptr, value, __ATOMIC_SEQ_CST)
# define OPENTELEMETRY_ATOMIC_READ_RELAXED_8(ptr) __atomic_load_n(ptr, __ATOMIC_RELAXED)

#elif defined(_MSC_VER)

Expand All @@ -384,6 +385,9 @@ point.
static_cast<uint8_t>(_InterlockedCompareExchange8(reinterpret_cast<char *>(ptr), 0, 0))
# define OPENTELEMETRY_ATOMIC_WRITE_8(ptr, value) \
_InterlockedExchange8(reinterpret_cast<char *>(ptr), static_cast<char>(value))
// A plain load, unlike OPENTELEMETRY_ATOMIC_READ_8 which locks the cache line on every read.
# define OPENTELEMETRY_ATOMIC_READ_RELAXED_8(ptr) \
static_cast<uint8_t>(__iso_volatile_load8(reinterpret_cast<const volatile char *>(ptr)))

#else
# error port atomics read/write for the current platform
Expand Down
22 changes: 18 additions & 4 deletions api/include/opentelemetry/metrics/noop.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ class NoopCounter : public Counter<T>
NoopCounter(nostd::string_view /* name */,
nostd::string_view /* description */,
nostd::string_view /* unit */) noexcept
{}
{
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
this->SetEnabledFlag(this->DisabledFlag());
#endif
}
void Add(T /* value */) noexcept override {}
void Add(T /* value */, const context::Context & /* context */) noexcept override {}
void Add(T /* value */, const common::KeyValueIterable & /* attributes */) noexcept override {}
Expand All @@ -63,7 +67,11 @@ class NoopHistogram : public Histogram<T>
NoopHistogram(nostd::string_view /* name */,
nostd::string_view /* description */,
nostd::string_view /* unit */) noexcept
{}
{
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
this->SetEnabledFlag(this->DisabledFlag());
#endif
}
void Record(T /* value */, const context::Context & /* context */) noexcept override {}
void Record(T /* value */,
const common::KeyValueIterable & /* attributes */,
Expand Down Expand Up @@ -93,7 +101,11 @@ class NoopUpDownCounter : public UpDownCounter<T>
NoopUpDownCounter(nostd::string_view /* name */,
nostd::string_view /* description */,
nostd::string_view /* unit */) noexcept
{}
{
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
this->SetEnabledFlag(this->DisabledFlag());
#endif
}
void Add(T /* value */) noexcept override {}
void Add(T /* value */, const context::Context & /* context */) noexcept override {}
void Add(T /* value */, const common::KeyValueIterable & /* attributes */) noexcept override {}
Expand All @@ -111,7 +123,9 @@ class NoopGauge : public Gauge<T>
NoopGauge(nostd::string_view /* name */,
nostd::string_view /* description */,
nostd::string_view /* unit */) noexcept
{}
{
this->SetEnabledFlag(this->DisabledFlag());
}
void Record(T /* value */) noexcept override {}
void Record(T /* value */, const context::Context & /* context */) noexcept override {}
void Record(T /* value */, const common::KeyValueIterable & /* attributes */) noexcept override {}
Expand Down
40 changes: 40 additions & 0 deletions api/include/opentelemetry/metrics/sync_instruments.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ class SynchronousInstrument
SynchronousInstrument &operator=(const SynchronousInstrument &) = default;
SynchronousInstrument &operator=(SynchronousInstrument &&) noexcept = default;
virtual ~SynchronousInstrument() = default;

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
/**
* Reports if the instrument is enabled or not. A disabled instrument drops its measurements.
*
* The instrumentation authors should call this method each time before recording a
* measurement, to potentially avoid performing computationally expensive operations for
* disabled instruments. The returned value can change over time.
*
* @since ABI_VERSION 2
*/
bool Enabled() const noexcept { return OPENTELEMETRY_ATOMIC_READ_RELAXED_8(enabled_) != 0; }

protected:
/**
* Sets the flag read by Enabled(). SDKs point it at a flag they update; it must outlive this
* instrument.
*
* @since ABI_VERSION 2
*/
void SetEnabledFlag(const bool *enabled) noexcept { enabled_ = enabled; }
Comment thread
adityabavadekar marked this conversation as resolved.
Resolved

/** A flag that is always false, for instruments that are never enabled. */
static const bool *DisabledFlag() noexcept
{
static const bool kDisabled = false;
return &kDisabled;
}

private:
static const bool *EnabledFlag() noexcept
{
static const bool kEnabled = true;
return &kEnabled;
}

// Variable to support implementation of Enabled method introduced in ABI V2.
// A pointer, so an SDK can flip many instruments with a single write.
const bool *enabled_ = EnabledFlag();
Comment thread
adityabavadekar marked this conversation as resolved.
Resolved
#endif
};

#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
Expand Down
15 changes: 15 additions & 0 deletions api/test/metrics/noop_sync_instrument_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,18 @@ TEST(UpDownCountr, Record)
counter->Add(10, {{"k1", "1"}, {"k2", 2}});
counter->Add(10, {{"k1", "1"}, {"k2", 2}}, opentelemetry::context::Context{});
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
TEST(SynchronousInstrument, NoopInstrumentsAreDisabled)
{
opentelemetry::metrics::NoopCounter<uint64_t> counter("test", "none", "unitless");
opentelemetry::metrics::NoopHistogram<double> histogram("test", "none", "unitless");
opentelemetry::metrics::NoopUpDownCounter<int64_t> up_down_counter("test", "none", "unitless");
opentelemetry::metrics::NoopGauge<double> gauge("test", "none", "unitless");

EXPECT_FALSE(counter.Enabled());
EXPECT_FALSE(histogram.Enabled());
EXPECT_FALSE(up_down_counter.Enabled());
EXPECT_FALSE(gauge.Enabled());
}
#endif
8 changes: 7 additions & 1 deletion sdk/include/opentelemetry/sdk/metrics/meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace metrics

class MetricStorage;
class SyncWritableMetricStorage;
class SyncMultiMetricStorage;
class AsyncWritableMetricsStorge;
class ObservableRegistry;

Expand Down Expand Up @@ -169,8 +170,13 @@ class Meter final : public opentelemetry::metrics::Meter
std::shared_ptr<ObservableRegistry> observable_registry_;
// Shared with this meter's instruments so updates reach them; atomic so recording never blocks.
std::shared_ptr<MeterEnabledState> meter_enabled_state_{new MeterEnabledState()};
std::unique_ptr<SyncWritableMetricStorage> RegisterSyncMetricStorage(
// Shared with instruments whose matching views all use the Drop aggregation. Never enabled.
std::shared_ptr<MeterEnabledState> always_disabled_state_{new MeterEnabledState(false)};
std::unique_ptr<SyncMultiMetricStorage> RegisterSyncMetricStorage(
InstrumentDescriptor &instrument_descriptor);
/** Returns the enabled state an instrument with this storage should share. */
std::shared_ptr<MeterEnabledState> GetInstrumentEnabledState(
const SyncMultiMetricStorage *storage) const noexcept;
std::unique_ptr<AsyncWritableMetricStorage> RegisterAsyncMetricStorage(
InstrumentDescriptor &instrument_descriptor);
std::mutex storage_lock_;
Expand Down
26 changes: 20 additions & 6 deletions sdk/include/opentelemetry/sdk/metrics/meter_enabled_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

#pragma once

#include <atomic>

#include "opentelemetry/common/macros.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand All @@ -16,24 +15,39 @@ namespace metrics
/**
* A Meter's enabled flag, shared with every instrument it creates so a configurator update
* reaches instruments that already exist. Relaxed ordering: no dependent data, only visibility.
* A plain bool, not std::atomic, because API instruments read it through Flag().
*/
class MeterEnabledState
{
public:
explicit MeterEnabledState(bool enabled = true) noexcept : enabled_(enabled) {}
explicit MeterEnabledState(bool enabled = true) noexcept { flag_.enabled = enabled; }

~MeterEnabledState() = default;
MeterEnabledState(const MeterEnabledState &) = delete;
MeterEnabledState(MeterEnabledState &&) = delete;
MeterEnabledState &operator=(const MeterEnabledState &) = delete;
MeterEnabledState &operator=(MeterEnabledState &&) = delete;

bool IsEnabled() const noexcept { return enabled_.load(std::memory_order_relaxed); }
bool IsEnabled() const noexcept
{
return OPENTELEMETRY_ATOMIC_READ_RELAXED_8(&flag_.enabled) != 0;
}

void SetEnabled(bool enabled) noexcept { OPENTELEMETRY_ATOMIC_WRITE_8(&flag_.enabled, enabled); }

void SetEnabled(bool enabled) noexcept { enabled_.store(enabled, std::memory_order_relaxed); }
/** The flag itself, valid for the lifetime of this object. */
const bool *Flag() const noexcept { return &flag_.enabled; }

private:
std::atomic<bool> enabled_;
// Padding keeps the flag off cache lines that neighbouring heap objects write to, since every
// instrument of the meter reads it on each Enabled() call.
struct PaddedFlag
{
char padding_before[64];
bool enabled;
char padding_after[63];
};
PaddedFlag flag_{};
};

} // namespace metrics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class SyncMultiMetricStorage : public SyncWritableMetricStorage
return true;
}

/** True when no storage was added, e.g. every matching view uses the Drop aggregation. */
bool Empty() const noexcept { return storages_.empty(); }

void RecordLong(int64_t value, const opentelemetry::context::Context &context) noexcept override
{
for (auto &s : storages_)
Expand Down
58 changes: 38 additions & 20 deletions sdk/src/metrics/meter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ void Meter::UpdateMeterConfig(MeterConfig config) noexcept
meter_enabled_state_->SetEnabled(config.IsEnabled());
}

std::shared_ptr<MeterEnabledState> Meter::GetInstrumentEnabledState(
const SyncMultiMetricStorage *storage) const noexcept
{
// Every matching view uses the Drop aggregation, so the instrument never records.
if (storage != nullptr && storage->Empty())
{
return always_disabled_state_;
}
return meter_enabled_state_;
}

opentelemetry::nostd::unique_ptr<metrics::Counter<uint64_t>> Meter::CreateUInt64Counter(
opentelemetry::nostd::string_view name,
opentelemetry::nostd::string_view description,
Expand All @@ -171,9 +182,10 @@ opentelemetry::nostd::unique_ptr<metrics::Counter<uint64_t>> Meter::CreateUInt64
InstrumentDescriptor instrument_descriptor = {
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kCounter, InstrumentValueType::kLong};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Counter<uint64_t>>(
new LongCounter(instrument_descriptor, std::move(storage), meter_enabled_state_));
new LongCounter(instrument_descriptor, std::move(storage), std::move(enabled_state)));
}

opentelemetry::nostd::unique_ptr<metrics::Counter<double>> Meter::CreateDoubleCounter(
Expand All @@ -193,9 +205,10 @@ opentelemetry::nostd::unique_ptr<metrics::Counter<double>> Meter::CreateDoubleCo
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kCounter,
InstrumentValueType::kDouble};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Counter<double>>{
new DoubleCounter(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new DoubleCounter(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

opentelemetry::nostd::shared_ptr<opentelemetry::metrics::ObservableInstrument>
Expand Down Expand Up @@ -257,9 +270,10 @@ opentelemetry::nostd::unique_ptr<metrics::Histogram<uint64_t>> Meter::CreateUInt
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kHistogram,
InstrumentValueType::kLong};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Histogram<uint64_t>>{
new LongHistogram(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new LongHistogram(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

opentelemetry::nostd::unique_ptr<metrics::Histogram<double>> Meter::CreateDoubleHistogram(
Expand All @@ -279,9 +293,10 @@ opentelemetry::nostd::unique_ptr<metrics::Histogram<double>> Meter::CreateDouble
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kHistogram,
InstrumentValueType::kDouble};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Histogram<double>>{
new DoubleHistogram(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new DoubleHistogram(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

#if OPENTELEMETRY_ABI_VERSION_NO >= 2
Expand All @@ -301,9 +316,10 @@ opentelemetry::nostd::unique_ptr<metrics::Gauge<int64_t>> Meter::CreateInt64Gaug
InstrumentDescriptor instrument_descriptor = {
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kGauge, InstrumentValueType::kLong};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Gauge<int64_t>>{
new LongGauge(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new LongGauge(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

opentelemetry::nostd::unique_ptr<metrics::Gauge<double>> Meter::CreateDoubleGauge(
Expand All @@ -322,9 +338,10 @@ opentelemetry::nostd::unique_ptr<metrics::Gauge<double>> Meter::CreateDoubleGaug
InstrumentDescriptor instrument_descriptor = {
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kGauge, InstrumentValueType::kDouble};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::Gauge<double>>{
new DoubleGauge(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new DoubleGauge(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}
#endif

Expand Down Expand Up @@ -387,9 +404,10 @@ opentelemetry::nostd::unique_ptr<metrics::UpDownCounter<int64_t>> Meter::CreateI
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter,
InstrumentValueType::kLong};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::UpDownCounter<int64_t>>{
new LongUpDownCounter(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new LongUpDownCounter(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

opentelemetry::nostd::unique_ptr<metrics::UpDownCounter<double>> Meter::CreateDoubleUpDownCounter(
Expand All @@ -409,9 +427,10 @@ opentelemetry::nostd::unique_ptr<metrics::UpDownCounter<double>> Meter::CreateDo
std::string{name.data(), name.size()}, std::string{description.data(), description.size()},
std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter,
InstrumentValueType::kDouble};
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto storage = RegisterSyncMetricStorage(instrument_descriptor);
auto enabled_state = GetInstrumentEnabledState(storage.get());
return opentelemetry::nostd::unique_ptr<metrics::UpDownCounter<double>>{
new DoubleUpDownCounter(instrument_descriptor, std::move(storage), meter_enabled_state_)};
new DoubleUpDownCounter(instrument_descriptor, std::move(storage), std::move(enabled_state))};
}

opentelemetry::nostd::shared_ptr<opentelemetry::metrics::ObservableInstrument>
Expand Down Expand Up @@ -462,7 +481,7 @@ const sdk::instrumentationscope::InstrumentationScope *Meter::GetInstrumentation
return scope_.get();
}

std::unique_ptr<SyncWritableMetricStorage> Meter::RegisterSyncMetricStorage(
std::unique_ptr<SyncMultiMetricStorage> Meter::RegisterSyncMetricStorage(
InstrumentDescriptor &instrument_descriptor)
{
std::lock_guard<std::mutex> guard(storage_lock_);
Expand All @@ -476,7 +495,7 @@ std::unique_ptr<SyncWritableMetricStorage> Meter::RegisterSyncMetricStorage(
}

auto view_registry = ctx->GetViewRegistry();
std::unique_ptr<SyncWritableMetricStorage> storages(new SyncMultiMetricStorage());
std::unique_ptr<SyncMultiMetricStorage> storages(new SyncMultiMetricStorage());

#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
auto exemplar_filter_type = ctx->GetExemplarFilter();
Expand Down Expand Up @@ -533,8 +552,7 @@ std::unique_ptr<SyncWritableMetricStorage> Meter::RegisterSyncMetricStorage(
view.GetAggregationConfig()));
storage_registry_.insert({view_instr_desc, sync_storage});
}
auto sync_multi_storage = static_cast<SyncMultiMetricStorage *>(storages.get());
if (!sync_multi_storage->AddStorage(sync_storage))
if (!storages->AddStorage(sync_storage))
{
WarnOnViewSemanticError(GetInstrumentationScope(), instrument_descriptor, view_instr_desc,
view);
Expand Down
Loading
Loading