Skip to content

Commit 1fc14cf

Browse files
committed
Add preview bound sync metric instruments
1 parent 99300d1 commit 1fc14cf

16 files changed

Lines changed: 1747 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ endif()
6060
option(WITH_ABI_VERSION_1 "ABI version 1" ON)
6161
option(WITH_ABI_VERSION_2 "EXPERIMENTAL: ABI version 2 preview" OFF)
6262

63+
# Experimental: bound synchronous metric instruments (Counter, Histogram).
64+
# Requires WITH_ABI_VERSION_2. Context-bearing bound operations, exemplar
65+
# parity, Gauge, and UpDownCounter bound support are follow-ups.
66+
option(WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW
67+
"EXPERIMENTAL: bound synchronous metric instruments preview" OFF)
68+
6369
option(WITH_CONFIGURATION "EXPERIMENTAL: YAML configuration file" OFF)
6470

6571
#
@@ -94,6 +100,12 @@ else()
94100
set(OPENTELEMETRY_ABI_VERSION_NO "${OPENTELEMETRY_ABI_VERSION_DEFAULT}")
95101
endif()
96102

103+
if(WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW AND NOT WITH_ABI_VERSION_2)
104+
message(
105+
FATAL_ERROR
106+
"WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW requires WITH_ABI_VERSION_2")
107+
endif()
108+
97109
option(WITH_NO_DEPRECATED_CODE "Do not include deprecated code" OFF)
98110

99111
set(WITH_STL
@@ -483,6 +495,10 @@ message(STATUS "WITH_API_ONLY: ${WITH_API_ONLY}")
483495
message(STATUS "WITH_NO_DEPRECATED_CODE: ${WITH_NO_DEPRECATED_CODE}")
484496
message(STATUS "WITH_ABI_VERSION_1: ${WITH_ABI_VERSION_1}")
485497
message(STATUS "WITH_ABI_VERSION_2: ${WITH_ABI_VERSION_2}")
498+
message(
499+
STATUS
500+
"WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW: ${WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW}"
501+
)
486502
message(STATUS "OTELCPP_VERSIONED_LIBS: ${OTELCPP_VERSIONED_LIBS}")
487503
message(STATUS "OTELCPP_MAINTAINER_MODE: ${OTELCPP_MAINTAINER_MODE}")
488504
message(STATUS "WITH_STL: ${WITH_STL}")

api/BUILD

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ cc_library(
3939
}) + select({
4040
":abi_version_no_1": ["OPENTELEMETRY_ABI_VERSION_NO=1"],
4141
":abi_version_no_2": ["OPENTELEMETRY_ABI_VERSION_NO=2"],
42+
}) + select({
43+
# Experimental: bound synchronous metric instruments preview.
44+
# NOTE: this define only takes effect when ABI v2 is also enabled
45+
# (`--//api:abi_version_no=2`); see api/include/opentelemetry/version.h
46+
# which gates OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW on
47+
# both ABI v2 and ENABLE_METRICS_BOUND_INSTRUMENTS_PREVIEW.
48+
":metrics_bound_instruments_preview_enabled": ["ENABLE_METRICS_BOUND_INSTRUMENTS_PREVIEW"],
49+
"//conditions:default": [],
4250
}),
4351
strip_include_prefix = "include",
4452
tags = ["api"],
@@ -80,3 +88,15 @@ config_setting(
8088
name = "abi_version_no_2",
8189
flag_values = {":abi_version_no": "2"},
8290
)
91+
92+
# Experimental: bound synchronous metric instruments preview.
93+
# Only effective with `--//api:abi_version_no=2`.
94+
bool_flag(
95+
name = "with_metrics_bound_instruments_preview",
96+
build_setting_default = False,
97+
)
98+
99+
config_setting(
100+
name = "metrics_bound_instruments_preview_enabled",
101+
flag_values = {":with_metrics_bound_instruments_preview": "true"},
102+
)

api/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ target_compile_definitions(
8181
opentelemetry_api
8282
INTERFACE OPENTELEMETRY_ABI_VERSION_NO=${OPENTELEMETRY_ABI_VERSION_NO})
8383

84+
if(WITH_METRICS_BOUND_INSTRUMENTS_PREVIEW)
85+
target_compile_definitions(opentelemetry_api
86+
INTERFACE ENABLE_METRICS_BOUND_INSTRUMENTS_PREVIEW)
87+
endif()
88+
8489
if(WITH_OTLP_RETRY_PREVIEW)
8590
target_compile_definitions(opentelemetry_api
8691
INTERFACE ENABLE_OTLP_RETRY_PREVIEW)

api/include/opentelemetry/metrics/noop.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ OPENTELEMETRY_BEGIN_NAMESPACE
1414
namespace metrics
1515
{
1616

17+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
18+
template <class T>
19+
class NoopBoundCounter : public BoundCounter<T>
20+
{
21+
public:
22+
NoopBoundCounter() noexcept = default;
23+
void Add(T /* value */) noexcept override {}
24+
};
25+
26+
template <class T>
27+
class NoopBoundHistogram : public BoundHistogram<T>
28+
{
29+
public:
30+
NoopBoundHistogram() noexcept = default;
31+
void Record(T /* value */) noexcept override {}
32+
};
33+
#endif
34+
1735
template <class T>
1836
class NoopCounter : public Counter<T>
1937
{
@@ -29,6 +47,13 @@ class NoopCounter : public Counter<T>
2947
const common::KeyValueIterable & /* attributes */,
3048
const context::Context & /* context */) noexcept override
3149
{}
50+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
51+
nostd::unique_ptr<BoundCounter<T>> Bind(
52+
const common::KeyValueIterable & /* attributes */) noexcept override
53+
{
54+
return nostd::unique_ptr<BoundCounter<T>>{new NoopBoundCounter<T>()};
55+
}
56+
#endif
3257
};
3358

3459
template <class T>
@@ -51,6 +76,14 @@ class NoopHistogram : public Histogram<T>
5176

5277
void Record(T /*value*/) noexcept override {}
5378
#endif
79+
80+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
81+
nostd::unique_ptr<BoundHistogram<T>> Bind(
82+
const common::KeyValueIterable & /* attributes */) noexcept override
83+
{
84+
return nostd::unique_ptr<BoundHistogram<T>>{new NoopBoundHistogram<T>()};
85+
}
86+
#endif
5487
};
5588

5689
template <class T>

api/include/opentelemetry/metrics/sync_instruments.h

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
#include "opentelemetry/nostd/type_traits.h"
1212
#include "opentelemetry/version.h"
1313

14+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
15+
# include "opentelemetry/nostd/unique_ptr.h"
16+
#endif
17+
1418
OPENTELEMETRY_BEGIN_NAMESPACE
1519
namespace metrics
1620
{
@@ -26,6 +30,62 @@ class SynchronousInstrument
2630
virtual ~SynchronousInstrument() = default;
2731
};
2832

33+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
34+
// Bound synchronous instrument support intentionally covers Counter and
35+
// Histogram only. UpDownCounter, Gauge, exemplar parity, and context-bearing
36+
// bound operations are follow-ups. This API is experimental and is gated
37+
// behind both ABI v2 and ENABLE_METRICS_BOUND_INSTRUMENTS_PREVIEW
38+
// (see OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW in version.h).
39+
/**
40+
* @since ABI_VERSION 2
41+
* A bound counter handle obtained via Counter<T>::Bind(...). The associated
42+
* attribute set is captured at Bind time so the hot path avoids per-call
43+
* attribute processing and hashmap lookup. The handle must not outlive the
44+
* Counter instrument from which it was obtained.
45+
*/
46+
template <class T>
47+
class BoundCounter
48+
{
49+
public:
50+
BoundCounter() = default;
51+
BoundCounter(const BoundCounter &) = delete;
52+
BoundCounter(BoundCounter &&) noexcept = delete;
53+
BoundCounter &operator=(const BoundCounter &) = delete;
54+
BoundCounter &operator=(BoundCounter &&) noexcept = delete;
55+
virtual ~BoundCounter() = default;
56+
57+
/**
58+
* Record a value against the bound attribute set.
59+
*
60+
* @param value The increment amount. MUST be non-negative.
61+
*/
62+
virtual void Add(T value) noexcept = 0;
63+
};
64+
65+
/**
66+
* @since ABI_VERSION 2
67+
* A bound histogram handle obtained via Histogram<T>::Bind(...).
68+
*/
69+
template <class T>
70+
class BoundHistogram
71+
{
72+
public:
73+
BoundHistogram() = default;
74+
BoundHistogram(const BoundHistogram &) = delete;
75+
BoundHistogram(BoundHistogram &&) noexcept = delete;
76+
BoundHistogram &operator=(const BoundHistogram &) = delete;
77+
BoundHistogram &operator=(BoundHistogram &&) noexcept = delete;
78+
virtual ~BoundHistogram() = default;
79+
80+
/**
81+
* Record a value against the bound attribute set.
82+
*
83+
* @param value The measurement value. MUST be non-negative.
84+
*/
85+
virtual void Record(T value) noexcept = 0;
86+
};
87+
#endif
88+
2989
/* A Counter instrument that adds values. */
3090
template <class T>
3191
class Counter : public SynchronousInstrument
@@ -98,6 +158,32 @@ class Counter : public SynchronousInstrument
98158
attributes.begin(), attributes.end()},
99159
context);
100160
}
161+
162+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
163+
/**
164+
* @since ABI_VERSION 2
165+
* Returns a bound counter handle for the given attribute set. Repeated calls
166+
* to BoundCounter<T>::Add(value) avoid per-call attribute processing and
167+
* hashmap lookup. The bound handle MUST NOT outlive this Counter instrument.
168+
*/
169+
virtual nostd::unique_ptr<BoundCounter<T>> Bind(
170+
const common::KeyValueIterable &attributes) noexcept = 0;
171+
172+
template <class U,
173+
nostd::enable_if_t<common::detail::is_key_value_iterable<U>::value> * = nullptr>
174+
nostd::unique_ptr<BoundCounter<T>> Bind(const U &attributes) noexcept
175+
{
176+
return this->Bind(common::KeyValueIterableView<U>{attributes});
177+
}
178+
179+
nostd::unique_ptr<BoundCounter<T>> Bind(
180+
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>
181+
attributes) noexcept
182+
{
183+
return this->Bind(nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{
184+
attributes.begin(), attributes.end()});
185+
}
186+
#endif
101187
};
102188

103189
/** A histogram instrument that records values. */
@@ -140,6 +226,33 @@ class Histogram : public SynchronousInstrument
140226
}
141227
#endif
142228

229+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
230+
/**
231+
* @since ABI_VERSION 2
232+
* Returns a bound histogram handle for the given attribute set. Repeated
233+
* calls to BoundHistogram<T>::Record(value) avoid per-call attribute
234+
* processing and hashmap lookup. The bound handle MUST NOT outlive this
235+
* Histogram instrument.
236+
*/
237+
virtual nostd::unique_ptr<BoundHistogram<T>> Bind(
238+
const common::KeyValueIterable &attributes) noexcept = 0;
239+
240+
template <class U,
241+
nostd::enable_if_t<common::detail::is_key_value_iterable<U>::value> * = nullptr>
242+
nostd::unique_ptr<BoundHistogram<T>> Bind(const U &attributes) noexcept
243+
{
244+
return this->Bind(common::KeyValueIterableView<U>{attributes});
245+
}
246+
247+
nostd::unique_ptr<BoundHistogram<T>> Bind(
248+
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>
249+
attributes) noexcept
250+
{
251+
return this->Bind(nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{
252+
attributes.begin(), attributes.end()});
253+
}
254+
#endif
255+
143256
/**
144257
* Records a value.
145258
*

api/include/opentelemetry/version.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,12 @@
3030
#define OPENTELEMETRY_NAMESPACE opentelemetry :: OPENTELEMETRY_CONCAT(v, OPENTELEMETRY_ABI_VERSION_NO)
3131

3232
// clang-format on
33+
34+
// Experimental: bound synchronous metric instruments (Counter, Histogram).
35+
// Enabled when both ABI v2 and the preview flag are set. Guard all bound
36+
// instrument code with `#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW`.
37+
// This macro affects SDK class layout/vtables and MUST match between the SDK
38+
// library build and consumer translation units.
39+
#if OPENTELEMETRY_ABI_VERSION_NO >= 2 && defined(ENABLE_METRICS_BOUND_INSTRUMENTS_PREVIEW)
40+
# define OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW 1
41+
#endif

sdk/include/opentelemetry/sdk/metrics/state/metric_storage.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@ namespace metrics
2929
/* Represent the storage from which to collect the metrics */
3030
class CollectorHandle;
3131

32+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
33+
/**
34+
* @since ABI_VERSION 2
35+
* Storage-side interface for a bound sync metric handle. Created by
36+
* SyncWritableMetricStorage::Bind(...). The hot path RecordLong/RecordDouble
37+
* skips per-call attribute filtering and hashmap lookup.
38+
*/
39+
class BoundSyncWritableMetricStorage
40+
{
41+
public:
42+
BoundSyncWritableMetricStorage() = default;
43+
BoundSyncWritableMetricStorage(const BoundSyncWritableMetricStorage &) = delete;
44+
BoundSyncWritableMetricStorage(BoundSyncWritableMetricStorage &&) = delete;
45+
BoundSyncWritableMetricStorage &operator=(const BoundSyncWritableMetricStorage &) = delete;
46+
BoundSyncWritableMetricStorage &operator=(BoundSyncWritableMetricStorage &&) = delete;
47+
virtual ~BoundSyncWritableMetricStorage() = default;
48+
49+
virtual void RecordLong(int64_t value) noexcept = 0;
50+
virtual void RecordDouble(double value) noexcept = 0;
51+
};
52+
#endif
53+
3254
class MetricStorage
3355
{
3456
public:
@@ -75,6 +97,19 @@ class SyncWritableMetricStorage
7597
virtual void RecordDouble(double value,
7698
const opentelemetry::common::KeyValueIterable &attributes,
7799
const opentelemetry::context::Context &context) noexcept = 0;
100+
101+
#ifdef OPENTELEMETRY_HAVE_METRICS_BOUND_INSTRUMENTS_PREVIEW
102+
/**
103+
* @since ABI_VERSION 2
104+
* Returns a bound storage handle for the given attribute set, or nullptr if
105+
* the storage does not support binding. Default returns nullptr.
106+
*/
107+
virtual std::shared_ptr<BoundSyncWritableMetricStorage> Bind(
108+
const opentelemetry::common::KeyValueIterable & /* attributes */) noexcept
109+
{
110+
return nullptr;
111+
}
112+
#endif
78113
};
79114

80115
/* Represents the async metric stroage */

0 commit comments

Comments
 (0)