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)), )
meter.create_observable_counter(name=“PF”, description=“process page faults”, pf_callback)
```python
# Python
def pf_callback(result):
# Note: in the real world these would be retrieved from the operating system
result.Observe(8, ("pid", 0), ("bitness", 64))
result.Observe(37741921, ("pid", 4), ("bitness", 64))
result.Observe(10465, ("pid", 880), ("bitness", 32))
meter.create_observable_counter(name="PF", description="process page faults", pf_callback)
// C#
// A simple scenario where only one value is reported
interface IAtomicClock
{
UInt64 GetCaesiumOscillates();
}
IAtomicClock clock = AtomicClock.Connect();
meter.CreateObservableCounter<UInt64>("caesium_oscillates", () => clock.GetCaesiumOscillates());
Asynchronous Counter operations
Asynchronous Counter uses an idiomatic interface for reporting
measurements through a callback
, which is registered during
Asynchronous Counter creation.
For callback functions registered after an asynchronous instrument is
created, the API is required to support a mechanism for
unregistration. For example, the object returned from register_callback
can support an unregister()
method directly.
# Python
class Device:
"""A device with one counter"""
def __init__(self, meter, x):
self.x = x
counter = meter.create_observable_counter(name="usage", description="count of items used")
self.cb = counter.register_callback(self.counter_callback)
def counter_callback(self, result):
result.Observe(self.read_counter(), {'x', self.x})
def read_counter(self):
return 100 # ...
def stop(self):
self.cb.unregister()
Histogram
Histogram
is a synchronous Instrument which can be
used to report arbitrary values that are likely to be statistically meaningful.
It is intended for statistics such as histograms, summaries, and percentile.
Example uses for Histogram
:
- the request duration
- the size of the response payload
Histogram creation
There MUST NOT be any API for creating a Histogram
other than with a
Meter
. This MAY be called CreateHistogram
. If strong type is
desired, OpenTelemetry API authors MAY decide the language
idiomatic name(s), for example CreateUInt64Histogram
, CreateDoubleHistogram
,
CreateHistogram<UInt64>
, CreateHistogram<double>
.
See the general requirements for synchronous instruments.
Here are some examples that OpenTelemetry API authors might consider:
# Python
http_server_duration = meter.create_histogram(
name="http.server.duration",
description="measures the duration of the inbound HTTP request",
unit="ms",
value_type=float)
// C#
var httpServerDuration = meter.CreateHistogram<double>(
"http.server.duration",
description: "measures the duration of the inbound HTTP request",
unit: "ms"
);
Histogram operations
Record
Updates the statistics with the specified amount.
This API SHOULD NOT return a value (it MAY return a dummy value if required by
certain programming languages or systems, for example null
, undefined
).
This API MUST accept the following parameter:
-
A numeric value to record.
The value needs to be provided by a user. If possible, this API SHOULD be structured so a user is obligated to provide this parameter. If it is not possible to structurally enforce this obligation, this API MUST be documented in a way to communicate to users that this parameter is needed.
The value is expected to be non-negative. This API SHOULD be documented in a way to communicate to users that this value is expected to be non-negative. This API SHOULD NOT validate this value, that is left to implementations of the API.
-
Attributes to associate with the value.
Users can provide attributes to associate with the value, but it is up to their discretion. Therefore, this API MUST be structured to accept a variable number of attributes, including none.
OpenTelemetry API authors MAY decide to allow flexible attributes to be passed in as individual arguments. OpenTelemetry API authors MAY allow attribute values to be passed in using a more efficient way (e.g. strong typed struct allocated on the callstack, tuple). Here are some examples that OpenTelemetry API authors might consider:
# Python
http_server_duration.Record(50, {"http.request.method": "POST", "url.scheme": "https"})
http_server_duration.Record(100, http_method="GET", http_scheme="http")
// C#
httpServerDuration.Record(50, ("http.request.method", "POST"), ("url.scheme", "https"));
httpServerDuration.Record(100, new HttpRequestAttributes { method = "GET", scheme = "http" });
Gauge
Status: Experimental
Gauge
is a synchronous Instrument which can be
used to record non-additive value(s) (e.g. the background noise level - it makes
no sense to record the background noise level value from multiple rooms and sum
them up) when changes occur.
Note: If the values are additive (e.g. the process heap size - it makes sense to report the heap size from multiple processes and sum them up, so we get the total heap usage), use UpDownCounter.
Note: Synchronous Gauge is normally used when the measurements are exposed via a
subscription to change events (
i.e. backgroundNoiseLevel.onChange(value -> gauge.record(value))
). If the
measurement is exposed via an accessor,
use Asynchronous Gauge to invoke the accessor in a
callback function (
i.e. createObservableGauge(observable -> observable.record(backgroundNoiseLevel.getCurrentValue()))
.
Example uses for Gauge:
- subscribe to change events for the background noise level
- subscribe to change events for the CPU fan speed
Gauge creation
There MUST NOT be any API for creating a Gauge
other than with a
Meter
. This MAY be called CreateGauge
. If strong type is
desired, OpenTelemetry API authors MAY decide the language
idiomatic name(s), for example CreateUInt64Gauge
, CreateDoubleGauge
,
CreateGauge<UInt64>
, CreateGauge<double>
.
See the general requirements for synchronous instruments.
Here are some examples that OpenTelemetry API authors might consider:
// Java
DoubleGauge backgroundNoiseLevel = meter.gaugeBuilder("facility.noise.level")
.setDescription("Background noise level of rooms")
.setUnit("B")
.build();
Gauge operations
Record
Record the Gauge current value.
This API SHOULD NOT return a value (it MAY return a dummy value if required by
certain programming languages or systems, for example null
, undefined
).
This API MUST accept the following parameter:
-
A numeric value. The current absolute value.
The value needs to be provided by a user. If possible, this API SHOULD be structured so a user is obligated to provide this parameter. If it is not possible to structurally enforce this obligation, this API MUST be documented in a way to communicate to users that this parameter is needed.
-
Attributes to associate with the value.
Users can provide attributes to associate with the value, but it is up to their discretion. Therefore, this API MUST be structured to accept a variable number of attributes, including none.
The OpenTelemetry API authors MAY decide to allow flexible attributes to be passed in as arguments. If the attribute names and types are provided during the gauge creation, the OpenTelemetry API authors MAY allow attribute values to be passed in using a more efficient way (e.g. strong typed struct allocated on the callstack, tuple). The API MUST allow callers to provide flexible attributes at invocation time rather than having to register all the possible attribute names during the instrument creation. Here are some examples that OpenTelemetry API authors might consider:
// Java
Attributes roomA = Attributes.builder().put("room.id", "Rack A");
Attributes roomB = Attributes.builder().put("room.id", "Rack B");
backgroundNoiseLevel.record(4.3, roomA);
backgroundNoiseLevel.record(2.5, roomB);
Asynchronous Gauge
Asynchronous Gauge is an asynchronous Instrument which reports non-additive value(s) (e.g. the room temperature - it makes no sense to report the temperature value from multiple rooms and sum them up) when the instrument is being observed.
Note: if the values are additive (e.g. the process heap size - it makes sense to report the heap size from multiple processes and sum them up, so we get the total heap usage), use Asynchronous Counter or Asynchronous UpDownCounter.
Example uses for Asynchronous Gauge:
- the current room temperature
- the CPU fan speed
Asynchronous Gauge creation
There MUST NOT be any API for creating an Asynchronous Gauge other than with a
Meter
. This MAY be called CreateObservableGauge
. If strong type is
desired, OpenTelemetry API authors MAY decide the language
idiomatic name(s), for example CreateUInt64ObservableGauge
,
CreateDoubleObservableGauge
, CreateObservableGauge<UInt64>
,
CreateObservableGauge<double>
.
It is highly recommended that implementations use the name ObservableGauge
(or any language idiomatic variation, e.g. observable_gauge
) unless there is
a strong reason not to do so. Please note that the name has nothing to do with
asynchronous
pattern and
observer pattern.
See the general requirements for asynchronous instruments.
Here are some examples that OpenTelemetry API authors might consider:
# Python
def cpu_frequency_callback():
# Note: in the real world these would be retrieved from the operating system
return (
(3.38, ("cpu", 0), ("core", 0)),
(3.51, ("cpu", 0), ("core", 1)),
(0.57, ("cpu", 1), ("core", 0)),
(0.56, ("cpu", 1), ("core", 1)),
)
meter.create_observable_gauge(
name="cpu.frequency",
description="the real-time CPU clock speed",
callback=cpu_frequency_callback,
unit="GHz",
value_type=float)
# Python
def cpu_frequency_callback(result):
# Note: in the real world these would be retrieved from the operating system
result.Observe(3.38, ("cpu", 0), ("core", 0))
result.Observe(3.51, ("cpu", 0), ("core", 1))
result.Observe(0.57, ("cpu", 1), ("core", 0))
result.Observe(0.56, ("cpu", 1), ("core", 1))
meter.create_observable_gauge(
name="cpu.frequency",
description="the real-time CPU clock speed",
callback=cpu_frequency_callback,
unit="GHz",
value_type=float)
// C#
// A simple scenario where only one value is reported
meter.CreateObservableGauge<double>("temperature", () => sensor.GetTemperature());
Asynchronous Gauge operations
Asynchronous Gauge uses an idiomatic interface for reporting
measurements through a callback
, which is registered during
Asynchronous Gauge creation.
For callback functions registered after an asynchronous instrument is
created, the API is required to support a mechanism for
unregistration. For example, the object returned from register_callback
can support an unregister()
method directly.
# Python
class Device:
"""A device with one gauge"""
def __init__(self, meter, x):
self.x = x
gauge = meter.create_observable_gauge(name="pressure", description="force/area")
self.cb = gauge.register_callback(self.gauge_callback)
def gauge_callback(self, result):
result.Observe(self.read_gauge(), {'x', self.x})
def read_gauge(self):
return 100 # ...
def stop(self):
self.cb.unregister()
UpDownCounter
UpDownCounter
is a synchronous Instrument which
supports increments and decrements.
Note: if the value is monotonically increasing, use Counter instead.
Example uses for UpDownCounter
:
- the number of active requests
- the number of items in a queue
An UpDownCounter
is intended for scenarios where the absolute values are not
pre-calculated, or fetching the “current value” requires extra effort. If the
pre-calculated value is already available or fetching the snapshot of the
“current value” is straightforward, use Asynchronous
UpDownCounter instead.
UpDownCounter supports counting the size of a collection incrementally, e.g. reporting the number of items in a concurrent bag by the “color” and “material” properties as they are added and removed.
Color | Material | Count |
---|---|---|
Red | Aluminum | 1 |
Red | Steel | 2 |
Blue | Aluminum | 0 |
Blue | Steel | 5 |
Yellow | Aluminum | 0 |
Yellow | Steel | 3 |
# Python
items_counter = meter.create_up_down_counter(
name="store.inventory",
description="the number of the items available")
def restock_item(color, material):
inventory.add_item(color=color, material=material)
items_counter.add(1, {"color": color, "material": material})
return true
def sell_item(color, material):
succeeded = inventory.take_item(color=color, material=material)
if succeeded:
items_counter.add(-1, {"color": color, "material": material})
return succeeded
UpDownCounter creation
There MUST NOT be any API for creating an UpDownCounter
other than with a
Meter
. This MAY be called CreateUpDownCounter
. If strong type is
desired, OpenTelemetry API authors MAY decide the language
idiomatic name(s), for example CreateInt64UpDownCounter
,
CreateDoubleUpDownCounter
, CreateUpDownCounter<Int64>
,
CreateUpDownCounter<double>
.
See the general requirements for synchronous instruments.
Here are some examples that OpenTelemetry API authors might consider:
# Python
customers_in_store = meter.create_up_down_counter(
name="grocery.customers",
description="measures the current customers in the grocery store",
value_type=int)
// C#
var customersInStore = meter.CreateUpDownCounter<int>(
"grocery.customers",
description: "measures the current customers in the grocery store",
);
UpDownCounter operations
Add
Increment or decrement the UpDownCounter by a fixed amount.
This API SHOULD NOT return a value (it MAY return a dummy value if required by
certain programming languages or systems, for example null
, undefined
).
This API MUST accept the following parameter:
-
A numeric value to add.
The value needs to be provided by a user. If possible, this API SHOULD be structured so a user is obligated to provide this parameter. If it is not possible to structurally enforce this obligation, this API MUST be documented in a way to communicate to users that this parameter is needed.
-
Attributes to associate with the value.
Users can provide attributes to associate with the value, but it is up to their discretion. Therefore, this API MUST be structured to accept a variable number of attributes, including none.
OpenTelemetry API authors MAY decide to allow flexible attributes to be passed in as individual arguments. OpenTelemetry API authors MAY allow attribute values to be passed in using a more efficient way (e.g. strong typed struct allocated on the callstack, tuple). Here are some examples that OpenTelemetry API authors might consider:
# Python
customers_in_store.add(1, {"account.type": "commercial"})
customers_in_store.add(-1, account_type="residential")
// C#
customersInStore.Add(1, ("account.type", "commercial"));
customersInStore.Add(-1, new Account { Type = "residential" });
Asynchronous UpDownCounter
Asynchronous UpDownCounter is an asynchronous Instrument which reports additive value(s) (e.g. the process heap size - it makes sense to report the heap size from multiple processes and sum them up, so we get the total heap usage) when the instrument is being observed.
Note: if the value is monotonically increasing, use Asynchronous Counter instead; if the value is non-additive, use Asynchronous Gauge instead.
Example uses for Asynchronous UpDownCounter:
- the process heap size
- the approximate number of items in a lock-free circular buffer
Asynchronous UpDownCounter creation
There MUST NOT be any API for creating an Asynchronous UpDownCounter other than
with a Meter
. This MAY be called CreateObservableUpDownCounter
. If
strong type is desired, OpenTelemetry API authors MAY
decide the language idiomatic name(s), for example
CreateUInt64ObservableUpDownCounter
, CreateDoubleObservableUpDownCounter
,
CreateObservableUpDownCounter<UInt64>
,
CreateObservableUpDownCounter<double>
.
It is highly recommended that implementations use the name
ObservableUpDownCounter
(or any language idiomatic variation, e.g.
observable_up_down_counter
) unless there is a strong reason not to do so. Please
note that the name has nothing to do with asynchronous
pattern and
observer pattern.
See the general requirements for asynchronous instruments.
Note: Unlike UpDownCounter.Add() which takes the increment/delta value, the callback function reports the absolute value of the Asynchronous UpDownCounter. To determine the reported rate the Asynchronous UpDownCounter is changing, the difference between successive measurements is used.
Here are some examples that OpenTelemetry API authors might consider:
# Python
def ws_callback():
# Note: in the real world these would be retrieved from the operating system
return (
(8, ("pid", 0), ("bitness", 64)),
(20, ("pid", 4), ("bitness", 64)),
(126032, ("pid", 880), ("bitness", 32)),
)
meter.create_observable_up_down_counter(
name="process.workingset",
description="process working set",
callback=ws_callback,
unit="kB",
value_type=int)
# Python
def ws_callback(result):
# Note: in the real world these would be retrieved from the operating system
result.Observe(8, ("pid", 0), ("bitness", 64))
result.Observe(20, ("pid", 4), ("bitness", 64))
result.Observe(126032, ("pid", 880), ("bitness", 32))
meter.create_observable_up_down_counter(
name="process.workingset",
description="process working set",
callback=ws_callback,
unit="kB",
value_type=int)
// C#
// A simple scenario where only one value is reported
meter.CreateObservableUpDownCounter<UInt64>("memory.physical.free", () => WMI.Query("FreePhysicalMemory"));
Asynchronous UpDownCounter operations
Asynchronous UpDownCounter uses an idiomatic interface for reporting
measurements through a callback
, which is registered during
Asynchronous Updowncounter creation.
For callback functions registered after an asynchronous instrument is
created, the API is required to support a mechanism for
unregistration. For example, the object returned from register_callback
can support an unregister()
method directly.
# Python
class Device:
"""A device with one up_down_counter"""
def __init__(self, meter, x):
self.x = x
updowncounter = meter.create_observable_up_down_counter(name="queue_size", description="items in process")
self.cb = updowncounter.register_callback(self.up_down_counter_callback)
def up_down_counter_callback(self, result):
result.Observe(self.read_up_down_counter(), {'x', self.x})
def read_up_down_counter(self):
return 100 # ...
def stop(self):
self.cb.unregister()
Measurement
A Measurement
represents a data point reported via the metrics API to the SDK.
Please refer to the Metrics Programming Model
for the interaction between the API and SDK.
Measurement
s encapsulate:
- A value
Attributes
Multiple-instrument callbacks
The Metrics API MAY support an interface allowing the use of multiple instruments from a single registered Callback. The API to register a new Callback SHOULD accept:
- A
callback
function - A list (or tuple, etc.) of Instruments used in the
callback
function.
It is RECOMMENDED that the API authors use one of the following forms
for the callback
function:
- The list (or tuple, etc.) returned by the
callback
function contains(Instrument, Measurement)
pairs. - the Observable Result parameter receives an additional
(Instrument, Measurement)
pairs
This interface is typically a more performant way to report multiple
measurements when they are obtained through an expensive process, such
as reading /proc
files or probing the garbage collection subsystem.
For example,
# Python
class Device:
"""A device with two instruments"""
def __init__(self, meter, property):
self.property = property
self.usage = meter.create_observable_counter(name="usage", description="count of items used")
self.pressure = meter.create_observable_gauge(name="pressure", description="force per unit area")
# Note the two associated instruments are passed to the callback.
meter.register_callback([self.usage, self.pressure], self.observe)
def observe(self, result):
usage, pressure = expensive_system_call()
result.observe(self.usage, usage, {'property', self.property})
result.observe(self.pressure, pressure, {'property', self.property})
Compatibility requirements
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, if possible.
Concurrency requirements
For languages which support concurrent execution the Metrics APIs provide specific guarantees and safeties.
MeterProvider - all methods are safe to be called concurrently.
Meter - all methods are safe to be called concurrently.
Instrument - All methods of any Instrument are safe to be called concurrently.