From 21e4501053d9a14d765f9fc1c3f46f8d12b89dc4 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 31 Mar 2021 23:26:38 -0700 Subject: [PATCH 01/15] add observable counter --- specification/metrics/new_api.md | 89 +++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index dca6970d3fa..aa13b027aaa 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -29,7 +29,11 @@ Table of Contents * [Meter operations](#meter-operations) * [Instrument](#instrument) * [Counter](#counter) - * [Counter Creation](#counter-creation) + * [Counter creation](#counter-creation) + * [Counter operations](#counter-operations) + * [ObservableCounter](#observablecounter) + * [ObservableCounter creation](#observablecounter-creation) + * [ObservableCounter operations](#observablecounter-operations) * [Measurement](#measurement) @@ -227,7 +231,7 @@ Example uses for `Counter`: * count the number of checkpoints run * count the number of HTTP 5xx errors -#### Counter Creation +#### Counter creation There MUST NOT be any API for creating a `Counter` other than with a [`Meter`](#meter). This MAY be called `CreateCounter`. If strong type is @@ -308,6 +312,87 @@ counterPowerUsed.Add(13.5, new PowerConsumption { customer = "Tom" }); counterPowerUsed.Add(200, new PowerConsumption { customer = "Jerry" }, ("is_green_energy", true)); ``` +### ObservableCounter + +`ObservableCounter` is an asynchronous Instrument which reports +[monotonically](https://wikipedia.org/wiki/Monotonic_function) increasing +value(s) when the Meter is observed. + +Example uses for `ObservableCounter`: + +* [CPU time](https://wikipedia.org/wiki/CPU_time), which could be reported for + each thread, each process or the entire system. For example "the CPU time for + process A running in user mode, measured in seconds". +* The number of [page faults](https://wikipedia.org/wiki/Page_fault) for each + process. + +#### ObservableCounter creation + +There MUST NOT be any API for creating a `ObservableCounter` other than with a +[`Meter`](#meter). This MAY be called `CreateObservableCounter`. If strong type +is desired, the client can decide the language idomatic name(s), for example +`CreateUInt64ObservableCounter`, `CreateDoubleObservableCounter`, +`CreateObservableCounter`, `CreateObservableCounter`. + +The API MUST accept the following parameters: + +* The `name` of the Instrument, following the [instrument naming + rule](#instrument-naming-rule). +* An optional `unit of measure`, following the [instrument unit + rule](#instrument-unit). +* An optional `description`, following the [instrument description + rule](#instrument-description). +* An optional list of [`Attribute`](../common/common.md#attributes) names and + types. +* A `callback` function. + * This callback function will only be called when the Meter is being observed. + * This callback function will return the value(s) with optional + [`Attribute`](../common/common.md#attributes). Individual language client + can decide what is the idomatic approach (e.g. it could be a list, tuple, + generator, enumerator, etc.). + * Individual language client SHOULD define whether this callback function + needs to be reentrant safe / thread safe or not. +* An optional `state`. + * The `state` parameter can be used to hold any stateful information. + * If `state` is not provided, the `callback` function MUST take no input + argument, otherwise it MUST take the `state` as the only input argument. + +Here are some examples that individual language client might consider: + +```python +# Python + +def pf_callback(): + # Note: in the real world these would be retrieved from the operating system + return ( + (8, ("pid", 0), ("bitness", 64)), + (37741921, ("pid", 4), ("bitness", 64)), + (10465, ("pid", 880), ("bitness", 32)), + ) + +page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) +``` + +```csharp +// C# + +// A simple scenario where only one value is reported + +interface IAtomicClock +{ + UInt64 GetCaesiumOscillates(); +} + +IAtomicClock clock = AtomicClock.Connect(); + +var obCaesiumOscillates = meter.CreateCounterObserver("caesium_oscillates", clk => clk.GetCaesiumOscillates(), clock); +``` + +#### ObservableCounter operations + +`ObservableCounter` is only intended for asynchronous scenario, it does not +provide any operation. + ## Measurement A `Measurement` represents a data point reported via the metrics API to the SDK. From 1264dcbd15a45cc61b032d5a6a445d112d488e13 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 1 Apr 2021 13:00:55 -0700 Subject: [PATCH 02/15] change the wording to allow language client to decide how to handle callback state --- specification/metrics/new_api.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index aa13b027aaa..686c8e02332 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -352,10 +352,10 @@ The API MUST accept the following parameters: generator, enumerator, etc.). * Individual language client SHOULD define whether this callback function needs to be reentrant safe / thread safe or not. -* An optional `state`. - * The `state` parameter can be used to hold any stateful information. - * If `state` is not provided, the `callback` function MUST take no input - argument, otherwise it MUST take the `state` as the only input argument. + +The API SHOULD provide some way to pass `state` to the callback. Individual +language client can decide what is the idomatic approach (e.g. it could be an +additional parameter, or captured by the lambda closure, or something else). Here are some examples that individual language client might consider: @@ -365,9 +365,9 @@ Here are some examples that individual language client might consider: def pf_callback(): # Note: in the real world these would be retrieved from the operating system return ( - (8, ("pid", 0), ("bitness", 64)), - (37741921, ("pid", 4), ("bitness", 64)), - (10465, ("pid", 880), ("bitness", 32)), + (8, ("pid", 0), ("bitness", 64)), + (37741921, ("pid", 4), ("bitness", 64)), + (10465, ("pid", 880), ("bitness", 32)), ) page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) From 3c2ffc6199cf357d43bb0dd77d4230a664d84f1b Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 1 Apr 2021 20:29:44 -0700 Subject: [PATCH 03/15] update based on discussion during the SIG meeting --- specification/metrics/new_api.md | 41 +++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 686c8e02332..a274b1559b4 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -157,8 +157,6 @@ will have the following information: instruments, whether it is synchronous or asynchronous * An optional `unit of measure` * An optional `description` -* An optional list of [`Attribute`](../common/common.md#attributes) names and - types Instruments are associated with the Meter during creation, and are identified by the name: @@ -247,8 +245,6 @@ The API MUST accept the following parameters: rule](#instrument-unit). * An optional `description`, following the [instrument description rule](#instrument-description). -* An optional list of [`Attribute`](../common/common.md#attributes) names and - types. Here are some examples that individual language client might consider: @@ -342,20 +338,29 @@ The API MUST accept the following parameters: rule](#instrument-unit). * An optional `description`, following the [instrument description rule](#instrument-description). -* An optional list of [`Attribute`](../common/common.md#attributes) names and - types. * A `callback` function. - * This callback function will only be called when the Meter is being observed. - * This callback function will return the value(s) with optional - [`Attribute`](../common/common.md#attributes). Individual language client - can decide what is the idomatic approach (e.g. it could be a list, tuple, - generator, enumerator, etc.). - * Individual language client SHOULD define whether this callback function - needs to be reentrant safe / thread safe or not. + +The `callback` function is responsible for reporting the +[Measurement](#measurement)s. It will only be called when the Meter is being +observed. Individual language client SHOULD define whether this callback +function needs to be reentrant safe / thread safe or not. + +Individual language client can decide what is the idomatic approach. Here are +some examples: + +* Return a list (or tuple, generator, enumerator, etc.) of `Measurement`s. +* Use an observer argument to allow individual `Measurement`s to be reported. + +It is the responsibility of the [SDK](./README.md#sdk) to decide how to handle +duplicates. For example, during the callback invocation if two measurements +`value=1, attributes={pid:4 bitness:64}` and `value=2, attributes={pid:4, +bitness:64}` are reported, the SDK can decide to drop the entire data, pick the +last one, take all the data and add them together, or something else. The API SHOULD provide some way to pass `state` to the callback. Individual language client can decide what is the idomatic approach (e.g. it could be an -additional parameter, or captured by the lambda closure, or something else). +additional parameter to the callback function, or captured by the lambda +closure, or something else). Here are some examples that individual language client might consider: @@ -404,6 +409,14 @@ for the interaction between the API and SDK. * A value * [`Attributes`](../common/common.md#attributes) +## Compatibility + +All the metrics components SHOULD allow new APIs to be added to existing +components without introducing breaking changes. + +All the metrics APIs SHOULD allow optional parameter(s) to be added to existing +APIs without introducing breaking changes. + ## Concurrency For languages which support concurrent execution the Metrics APIs provide From 05d9ce8cdfca68bf4c86ff3a743919a13724d7f6 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 1 Apr 2021 20:46:56 -0700 Subject: [PATCH 04/15] add observer example --- specification/metrics/new_api.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index a274b1559b4..dd1226ee0c5 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -378,6 +378,18 @@ def pf_callback(): page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) ``` +```python +# Python + +def pf_callback(observer): + # Note: in the real world these would be retrieved from the operating system + observer.Observe(8, ("pid", 0), ("bitness", 64)) + observer.Observe(37741921, ("pid", 4), ("bitness", 64)) + observer.Observe(10465, ("pid", 880), ("bitness", 32)) + +page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) +``` + ```csharp // C# From 9f97e83f742444be9b7d0d13cc6376c01ed698bd Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 2 Apr 2021 14:38:53 -0700 Subject: [PATCH 05/15] clarify that it is the instrument not meter being observed --- specification/metrics/new_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index dd1226ee0c5..8ca1cd27b94 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -312,7 +312,7 @@ counterPowerUsed.Add(200, new PowerConsumption { customer = "Jerry" }, ("is_gree `ObservableCounter` is an asynchronous Instrument which reports [monotonically](https://wikipedia.org/wiki/Monotonic_function) increasing -value(s) when the Meter is observed. +value(s) when the instrument is being observed. Example uses for `ObservableCounter`: From f72826f3f185217d1a948c55f91e877aa43df4e3 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 2 Apr 2021 14:41:29 -0700 Subject: [PATCH 06/15] clarify that duplicates are not allowed, and the sdk can decide how to handle it --- specification/metrics/new_api.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 8ca1cd27b94..1c5664366c4 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -351,11 +351,12 @@ some examples: * Return a list (or tuple, generator, enumerator, etc.) of `Measurement`s. * Use an observer argument to allow individual `Measurement`s to be reported. -It is the responsibility of the [SDK](./README.md#sdk) to decide how to handle -duplicates. For example, during the callback invocation if two measurements -`value=1, attributes={pid:4 bitness:64}` and `value=2, attributes={pid:4, -bitness:64}` are reported, the SDK can decide to drop the entire data, pick the -last one, take all the data and add them together, or something else. +Duplicates are not allowed. If it happens, the [SDK](./README.md#sdk) can decide +how to handle it. For example, during the callback invocation if two +measurements `value=1, attributes={pid:4 bitness:64}` and `value=2, +attributes={pid:4, bitness:64}` are reported, the SDK can decide to drop the +entire data, pick the last one, take all the data and add them together, or +something else. The API SHOULD provide some way to pass `state` to the callback. Individual language client can decide what is the idomatic approach (e.g. it could be an From a8f3b39bb33235ea81e02087f5997a4900e3d933 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 2 Apr 2021 15:31:31 -0700 Subject: [PATCH 07/15] address review feedback --- specification/metrics/new_api.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 1c5664366c4..931e0bfce49 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -157,6 +157,8 @@ will have the following information: instruments, whether it is synchronous or asynchronous * An optional `unit of measure` * An optional `description` +* An optional list of [`Attribute`](../common/common.md#attributes) names and + types Instruments are associated with the Meter during creation, and are identified by the name: @@ -245,6 +247,8 @@ The API MUST accept the following parameters: rule](#instrument-unit). * An optional `description`, following the [instrument description rule](#instrument-description). +* An optional list of [`Attribute`](../common/common.md#attributes) names and + types Here are some examples that individual language client might consider: @@ -382,11 +386,11 @@ page_faults_observable_counter = meter.create_observable_counter(name="PF", desc ```python # Python -def pf_callback(observer): +def pf_callback(result): # Note: in the real world these would be retrieved from the operating system - observer.Observe(8, ("pid", 0), ("bitness", 64)) - observer.Observe(37741921, ("pid", 4), ("bitness", 64)) - observer.Observe(10465, ("pid", 880), ("bitness", 32)) + result.Observe(8, ("pid", 0), ("bitness", 64)) + result.Observe(37741921, ("pid", 4), ("bitness", 64)) + result.Observe(10465, ("pid", 880), ("bitness", 32)) page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) ``` @@ -422,14 +426,6 @@ for the interaction between the API and SDK. * A value * [`Attributes`](../common/common.md#attributes) -## Compatibility - -All the metrics components SHOULD allow new APIs to be added to existing -components without introducing breaking changes. - -All the metrics APIs SHOULD allow optional parameter(s) to be added to existing -APIs without introducing breaking changes. - ## Concurrency For languages which support concurrent execution the Metrics APIs provide From dca5ddc099c9178280f9bae1ddf1c640a9afcfe8 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Fri, 2 Apr 2021 15:32:37 -0700 Subject: [PATCH 08/15] fix typo --- specification/metrics/new_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 931e0bfce49..842140adffc 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -248,7 +248,7 @@ The API MUST accept the following parameters: * An optional `description`, following the [instrument description rule](#instrument-description). * An optional list of [`Attribute`](../common/common.md#attributes) names and - types + types. Here are some examples that individual language client might consider: From eb5cd995395a8224edbf595ce76d9e4f206015cd Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 6 Apr 2021 08:28:19 -0700 Subject: [PATCH 09/15] specify that the callback ensures a single timestamp --- specification/metrics/new_api.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index d91dfa6a250..423704641da 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -351,11 +351,12 @@ some examples: * Return a list (or tuple, generator, enumerator, etc.) of `Measurement`s. * Use an observer argument to allow individual `Measurement`s to be reported. -Duplicates are not allowed. If it happens, the [SDK](./README.md#sdk) can decide -how to handle it. For example, during the callback invocation if two -measurements `value=1, attributes={pid:4 bitness:64}` and `value=2, -attributes={pid:4, bitness:64}` are reported, the SDK can decide to drop the -entire data, pick the last one, take all the data and add them together, or +A single timestamp MUST be used per invocation of the callback. Duplicates are +not allowed. If it happens, the [SDK](./README.md#sdk) can decide how to handle +it. For example, during the callback invocation if two measurements `value=1, +attributes={pid:4 bitness:64}` and `value=2, attributes={pid:4, bitness:64}` are +reported, the SDK can decide to simply let them pass through (so the downstream +consumer can handle duplication), drop the entire data, pick the last one, or something else. The API SHOULD provide some way to pass `state` to the callback. Individual From b06a5d9be37c8e01ec73a97523385b394f4a95ab Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 6 Apr 2021 23:34:41 -0700 Subject: [PATCH 10/15] rename to CounterFunc --- specification/metrics/new_api.md | 46 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 423704641da..5e06410a5c2 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -31,9 +31,9 @@ Table of Contents * [Counter](#counter) * [Counter creation](#counter-creation) * [Counter operations](#counter-operations) - * [ObservableCounter](#observablecounter) - * [ObservableCounter creation](#observablecounter-creation) - * [ObservableCounter operations](#observablecounter-operations) + * [CounterFunc](#counterfunc) + * [CounterFunc creation](#counterfunc-creation) + * [CounterFunc operations](#counterfunc-operations) * [Measurement](#measurement) @@ -48,8 +48,8 @@ The Metrics API consists of these main components: * [Instrument](#instrument) is responsible for reporting [Measurements](#measurement). -Here is an example of the object hierarchy inside a process instrumented with the -metrics API: +Here is an example of the object hierarchy inside a process instrumented with +the metrics API: ```text +-- MeterProvider(default) @@ -112,9 +112,9 @@ This API MUST accept the following parameters: the specified value is invalid SHOULD be logged. A library, implementing the OpenTelemetry API *may* also ignore this name and return a default instance for all calls, if it does not support "named" functionality (e.g. an - implementation which is not even observability-related). A MeterProvider - could also return a no-op Meter here if application owners configure the SDK - to suppress telemetry produced by this library. + implementation which is not even observability-related). A MeterProvider could + also return a no-op Meter here if application owners configure the SDK to + suppress telemetry produced by this library. * `version` (optional): Specifies the version of the instrumentation library (e.g. `1.0.0`). @@ -308,13 +308,13 @@ counterPowerUsed.Add(13.5, new PowerConsumption { customer = "Tom" }); counterPowerUsed.Add(200, new PowerConsumption { customer = "Jerry" }, ("is_green_energy", true)); ``` -### ObservableCounter +### CounterFunc -`ObservableCounter` is an asynchronous Instrument which reports +`CounterFunc` is an asynchronous Instrument which reports [monotonically](https://wikipedia.org/wiki/Monotonic_function) increasing value(s) when the instrument is being observed. -Example uses for `ObservableCounter`: +Example uses for `CounterFunc`: * [CPU time](https://wikipedia.org/wiki/CPU_time), which could be reported for each thread, each process or the entire system. For example "the CPU time for @@ -322,13 +322,13 @@ Example uses for `ObservableCounter`: * The number of [page faults](https://wikipedia.org/wiki/Page_fault) for each process. -#### ObservableCounter creation +#### CounterFunc creation -There MUST NOT be any API for creating a `ObservableCounter` other than with a -[`Meter`](#meter). This MAY be called `CreateObservableCounter`. If strong type -is desired, the client can decide the language idomatic name(s), for example -`CreateUInt64ObservableCounter`, `CreateDoubleObservableCounter`, -`CreateObservableCounter`, `CreateObservableCounter`. +There MUST NOT be any API for creating a `CounterFunc` other than with a +[`Meter`](#meter). This MAY be called `CreateCounterFunc`. If strong type is +desired, the client can decide the language idomatic name(s), for example +`CreateUInt64CounterFunc`, `CreateDoubleCounterFunc`, +`CreateCounterFunc`, `CreateCounterFunc`. The API MUST accept the following parameters: @@ -377,7 +377,7 @@ def pf_callback(): (10465, ("pid", 880), ("bitness", 32)), ) -page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) +page_faults_counter_func = meter.create_counter_func(name="PF", description="process page faults", pf_callback) ``` ```python @@ -389,7 +389,7 @@ def pf_callback(result): result.Observe(37741921, ("pid", 4), ("bitness", 64)) result.Observe(10465, ("pid", 880), ("bitness", 32)) -page_faults_observable_counter = meter.create_observable_counter(name="PF", description="process page faults", pf_callback) +page_faults_counter_func = meter.create_counter_func(name="PF", description="process page faults", pf_callback) ``` ```csharp @@ -404,13 +404,13 @@ interface IAtomicClock IAtomicClock clock = AtomicClock.Connect(); -var obCaesiumOscillates = meter.CreateCounterObserver("caesium_oscillates", clk => clk.GetCaesiumOscillates(), clock); +var obCaesiumOscillates = meter.CreateCounterFunc("caesium_oscillates", clk => clk.GetCaesiumOscillates(), clock); ``` -#### ObservableCounter operations +#### CounterFunc operations -`ObservableCounter` is only intended for asynchronous scenario, it does not -provide any operation. +`CounterFunc` is only intended for asynchronous scenario, it does not provide +any operation. ## Measurement From be2c265ef2633999ccb9fae8d4723cac1d078b02 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 7 Apr 2021 11:01:09 -0700 Subject: [PATCH 11/15] address review comments --- specification/metrics/new_api.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 5e06410a5c2..0e4e614caa2 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -409,8 +409,9 @@ var obCaesiumOscillates = meter.CreateCounterFunc("caesium_oscillates", #### CounterFunc operations -`CounterFunc` is only intended for asynchronous scenario, it does not provide -any operation. +`CounterFunc` is only intended for asynchronous scenario. The only operation is +provided by the `callback`, which is registered during the [CounterFunc +creation](#counterfunc-creation). ## Measurement From 97a01e82f5e4d68fc13eed3bfc213ac98ff31956 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Wed, 7 Apr 2021 11:13:35 -0700 Subject: [PATCH 12/15] clarify that the callback function should not take indefinite amount of time --- specification/metrics/new_api.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 0e4e614caa2..7da89bd6a84 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -345,6 +345,10 @@ The `callback` function is responsible for reporting the observed. Individual language client SHOULD define whether this callback function needs to be reentrant safe / thread safe or not. +The callback function SHOULD NOT take indefinite amount of time. If multiple +independent SDKs coexist in a running process, they MUST invoke the callback +function(s) independently. + Individual language client can decide what is the idomatic approach. Here are some examples: From 138281dc6ce33b8f18266cebf46f13975fb7d3fe Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 8 Apr 2021 13:29:57 -0700 Subject: [PATCH 13/15] update the wording for callback based on PR comments --- specification/metrics/new_api.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 7da89bd6a84..72a040618ae 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -355,13 +355,16 @@ some examples: * Return a list (or tuple, generator, enumerator, etc.) of `Measurement`s. * Use an observer argument to allow individual `Measurement`s to be reported. -A single timestamp MUST be used per invocation of the callback. Duplicates are -not allowed. If it happens, the [SDK](./README.md#sdk) can decide how to handle -it. For example, during the callback invocation if two measurements `value=1, -attributes={pid:4 bitness:64}` and `value=2, attributes={pid:4, bitness:64}` are -reported, the SDK can decide to simply let them pass through (so the downstream -consumer can handle duplication), drop the entire data, pick the last one, or -something else. +User code is recommended not to provide more than one `Measurement` with the +same `attributes` in a single callback. If it happens, the +[SDK](./README.md#sdk) can decide how to handle it. For example, during the +callback invocation if two measurements `value=1, attributes={pid:4 bitness:64}` +and `value=2, attributes={pid:4, bitness:64}` are reported, the SDK can decide +to simply let them pass through (so the downstream consumer can handle +duplication), drop the entire data, pick the last one, or something else. The +API must treat observations from a single callback as logically taking place at +a single instant, such that when recorded, observations from a single callback +MUST be reported with identical timestamps. The API SHOULD provide some way to pass `state` to the callback. Individual language client can decide what is the idomatic approach (e.g. it could be an From 11985497ea32b3996174710f365ae26e7a55a757 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 8 Apr 2021 14:45:15 -0700 Subject: [PATCH 14/15] add notes that CounterFunc callback returns absolute value instead of delta/rate --- specification/metrics/new_api.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 72a040618ae..234942ec2a0 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -345,6 +345,11 @@ The `callback` function is responsible for reporting the observed. Individual language client SHOULD define whether this callback function needs to be reentrant safe / thread safe or not. +Note: Unlike [Counter.Add()](#add) which takes the increment/delta value, the +callback function reports the absolute value of the counter. To determine the +reported rate the counter is changing, the difference between successive +measurements is used. + The callback function SHOULD NOT take indefinite amount of time. If multiple independent SDKs coexist in a running process, they MUST invoke the callback function(s) independently. From 0afe3818b66a4c2a1ac0bf801cae3dec14dd0f25 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Thu, 8 Apr 2021 14:47:40 -0700 Subject: [PATCH 15/15] update the example based on feedback --- specification/metrics/new_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/metrics/new_api.md b/specification/metrics/new_api.md index 234942ec2a0..7da2b3cce28 100644 --- a/specification/metrics/new_api.md +++ b/specification/metrics/new_api.md @@ -416,7 +416,7 @@ interface IAtomicClock IAtomicClock clock = AtomicClock.Connect(); -var obCaesiumOscillates = meter.CreateCounterFunc("caesium_oscillates", clk => clk.GetCaesiumOscillates(), clock); +var obCaesiumOscillates = meter.CreateCounterFunc("caesium_oscillates", () => clock.GetCaesiumOscillates()); ``` #### CounterFunc operations