From f087d9004c9447c53d94cee3b33335d90df259f5 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:03:17 -0500 Subject: [PATCH 1/9] Add bound instruments to metrics API spec --- specification/metrics/api.md | 88 ++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/specification/metrics/api.md b/specification/metrics/api.md index c37fccc944d..d016339981b 100644 --- a/specification/metrics/api.md +++ b/specification/metrics/api.md @@ -31,10 +31,12 @@ weight: 1 - [Asynchronous Instrument API](#asynchronous-instrument-api) * [General operations](#general-operations) + [Enabled](#enabled) + + [Bind](#bind) * [Counter](#counter) + [Counter creation](#counter-creation) + [Counter operations](#counter-operations) - [Add](#add) + - [Bound Add](#bound-add) * [Asynchronous Counter](#asynchronous-counter) + [Asynchronous Counter creation](#asynchronous-counter-creation) + [Asynchronous Counter operations](#asynchronous-counter-operations) @@ -42,10 +44,12 @@ weight: 1 + [Histogram creation](#histogram-creation) + [Histogram operations](#histogram-operations) - [Record](#record) + - [Bound Record](#bound-record) * [Gauge](#gauge) + [Gauge creation](#gauge-creation) + [Gauge operations](#gauge-operations) - [Record](#record-1) + - [Bound Record](#bound-record-1) * [Asynchronous Gauge](#asynchronous-gauge) + [Asynchronous Gauge creation](#asynchronous-gauge-creation) + [Asynchronous Gauge operations](#asynchronous-gauge-operations) @@ -53,6 +57,7 @@ weight: 1 + [UpDownCounter creation](#updowncounter-creation) + [UpDownCounter operations](#updowncounter-operations) - [Add](#add-1) + - [Bound Add](#bound-add-1) * [Asynchronous UpDownCounter](#asynchronous-updowncounter) + [Asynchronous UpDownCounter creation](#asynchronous-updowncounter-creation) + [Asynchronous UpDownCounter operations](#asynchronous-updowncounter-operations) @@ -475,6 +480,7 @@ or something else). All [synchronous instruments](#synchronous-instrument-api) SHOULD provide functions to: * [Report if instrument is `Enabled`](#enabled) +* [Bind to a set of attributes](#bind) #### Enabled @@ -494,6 +500,56 @@ The returned value is not always static, it can change over time. The API SHOULD be documented that instrumentation authors needs to call this API each time they record a measurement to ensure they have the most up-to-date response. +#### Bind + +**Status**: [Development](../document-status.md) + +The `Bind` API associates a fixed set of [Attributes](../common/README.md#attribute) with every +measurement recorded on the returned bound instrument. Because attributes are resolved at bind +time rather than at each recording, implementations can avoid per-recording attribute processing +and lookup overhead. + +This API MUST accept the following parameter: + +* [Attributes](../common/README.md#attribute) to associate with every measurement recorded on + the returned bound instrument. + + Users can provide attributes to associate with the bound instrument, but it is up + to their discretion. Therefore, this API MUST be structured to accept a variable + number of attributes, including none. + +This API MUST return a language-idiomatic type representing the instrument bound to those +attributes. + +The returned bound instrument MUST support the instrument's core recording operation without +accepting [Attributes](../common/README.md#attribute) as a parameter. The instrument kind +determines the recording operation on the bound instrument: + +* [Counter](#counter): [Bound Add](#bound-add) +* [Histogram](#histogram): [Bound Record](#bound-record) +* [Gauge](#gauge): [Bound Record](#bound-record-1) +* [UpDownCounter](#updowncounter): [Bound Add](#bound-add-1) + +Measurements recorded on the bound instrument can be associated with the +[Context](../context/README.md). + +Here are some examples that [OpenTelemetry API](../overview.md#api) authors might consider: + +```java +// Java + +LongCounter rolls = meter.counterBuilder("dice.rolls") + .setDescription("The number of times each side of the die was rolled") + .setUnit("{roll}") + .build(); + +var face1 = rolls.bind(Attributes.of(AttributeKey.longKey("roll.value"), 1L)); +var face6 = rolls.bind(Attributes.of(AttributeKey.longKey("roll.value"), 6L)); + +face1.add(1); +face6.add(1); +``` + ### Counter `Counter` is a [synchronous Instrument](#synchronous-instrument-api) which supports @@ -596,6 +652,14 @@ counterPowerUsed.Add(13.5, new PowerConsumption { customer = "Tom" }); counterPowerUsed.Add(200, new PowerConsumption { customer = "Jerry" }, ("is_green_energy", true)); ``` +##### Bound Add + +**Status**: [Development](../document-status.md) + +The bound counterpart to [Add](#add), obtained via [Bind](#bind). All semantics are +identical to [Add](#add) except [Attributes](../common/README.md#attribute) are not +accepted as a parameter. + ### Asynchronous Counter Asynchronous Counter is an [asynchronous Instrument](#asynchronous-instrument-api) @@ -825,6 +889,14 @@ httpServerDuration.Record(50, ("http.request.method", "POST"), ("url.scheme", "h httpServerDuration.Record(100, new HttpRequestAttributes { method = "GET", scheme = "http" }); ``` +##### Bound Record + +**Status**: [Development](../document-status.md) + +The bound counterpart to [Record](#record), obtained via [Bind](#bind). All semantics are +identical to [Record](#record) except [Attributes](../common/README.md#attribute) are not +accepted as a parameter. + ### Gauge `Gauge` is a [synchronous Instrument](#synchronous-instrument-api) which can be @@ -914,6 +986,14 @@ backgroundNoiseLevel.record(4.3, roomA); backgroundNoiseLevel.record(2.5, roomB); ``` +##### Bound Record + +**Status**: [Development](../document-status.md) + +The bound counterpart to [Record](#record-1), obtained via [Bind](#bind). All semantics are +identical to [Record](#record-1) except [Attributes](../common/README.md#attribute) are not +accepted as a parameter. + ### Asynchronous Gauge Asynchronous Gauge is an [asynchronous Instrument](#asynchronous-instrument-api) @@ -1155,6 +1235,14 @@ customersInStore.Add(1, ("account.type", "commercial")); customersInStore.Add(-1, new Account { Type = "residential" }); ``` +##### Bound Add + +**Status**: [Development](../document-status.md) + +The bound counterpart to [Add](#add-1), obtained via [Bind](#bind). All semantics are +identical to [Add](#add-1) except [Attributes](../common/README.md#attribute) are not +accepted as a parameter. + ### Asynchronous UpDownCounter Asynchronous UpDownCounter is an [asynchronous From 2549a42d993f07dd095d1fae40a4bf37978ddbac Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:16:46 -0500 Subject: [PATCH 2/9] Update sdk for bound instruments --- specification/metrics/api.md | 3 +++ specification/metrics/sdk.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/specification/metrics/api.md b/specification/metrics/api.md index d016339981b..c0aedb9da4d 100644 --- a/specification/metrics/api.md +++ b/specification/metrics/api.md @@ -480,6 +480,9 @@ or something else). All [synchronous instruments](#synchronous-instrument-api) SHOULD provide functions to: * [Report if instrument is `Enabled`](#enabled) + +All [synchronous instruments](#synchronous-instrument-api) MAY provide: + * [Bind to a set of attributes](#bind) #### Enabled diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index f5586f677a1..ccaed229fa6 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -54,6 +54,7 @@ weight: 3 + [Instrument advisory parameter: `ExplicitBucketBoundaries`](#instrument-advisory-parameter-explicitbucketboundaries) + [Instrument advisory parameter: `Attributes`](#instrument-advisory-parameter-attributes) * [Instrument enabled](#instrument-enabled) + * [Instrument bind](#instrument-bind) - [Attribute limits](#attribute-limits) - [Exemplar](#exemplar) * [ExemplarFilter](#exemplarfilter) @@ -1041,6 +1042,20 @@ Note: If a user makes no configuration changes, `Enabled` returns `true` since b default `MeterConfig.enabled=true` and instruments use the default aggregation when no matching views match the instrument. +### Instrument bind + +**Status**: [Development](../document-status.md) + +The SDK MAY implement the [Bind](./api.md#bind) API for synchronous instruments. + +A bound instrument MUST behave identically to calling the equivalent unbound recording +operation with the pre-bound [Attributes](../common/README.md#attribute) on each +measurement. + +The SDK SHOULD optimize by pre-resolving the underlying aggregator state at bind time, +such that subsequent recordings bypass per-recording attribute processing and map +lookup. + ## Attribute limits **Status**: [Stable](../document-status.md) From bb0478338e1befdea8c41a67940b9d5576b1ff99 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:26:10 -0500 Subject: [PATCH 3/9] Add spec compliance, changelog entry --- CHANGELOG.md | 2 ++ spec-compliance-matrix.md | 1 + spec-compliance-matrix/cpp.yaml | 2 ++ spec-compliance-matrix/dotnet.yaml | 2 ++ spec-compliance-matrix/erlang.yaml | 2 ++ spec-compliance-matrix/go.yaml | 2 ++ spec-compliance-matrix/java.yaml | 2 ++ spec-compliance-matrix/js.yaml | 2 ++ spec-compliance-matrix/kotlin.yaml | 2 ++ spec-compliance-matrix/php.yaml | 2 ++ spec-compliance-matrix/python.yaml | 2 ++ spec-compliance-matrix/ruby.yaml | 2 ++ spec-compliance-matrix/rust.yaml | 2 ++ spec-compliance-matrix/swift.yaml | 2 ++ spec-compliance-matrix/template.yaml | 2 ++ 15 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348aa2d6d5b..ee67b78c474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ release. ### Metrics +- Add in-development `Bind` API to synchronous instruments. + ([#XXXX](https://github.com/open-telemetry/opentelemetry-specification/pull/XXXX)) - Stabilize sections of Prometheus Metrics Exporter. - Stabilize temporality. ([#5024](https://github.com/open-telemetry/opentelemetry-specification/issues/5024)) diff --git a/spec-compliance-matrix.md b/spec-compliance-matrix.md index 532c53705a0..9ac5f58aad9 100644 --- a/spec-compliance-matrix.md +++ b/spec-compliance-matrix.md @@ -130,6 +130,7 @@ formats is required. Implementing more than one format is optional. | Instrument descriptions conform to the specified syntax. | | - | + | | - | + | + | | | - | + | | - | | Instrument supports the advisory ExplicitBucketBoundaries parameter. | | + | + | | | | + | | | | | | - | | Instrument supports the advisory Attributes parameter. | | - | + | | | | + | | | | | | - | +| Synchronous instruments support Bind to pre-associate attributes. | X | - | - | - | - | - | - | - | - | - | - | - | - | | All methods of `MeterProvider` are safe to be called concurrently. | | + | + | + | - | | + | | | + | + | | - | | All methods of `Meter` are safe to be called concurrently. | | + | + | + | - | | + | | | + | + | | - | | All methods of any instrument are safe to be called concurrently. | | + | + | + | - | | + | | | + | + | | - | diff --git a/spec-compliance-matrix/cpp.yaml b/spec-compliance-matrix/cpp.yaml index 81d935efb33..73b89390e79 100644 --- a/spec-compliance-matrix/cpp.yaml +++ b/spec-compliance-matrix/cpp.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/dotnet.yaml b/spec-compliance-matrix/dotnet.yaml index 87c7e13e0c0..b8716c6739a 100644 --- a/spec-compliance-matrix/dotnet.yaml +++ b/spec-compliance-matrix/dotnet.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/erlang.yaml b/spec-compliance-matrix/erlang.yaml index 63562233105..fb0618829a5 100644 --- a/spec-compliance-matrix/erlang.yaml +++ b/spec-compliance-matrix/erlang.yaml @@ -223,6 +223,8 @@ sections: status: '+' - name: Instrument supports the advisory Attributes parameter. status: '+' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/go.yaml b/spec-compliance-matrix/go.yaml index f8226c23340..fe7ae165f7f 100644 --- a/spec-compliance-matrix/go.yaml +++ b/spec-compliance-matrix/go.yaml @@ -223,6 +223,8 @@ sections: status: '+' - name: Instrument supports the advisory Attributes parameter. status: '-' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/java.yaml b/spec-compliance-matrix/java.yaml index 97d5f019df1..52f87f4f4ea 100644 --- a/spec-compliance-matrix/java.yaml +++ b/spec-compliance-matrix/java.yaml @@ -223,6 +223,8 @@ sections: status: '+' - name: Instrument supports the advisory Attributes parameter. status: '+' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/js.yaml b/spec-compliance-matrix/js.yaml index e251573e343..5c8a3086bca 100644 --- a/spec-compliance-matrix/js.yaml +++ b/spec-compliance-matrix/js.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '+' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/kotlin.yaml b/spec-compliance-matrix/kotlin.yaml index 85497e43b36..e09b598731c 100644 --- a/spec-compliance-matrix/kotlin.yaml +++ b/spec-compliance-matrix/kotlin.yaml @@ -223,6 +223,8 @@ sections: status: '-' - name: Instrument supports the advisory Attributes parameter. status: '-' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '-' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/php.yaml b/spec-compliance-matrix/php.yaml index b0a56fd4ab2..59082762795 100644 --- a/spec-compliance-matrix/php.yaml +++ b/spec-compliance-matrix/php.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '?' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/python.yaml b/spec-compliance-matrix/python.yaml index 4678fb92b36..80cc750d8d7 100644 --- a/spec-compliance-matrix/python.yaml +++ b/spec-compliance-matrix/python.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '-' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/ruby.yaml b/spec-compliance-matrix/ruby.yaml index bdd8f7148a3..739005a0a2f 100644 --- a/spec-compliance-matrix/ruby.yaml +++ b/spec-compliance-matrix/ruby.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '?' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/rust.yaml b/spec-compliance-matrix/rust.yaml index 9772047cd7e..e5657f4eea7 100644 --- a/spec-compliance-matrix/rust.yaml +++ b/spec-compliance-matrix/rust.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '?' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/swift.yaml b/spec-compliance-matrix/swift.yaml index 5bbc00b322a..d8177d29fb7 100644 --- a/spec-compliance-matrix/swift.yaml +++ b/spec-compliance-matrix/swift.yaml @@ -223,6 +223,8 @@ sections: status: '?' - name: Instrument supports the advisory Attributes parameter. status: '?' + - name: Synchronous instruments support Bind to pre-associate attributes. + status: '-' - name: All methods of `MeterProvider` are safe to be called concurrently. status: '?' - name: All methods of `Meter` are safe to be called concurrently. diff --git a/spec-compliance-matrix/template.yaml b/spec-compliance-matrix/template.yaml index cdd24a84326..8db2a6c7e4c 100644 --- a/spec-compliance-matrix/template.yaml +++ b/spec-compliance-matrix/template.yaml @@ -157,6 +157,8 @@ sections: - name: Instrument descriptions conform to the specified syntax. - name: Instrument supports the advisory ExplicitBucketBoundaries parameter. - name: Instrument supports the advisory Attributes parameter. + - name: Synchronous instruments support Bind to pre-associate attributes. + optional: true - name: All methods of `MeterProvider` are safe to be called concurrently. - name: All methods of `Meter` are safe to be called concurrently. - name: All methods of any instrument are safe to be called concurrently. From a3c8d2a3191c0db59de8e81da1a17c27132d6c8f Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:32:35 -0500 Subject: [PATCH 4/9] Remove reundant MAY in instrument bind --- specification/metrics/sdk.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index ccaed229fa6..c8d54455e49 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -1046,8 +1046,6 @@ aggregation when no matching views match the instrument. **Status**: [Development](../document-status.md) -The SDK MAY implement the [Bind](./api.md#bind) API for synchronous instruments. - A bound instrument MUST behave identically to calling the equivalent unbound recording operation with the pre-bound [Attributes](../common/README.md#attribute) on each measurement. From b7b0bf77290fc5ac969c23fc8b21b592a44705d6 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:36:55 -0500 Subject: [PATCH 5/9] Qualify bullet as in development --- specification/metrics/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/metrics/api.md b/specification/metrics/api.md index c0aedb9da4d..60f50d2298f 100644 --- a/specification/metrics/api.md +++ b/specification/metrics/api.md @@ -483,7 +483,7 @@ All [synchronous instruments](#synchronous-instrument-api) SHOULD provide functi All [synchronous instruments](#synchronous-instrument-api) MAY provide: -* [Bind to a set of attributes](#bind) +* (**Development**) [Bind to a set of attributes](#bind) #### Enabled From 5f80bf36d547b433e6d0ea88ef907523ffc012c8 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:37:23 -0500 Subject: [PATCH 6/9] Update changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee67b78c474..dce1f9bc0e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ release. ### Metrics - Add in-development `Bind` API to synchronous instruments. - ([#XXXX](https://github.com/open-telemetry/opentelemetry-specification/pull/XXXX)) + ([#5050](https://github.com/open-telemetry/opentelemetry-specification/pull/5050)) - Stabilize sections of Prometheus Metrics Exporter. - Stabilize temporality. ([#5024](https://github.com/open-telemetry/opentelemetry-specification/issues/5024)) From 12b46b83ea7c1488516a6549a1d15cc36ee57ed6 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 1 May 2026 10:48:53 -0500 Subject: [PATCH 7/9] PR feedback --- specification/metrics/api.md | 55 ++++++++---------------------------- specification/metrics/sdk.md | 9 ++++-- 2 files changed, 18 insertions(+), 46 deletions(-) diff --git a/specification/metrics/api.md b/specification/metrics/api.md index 60f50d2298f..ba733a04c56 100644 --- a/specification/metrics/api.md +++ b/specification/metrics/api.md @@ -36,7 +36,6 @@ weight: 1 + [Counter creation](#counter-creation) + [Counter operations](#counter-operations) - [Add](#add) - - [Bound Add](#bound-add) * [Asynchronous Counter](#asynchronous-counter) + [Asynchronous Counter creation](#asynchronous-counter-creation) + [Asynchronous Counter operations](#asynchronous-counter-operations) @@ -44,12 +43,10 @@ weight: 1 + [Histogram creation](#histogram-creation) + [Histogram operations](#histogram-operations) - [Record](#record) - - [Bound Record](#bound-record) * [Gauge](#gauge) + [Gauge creation](#gauge-creation) + [Gauge operations](#gauge-operations) - [Record](#record-1) - - [Bound Record](#bound-record-1) * [Asynchronous Gauge](#asynchronous-gauge) + [Asynchronous Gauge creation](#asynchronous-gauge-creation) + [Asynchronous Gauge operations](#asynchronous-gauge-operations) @@ -57,7 +54,6 @@ weight: 1 + [UpDownCounter creation](#updowncounter-creation) + [UpDownCounter operations](#updowncounter-operations) - [Add](#add-1) - - [Bound Add](#bound-add-1) * [Asynchronous UpDownCounter](#asynchronous-updowncounter) + [Asynchronous UpDownCounter creation](#asynchronous-updowncounter-creation) + [Asynchronous UpDownCounter operations](#asynchronous-updowncounter-operations) @@ -524,14 +520,19 @@ This API MUST accept the following parameter: This API MUST return a language-idiomatic type representing the instrument bound to those attributes. -The returned bound instrument MUST support the instrument's core recording operation without -accepting [Attributes](../common/README.md#attribute) as a parameter. The instrument kind -determines the recording operation on the bound instrument: +The returned bound instrument MUST support the instrument's core recording operation. +The instrument kind determines the recording operation on the bound instrument: -* [Counter](#counter): [Bound Add](#bound-add) -* [Histogram](#histogram): [Bound Record](#bound-record) -* [Gauge](#gauge): [Bound Record](#bound-record-1) -* [UpDownCounter](#updowncounter): [Bound Add](#bound-add-1) +* [Counter](#counter): [Add](#add) +* [Histogram](#histogram): [Record](#record) +* [Gauge](#gauge): [Record](#record-1) +* [UpDownCounter](#updowncounter): [Add](#add-1) + +This MAY be achieved by introducing a dedicated bound instrument type, or by reusing +the existing instrument interface. If the existing instrument interface is reused, the +`Bind` API MUST be documented to communicate to users that invoking attribute-bearing +recording operations on the returned bound instrument negates the performance benefits +of binding. Measurements recorded on the bound instrument can be associated with the [Context](../context/README.md). @@ -655,14 +656,6 @@ counterPowerUsed.Add(13.5, new PowerConsumption { customer = "Tom" }); counterPowerUsed.Add(200, new PowerConsumption { customer = "Jerry" }, ("is_green_energy", true)); ``` -##### Bound Add - -**Status**: [Development](../document-status.md) - -The bound counterpart to [Add](#add), obtained via [Bind](#bind). All semantics are -identical to [Add](#add) except [Attributes](../common/README.md#attribute) are not -accepted as a parameter. - ### Asynchronous Counter Asynchronous Counter is an [asynchronous Instrument](#asynchronous-instrument-api) @@ -892,14 +885,6 @@ httpServerDuration.Record(50, ("http.request.method", "POST"), ("url.scheme", "h httpServerDuration.Record(100, new HttpRequestAttributes { method = "GET", scheme = "http" }); ``` -##### Bound Record - -**Status**: [Development](../document-status.md) - -The bound counterpart to [Record](#record), obtained via [Bind](#bind). All semantics are -identical to [Record](#record) except [Attributes](../common/README.md#attribute) are not -accepted as a parameter. - ### Gauge `Gauge` is a [synchronous Instrument](#synchronous-instrument-api) which can be @@ -989,14 +974,6 @@ backgroundNoiseLevel.record(4.3, roomA); backgroundNoiseLevel.record(2.5, roomB); ``` -##### Bound Record - -**Status**: [Development](../document-status.md) - -The bound counterpart to [Record](#record-1), obtained via [Bind](#bind). All semantics are -identical to [Record](#record-1) except [Attributes](../common/README.md#attribute) are not -accepted as a parameter. - ### Asynchronous Gauge Asynchronous Gauge is an [asynchronous Instrument](#asynchronous-instrument-api) @@ -1238,14 +1215,6 @@ customersInStore.Add(1, ("account.type", "commercial")); customersInStore.Add(-1, new Account { Type = "residential" }); ``` -##### Bound Add - -**Status**: [Development](../document-status.md) - -The bound counterpart to [Add](#add-1), obtained via [Bind](#bind). All semantics are -identical to [Add](#add-1) except [Attributes](../common/README.md#attribute) are not -accepted as a parameter. - ### Asynchronous UpDownCounter Asynchronous UpDownCounter is an [asynchronous diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index c8d54455e49..47a7e386cf1 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -1050,9 +1050,12 @@ A bound instrument MUST behave identically to calling the equivalent unbound rec operation with the pre-bound [Attributes](../common/README.md#attribute) on each measurement. -The SDK SHOULD optimize by pre-resolving the underlying aggregator state at bind time, -such that subsequent recordings bypass per-recording attribute processing and map -lookup. +[Attribute processing](#measurement-processing) and [cardinality limit](#cardinality-limits) +evaluation MUST be performed at bind time. The resolved aggregator is fixed for the +lifetime of the bound instrument and does not change across collection cycles. + +The SDK MUST ensure subsequent recordings on a bound instrument bypass per-recording +map lookup. ## Attribute limits From d86ce0dbc29d8895918a63f732bf7081e88a96ee Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 1 May 2026 11:18:55 -0500 Subject: [PATCH 8/9] Clarify map free lookup is only for attribute-free recordings --- specification/metrics/sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index 47a7e386cf1..56b3d640d3f 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -1054,7 +1054,7 @@ measurement. evaluation MUST be performed at bind time. The resolved aggregator is fixed for the lifetime of the bound instrument and does not change across collection cycles. -The SDK MUST ensure subsequent recordings on a bound instrument bypass per-recording +The SDK MUST ensure attribute-free recordings on a bound instrument bypass per-recording map lookup. ## Attribute limits From 70727d9008a0653eaf4ec265598873676e5f9243 Mon Sep 17 00:00:00 2001 From: Jack Berg <34418638+jack-berg@users.noreply.github.com> Date: Tue, 5 May 2026 12:58:49 -0500 Subject: [PATCH 9/9] Clarify exemplar behavior and behavior of multiple calls to bind --- specification/metrics/sdk.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/specification/metrics/sdk.md b/specification/metrics/sdk.md index ac49c9a01a4..5a82bb0cc02 100644 --- a/specification/metrics/sdk.md +++ b/specification/metrics/sdk.md @@ -1051,8 +1051,17 @@ operation with the pre-bound [Attributes](../common/README.md#attribute) on each measurement. [Attribute processing](#measurement-processing) and [cardinality limit](#cardinality-limits) -evaluation MUST be performed at bind time. The resolved aggregator is fixed for the -lifetime of the bound instrument and does not change across collection cycles. +evaluation MUST be performed at bind time. Each call to `Bind` MUST be independently +evaluated against the cardinality state at that moment. As a consequence, separate calls +to `Bind` with identical attributes may resolve to different aggregators (e.g. one to a +concrete series, another to the [overflow series](#overflow-attribute)) based on the +cardinality state at the time of each call. The resolved aggregator MUST be fixed and +not change across collection cycles. + +Measurements recorded on a bound instrument MUST be candidates for [Exemplar](#exemplar) +sampling. The [Context](../context/README.md) associated with each recording, whether +implicit or explicit, MUST be used for exemplar [TraceBased](#tracebased) filtering and +passed to the [ExemplarReservoir](#exemplarreservoir) offer method. The SDK MUST ensure attribute-free recordings on a bound instrument bypass per-recording map lookup.