From 06e521783018747f84ab4c9ec745f79233917b7f Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Thu, 12 Sep 2019 10:21:03 -0700 Subject: [PATCH] RFC 0003, 0007 updates (Metrics measure type, metrics handle specification), RFC 0004 (configurable aggregation) deleted (open-telemetry/oteps#29) * Updates to 0003 following work session 8/21/2019 * Update date * Feedback applied * Feedback applied * Remove handle specification, will create another RFC * More typing * Add metrics handles RFC * Rename 0000 * Remove 0004 * Add an open question from python PR87 * Add an open question about RecordBatch * Clarify the open questions * Name NonNegative and NonDescending options * Clarify the Measurement unit for RecordBatch * Add issues addressed * Linkify * Linkify * Format * Address option names and default settings * Answer questions * Spelling * Use 0007 * Refer to 0008 * Take suggestion --- oteps/0003-measure-metric-type.md | 178 +++++++++++++++--- oteps/0004-metric-configurable-aggregation.md | 75 -------- oteps/0007-metric-handles.md | 78 ++++++++ 3 files changed, 233 insertions(+), 98 deletions(-) delete mode 100644 oteps/0004-metric-configurable-aggregation.md create mode 100644 oteps/0007-metric-handles.md diff --git a/oteps/0003-measure-metric-type.md b/oteps/0003-measure-metric-type.md index 916e67dd4..6a17671f6 100644 --- a/oteps/0003-measure-metric-type.md +++ b/oteps/0003-measure-metric-type.md @@ -2,50 +2,182 @@ **Status:** `proposed` -## Forward +# Foreword -This propsal was originally split into three semi-related parts. Based on the feedback, they are now combined here into a single proposal. The original proposals were: +A working group convened on 8/21/2019 to discuss and debate the two metrics RFCs (0003 and 0004) and several surrounding concerns. This document has been revised with related updates that were agreed upon during this working session. See the [meeting notes](https://docs.google.com/document/d/1d0afxe3J6bQT-I6UbRXeIYNcTIyBQv4axfjKF4yvAPA/edit#). - 000x-metric-pre-defined-labels - 000x-metric-measure - 000x-eliminate-stats-record +# Overview -## Overview +Introduce a `Measure` kind of metric object that supports a `Record` API method. Like existing `Gauge` and `Cumulative` metrics, the new `Measure` metric supports pre-defined labels. A new `RecordBatch` measurement API is introduced for recording multiple metric observations simultaneously. -Introduce a `Measure` type of metric object that supports a `Record` API. Like existing `Gauge` and `Cumulative` metrics, the new `Measure` metric supports pre-defined labels. A new measurement batch API is introduced for recording multiple metric observations simultaneously. +## Terminology -## Motivation +This RFC changes how "Measure" is used in the OpenTelemetry metrics specification. Before, "Measure" was the name of a series of raw measurements. After, "Measure" is the kind of a metric object used for recording a series raw measurements. -In the current `Metric.GetOrCreateTimeSeries` API for Gauges and Cumulatives, the caller obtains a `TimeSeries` handle for repeatedly recording metrics with certain pre-defined label values set. This is an important optimization, especially for exporting aggregated metrics. +Since this document will be read in the future after the proposal has been written, uses of the word "current" lead to confusion. For this document, the term "preceding" refers to the state that was current prior to these changes. -The use of pre-defined labels improves usability too, for working with metrics in code. Application programs with long-lived objects and associated Metrics can compute predefined label values once (e.g., in a constructor), rather than once per call site. +# Motivation -The current raw statistics API does not support pre-defined labels. This RFC replaces the raw statistics API by a new, general-purpose type of metric, `MeasureMetric`, generally intended for recording individual measurements the way raw statistics did, with added support for pre-defined labels. +In the preceding `Metric.GetOrCreateTimeSeries` API for Gauges and Cumulatives, the caller obtains a `TimeSeries` handle for repeatedly recording metrics with certain pre-defined label values set. This enables an important optimization for exporting pre-aggregated metrics, since the implementation is able to compute the aggregate summary "entry" using a pointer or fast table lookup. The efficiency gain requires that the aggregation keys be a subset of the pre-defined labels. -The former raw statistics API supported all-or-none recording for interdependent measurements. This RFC introduces a `MeasurementBatch` to support recording batches of metric observations. +Application programs with long-lived objects and associated Metrics can take advantage of pre-defined labels by computing label values once per object (e.g., in a constructor), rather than once per call site. In this way, the use of pre-defined labels improves the usability of the API as well as makes an important optimization possible to the implementation. -## Explanation +The preceding raw statistics API did not specify support for pre-defined labels. This RFC replaces the raw statistics API by a new, general-purpose kind of metric, `MeasureMetric`, generally intended for recording individual measurements like the preceding raw statistics API, with explicit support for pre-defined labels. -In the current proposal, Metrics are used for pre-aggregated metric types, whereas Raw statistics are used for uncommon and vendor-specific aggregations. The optimization and the usability advantages gained with pre-defined labels should be extended to Raw statistics because they are equally important and equally applicable. This is a new requirement. +The preceding raw statistics API supported all-or-none recording for interdependent measurements. This RFC introduces a `RecordBatch` API to support recording batches of measurements in a single API call, where a `Measurement` is now defined as a tuple of `MeasureMetric`, `Value` (integer or floating point), and `Labels`. -For example, where the application wants to compute a histogram of some value (e.g., latency), there's good reason to pre-aggregate such information. In this example, it allows an implementation to effienctly export the histogram of latencies "grouped" into individual results by label value(s). +# Explanation -The new `MeasureMetric` API satisfies the requirements of a single-argument call to record raw statistics, but the raw statistics API had secondary purpose, that of supporting recording multiple observed values simultaneously. This proposal introduces a `MeasurementBatch` API to record multiple metric observations in a single call. +The common use for `MeasureMetric`, like the preceding raw statistics API, is for reporting information about rates and distributions over structured, numerical event data. Measure metrics are the most general-purpose of metrics. Informally, the individual metric event has a logical format expressed as one primary key=value (the metric name and a numerical value) and any number of secondary key=values (the labels, resources, and context). -## Internal details + metric_name=_number_ + pre_defined1=_any_value_ + pre_defined2=_any_value_ + ... + resource1=_any_value_ + resource2=_any_value_ + ... + context_tag1=_any_value_ + context_tag2=_any_value_ + ... -The type known as `MeasureMetric` is a direct replacement for the raw statistics `Measure` type. The `MeasureMetric.Record` method records a single observation of the metric. The `MeasureMetric.GetOrCreateTimeSeries` supports pre-defined labels, just the same as `Gauge` and `Cumulative` metrics. +Here, "pre_defined" keys are those captured in the metrics handle, "resource" keys are those configured when the SDK was initialized, and "context_tag" keys are those propagated via context. -## Trade-offs and mitigations +Events of this form can logically capture a single update to a named metric, whether a cumulative, gauge, or measure kind of metric. This logical structure defines a _low-level encoding_ of any metric event, across the three kinds of metric. An SDK could simply encode a stream of these events and the consumer, provided access to the metric definition, should be able to interpret these events according to the semantics prescribed for each kind of metric. -This Measure Metric API is conceptually close to the Prometheus [Histogram, Summary, and Untyped metric types](https://prometheus.io/docs/concepts/metric_types/), but there is no way in OpenTelemetry to distinguish these cases at the declaration site, in code. This topic is covered in 0004-metric-configurable-aggregation. +## Metrics API concepts + +The `Meter` interface represents the metrics portion of the OpenTelemetry API. + +There are three kinds of metric instrument, `CumulativeMetric`, `GaugeMetric`, and `MeasureMetric`. + +Metric instruments are constructed by the API, they are not constructed by any specific SDK. + +| Field | Description | +|------|-----------| +| Name | A string. | +| Kind | One of Cumulative, Gauge, or Measure. | +| Required Keys | List of always-defined keys in handles for this metric. | +| Unit | The unit of measurement being recorded. | +| Description | Information about this metric. | + +See the specification for more information on these fields, including formatting and uniqueness requirements. To define a new metric, use one of the language-specific API methods (e.g., with names like `NewCumulativeMetric`, `NewGaugeMetric`, or `NewMeasureMetric`). + +Metric instrument Handles are SDK-provided objects that combine a metric instrument with a set of pre-defined labels. Handles are obtained by calling a language-specific API method (e.g., `GetHandle`) on the metric instrument with certain label values. Handles may be used to `Set()`, `Add()`, or `Record()` metrics according to their kind. + +## Selecting Metric Kind + +By separation of API and implementation in OpenTelemetry, we know that an implementation is free to do _anything_ in response to a metric API call. By the low-level interpretation defined above, all metric events have the same structural representation, only their logical interpretation varies according to the metric definition. Therefore, we select metric kinds based on two primary concerns: + +1. What should be the default implementation behavior? Unless configured otherwise, how should the implementation treat this metric variable? +1. How will the program source code read? Each metric uses a different verb, which helps convey meaning and describe default behavior. Cumulatives have an `Add()` method. Gauges have a `Set()` method. Measures have a `Record()` method. + +To guide the user in selecting the right kind of metric for an application, we'll consider the following questions about the primary intent of reporting given data. We use "of primary interest" here to mean information that is almost certainly useful in understanding system behavior. Consider these questions: + +- Does the measurement represent a quantity of something? Is it also non-negative? +- Is the sum a matter of primary interest? +- Is the event count a matter of primary interest? +- Is the distribution (p50, p99, etc.) a matter of primary interest? + +The specification will be updated with the following guidance. + +### Cumulative metric + +Likely to be the most common kind of metric, cumulative metric events express the computation of a sum. Choose this kind of metric when the value is a quantity, the sum is of primary interest, and the event count and distribution are not of primary interest. To raise (or lower) a cumulative metric, call the `Add()` method. + +If the quantity in question is always non-negative, it implies that the sum never descends. This is the common case, where cumulative metrics only go up, and these _unidirectional_ cumulative metric instruments serve to compute a rate. For this reason, cumulative metrics have a `Bidirectional` option to be declared as allowing negative inputs, the uncommon case. The API will reject negative inputs to (default) unidirectional cumulative metrics, instead submitting an SDK error event, which helps ensure meaningful rate calculations. + +For cumulative metrics, the default OpenTelemetry implementation exports the sum of event values taken over an interval of time. + +### Gauge metric + +Gauge metrics express a pre-calculated value that is either `Set()` by explicit instrumentation or observed through a callback. Generally, this kind of metric should be used when the metric cannot be expressed as a sum or a rate because the measurement interval is arbitrary. Use this kind of metric when the measurement is not a quantity, and the sum and event count are not of interest. + +Only the gauge kind of metric supports observing the metric via a gauge `Observer` callback (as an option, see `0008-metric-observer.md`). Semantically, there is an important difference between explicitly setting a gauge and observing it through a callback. In case of setting the gauge explicitly, the call happens inside of an implicit or explicit context. The implementation is free to associate the explicit `Set()` event with a context, for example. When observing gauge metrics via a callback, there is no context associated with the event. + +As a special case, to support existing metrics infrastructure and the `Observer` pattern, a gauge metric may be declared as a precomputed, unidirectional sum using the `Unidirectional` option, in which case it is may be used to define a rate. The initial value is presumed to be zero. The API will reject descending updates to non-descending gauges, instead submitting an SDK error event. + +For gauge metrics, the default OpenTelemetry implementation exports the last value that was explicitly `Set()`, or if using a callback, the current value from the `Observer`. + +### Measure metric + +Measure metrics express a distribution of values. This kind of metric should be used when the count or rate of events is meaningful and either: + +1. The sum is of interest in addition to the count (rate) +1. Quantile information is of interest. + +The key property of a measure metric event is that computing quantiles and/or summarizing a distribution (e.g., via a histogram) may be expensive. Not only will implementations have various capabilities and algorithms for this task, users may wish to control the quality and cost of aggregating measure metrics. + +Like cumulative metrics, non-negative measures are an important case because they support rate calculations. As an option, measure metrics may be declared as `NonNegative`. The API will reject negative metric events for non-negative measures, instead submitting an SDK error event. + +Because measure metrics have such wide application, implementations are likely to provide configurable behavior. OpenTelemetry may provide such a facility in its standard SDK, but in case no configuration is provided by the application, a low-cost policy is specified as the default behavior, which is to export the sum, the count (rate), the minimum value, and the maximum value. + +### Option to disable metrics by default + +Metric instruments are enabled by default, meaning that SDKs will export metric data for this instrument without configuration. Metric instruments support a `Disabled` option, marking them as verbose sources of information that may be configured on an as-needed basis to control cost (e.g., using a "views" API). + +### Option summary + +The optional properties of a metric instrument are: + +| Property | Description | Metric kind | +|----------|-------------|-------------| +| Required Keys | Determines labels that are always set on metric handles | All kinds | +| Disabled | Indicates a verbose metric that does not report by default | All kinds | +| Bidirectional | Indicates a cumulative metric instrument that goes up and down | Cumulative | +| Unidirectional | Indicate a gauge that only ascends, for rate calculation | Gauge | +| NonNegative | Indicates a measure that is never negative, for rate calculation | Measure | + +### RecordBatch API + +Applications sometimes want to act upon multiple metric handles in a single API call, either because the values are inter-related to each other, or because it lowers overhead. We agree that recording batch measurements will be restricted to measure metrics, although this support could be extended to all kinds of metric in the future. + +A single measurement is defined as: + +- Handle: the measure instrument and pre-defined label values +- Value: the recorded floating point or integer data + +The batch measurement API uses a language-specific method name (e.g., `RecordBatch`). The entire batch of measurements takes place within some (implicit or explicit) context. ## Prior art and alternatives -Prometheus supports the notion of vector metrics, which are those which support pre-defined labels. The vector-metric API supports a variety of methods like `WithLabelValues` to associate labels with a metric handle, similar to `GetOrCreateTimeSeries` in OpenTelemetry. As in this proposal, Prometheus supports a vector API for all metric types. +Prometheus supports the notion of vector metrics, which are those that support pre-defined labels for a specific set of required keys. The vector-metric API supports a variety of methods like `WithLabelValues` to associate labels with a metric handle, similar to `GetHandle` in OpenTelemetry. As in this proposal, Prometheus supports a vector API for all metric types. + +Statsd libraries generally report metric events individually. To implement statsd reporting from the OpenTelemetry, a `Meter` SDK would be installed that converts metric events into statsd updates. ## Open questions -Argument ordering has been proposed as the way to pass pre-defined label values in `GetOrCreateTimeseries`. The argument list must match the parameter list exactly, and if it doesn't we generally find out at runtime or not at all. This model has more optimization potential, but is easier to misuse, than the alternative. The alternative approach is to always pass label:value pairs to `GetOrCreateTimeseries`, as opposed to an ordered list of values. +### `GetHandle` argument ordering +Argument ordering has been proposed as the way to pass pre-defined label values in `GetHandle`. The argument list must match the parameter list exactly, and if it doesn't we generally find out at runtime or not at all. This model has more optimization potential, but is easier to misuse than the alternative. The alternative approach is to always pass label:value pairs to `GetOrCreateTimeseries`, as opposed to an ordered list of values. + +### `RecordBatch` argument ordering + +The discussion above can be had for the proposed `RecordBatch` method. It can be declared with an ordered list of metrics, then the `Record` API takes only an ordered list of numbers. Alternatively, and less prone to misuse, the `MeasurementBatch.Record` API could be declared with a list of metric:number pairs. + +### Eliminate `GetDefaultHandle()` + +Instead of a mechanism to obtain a default handle, some languages may prefer to simply operate on the metric instrument directly in this case. Should OpenTelemetry eliminate `GetDefaultHandle` and instead specify that cumulative, gauge, and measure metric instruments implement `Add()`, `Set()`, and `Record()` with the same interpretation? + +The argument against this is that metric instruments are meant to be pure API objects, they are not constructed through an SDK. Therefore, the default Meter (SDK) will have to be located from the context, meaning there is a question about whether this is as efficient as storing a re-usable handle for the default case. For metric instruments with no required keys, this will be a real question: what is the benefit of a handle of it specifies no information other than the SDK? + +If we eliminate `GetDefaultHandle()`, the SDK may keep a map of metric instrument to default handle on its own. + +### `RecordBatch` support for all metrics + +In the 8/21 working session, we agreed to limit `RecordBatch` to recording of simultaneous measure metrics, meaning to exclude cumulatives and gauges from batch recording. There are arguments in favor of supporting batch recording for all metric instruments. + +- If atomicity (i.e., the all-or-none property) is the reason for batch reporting, it makes sense to include all the metric instruments in the API +- `RecordBatch` support for cumulatives and gauges will be natural for SDKs that act as forwarders for metric events . The natural implementation for `Add()` and `Set()` methods will be `RecordBatch` with a single event. +- Likewise, it is simple for an SDK that acts as an aggregator (not a forwarder) to redirect `Add()` and `Set()` APIs to the handle-specific `Add()` and `Set()` methods; while the SDK, as the implementation, still may (not must) treat these cumulative and gauge updates as atomic. + +Arguments against batch recording for all metric instruments: + +- The `Record` in `RecordBatch` suggests it is to be applied to measure metrics. This is due to measure metrics being the most general-purpose of metric instruments. + +## Issues addressed + +[Raw vs. other metrics / measurements are unclear](https://github.com/open-telemetry/opentelemetry-specification/issues/83) + +[Eliminate Measurement class to save on allocations](https://github.com/open-telemetry/opentelemetry-specification/issues/145) -The same discussion can be had for the `MeasurementBatch` type described here. It can be declared with an ordered list of metrics, then the `Record` API takes only an ordered list of numbers. Alternatively, and less prone to misuse, the `MeasurementBatch.Record` API could be declared with a list of metric:number pairs. +[Implement three more types of Metric](https://github.com/open-telemetry/opentelemetry-specification/issues/146) diff --git a/oteps/0004-metric-configurable-aggregation.md b/oteps/0004-metric-configurable-aggregation.md deleted file mode 100644 index 9c9dc8e91..000000000 --- a/oteps/0004-metric-configurable-aggregation.md +++ /dev/null @@ -1,75 +0,0 @@ -# Let Metrics support configurable, recommended aggregations - -**Status:** `proposed` - -Let the user configure recommended Metric aggregations (SUM, COUNT, MIN, MAX, LAST_VALUE, HISTOGRAM, SUMMARY). - -## Motivation - -In the current API proposal, Metric types like Gauge and Cumulative are mapped into specific aggregations: Gauge:LAST_VALUE and Cumulative:SUM. Depending on RFC 0003-measure-metric-type, which creates a new MeasureMetric type, this proposal introduces the ability to configure alternative, potentially multiple aggregations for Metrics. This allows the MeasureMetric type to support HISTOGRAM and SUMMARY aggregations, as an alternative to raw statistics. - -## Explanation - -This proposal completes the elimination of Raw statistics by recognizing that aggregations should be independent of metric type. This recognizes that _sometimes_ we have a cumulative but want to compute a histogram of increment values, and _sometimes_ we have a measure that has multiple interesting aggregations. - -Following this change, we should think of the _Metric type_ as: - -1. Indicating something about what kind of numbers are being recorded (i.e., the input domain, e.g., restricted to values >= 0?) - 1. For Gauges: Something pre-computed where rate or count is not relevant - 1. For Cumulatives: Something where rate or count is relevant - 1. For Measures: Something where individual values are relevant -1. Indicating something about the default interpretation, based on the action verb (Set, Inc, Record, etc.) - 1. For Gauges: the action is Set() - 1. For Cumulatives: the action is Inc() - 1. For Measures: the action is Record() -1. Unless the programmer declares otherwise, suggesting a default aggregation - 1. For Gauges: LAST_VALUE is interesting, SUM is likely not interesting - 1. For Cumulatives: SUM is interesting, LAST_VALUE is likely not interesting - 1. For Measures: all aggregations apply, default is MIN, MAX, SUM, COUNT. - -## Internal details - -Metric constructors should take an optional list of aggregations, to override the default behavior. When constructed with an explicit list of aggregations, the implementation may use this as a hint about which aggregations should be exported by default. However, the implementation is not bound by these recommendations in any way and is free to control which aggregations that are applied. - -The standard defined aggregations are broken into two groups, those which are "decomposable" (i.e., inexpensive) and those which are not. - -The decomposable aggregations are simple to define: - -1. SUM: The sum of observed values. -1. COUNT: The number of observations. -1. MIN: The smallest value. -1. MAX: The largest value. -1. LAST_VALUE: The latest value. - -The non-decomposable aggregations do not have standard definitions, they are purely advisory. The intention behind these are: - -1. HISTOGRAM: The intended output is a distribution summary, specifically summarizing counts into non-overlapping ranges. -1. SUMMARY: This is a more generic way to request information about a distribution, perhaps represented in some vendor-specific way / not a histogram. - -## Example - -To declare a MeasureMetric, - -``` - myMetric := metric.NewMeasureMetric( - "ex.com/mymetric", - metric.WithAggregations(metric.SUM, metric.COUNT), - metric.WithLabelKeys(aKey, bKey)) -) -``` - -Here, we have declared a Measure-type metric with recommended SUM and COUNT aggregations (allowing to compute the average) with `aKey` and `bKey` as recommended aggregation dimensions. While the SDK has full control over which aggregations are actually performed, the programmer has specified a good default behavior for the implementation to use. - -## Trade-offs and mitigations - -This avoids requiring programmers to use the `view` API, which is an SDK API, not a user-facing instrumentation API. Letting the application programmer recommend aggregations directly gives the implementation more information about the raw statistics. Letting programmers declare their intent has few downsides, since there is a well-defined default behavior. - -## Prior art and alternatives - -Existing systems generally declare separate Metric types according to the desired aggregation. Raw statistics were invented to overcome this, and the present proposal brings back the ability to specify an Aggregation at the point where a Metric is defined. - -## Open questions - -There are questions about the value of the MIN and MAX aggregations. While they are simple to compute, they are difficult to use in practice. - -There are questions about the interpretation of HISTOGRAM and SUMMARY. The point of Raw statistics was that we shouldn't specify these aggregations because they are expensive and many implementations are possible. This is still true. What is the value in specifying HISTOGRAM as opposed to SUMMARY? How is SUMMARY different from MIN/MAX/COUNT/SUM, does it imply implementation-defined quantiles? diff --git a/oteps/0007-metric-handles.md b/oteps/0007-metric-handles.md new file mode 100644 index 000000000..a584b7cd6 --- /dev/null +++ b/oteps/0007-metric-handles.md @@ -0,0 +1,78 @@ +# Metric Handle API specification + +**Status:** `proposed` + +Specify the behavior of the Metrics API `Handle` type, for efficient repeated-use of metric instruments. + +## Motivation + +The specification currently names this concept `TimeSeries`, the object returned by `GetOrCreateTimeseries`, which supports binding a metric to a pre-defined set of labels for repeated use. This proposal renames these `Handle` and `GetHandle`, respectively, and adds further detail to the API specification for handles. + +## Explanation + +The `TimeSeries` is renamed to `Handle` as the former name suggests an implementation, not an API concept. `Handle`, we feel, is more descriptive of the intended use. Likewise with `GetOrCreateTimeSeries` to `GetHandle` and `GetDefaultTimeSeries` to `GetDefaultHandle`, these names suggest an implementation and not the intended use. + +Applications are encouraged to re-use metric handles for efficiency. + +Handles are useful to reduce the cost of repeatedly recording a metric instrument (cumulative, gauge, or measure) with a pre-defined set of label values. All metric kinds support declaring a set of required label keys. These label keys, by definition, must be specified in every metric `Handle`. We permit "unspecified" label values in cases where a handle is requested but a value was not provided. The default metric handle has all its required keys unspecified. We presume that fast pre-aggregation of metrics data is only possible, in general, when the pre-aggregation keys are a subset of the required keys on the metric. + +`GetHandle` specifies two calling patterns that may be supported: one with ordered label values and one without. The facility for ordered label values is provided as a potential optimization that facilitates a simple lookup for the SDK; in this form, the API is permitted to thrown an exception or return an error when there is a mismatch in the arguments to `GetHandle`. When label values are accepted in any order, some SDKs may perform an expensive lookup to find an existing metrics handle, but they must not throw exceptions. + +`GetHandle` and `GetDefaultHandle` support additional label values not required in the definition of the metric instrument. These optional labels act the same as pre-defined labels in the low-level metrics data representation, only that they are not required. Some SDKs may elect to use additional label values as the "attachment" data on metrics. + +## Internal details + +The names (`Handle`, `GetHandle`, ...) are just language-neutral recommendations. Because each of the metric kinds supports a different operation (`Add()`, `Set()`, and `Record()`), there are logically distinct kinds of handle. Language APIs should feel free to choose type and method names with attention to the language's style. + +An implementation of `GetHandle` may elect to return a unique object to multiple callers for its own purposes, but implementations are not required to do so. When unique objects are a guarantee, implementation should consider additional label values in the uniqueness of the handle, to maintain the low-level metric event respresentation discussed in RFC [0003-measure-metric-type](./0003-measure-metric-tuype.md). + +The `Observer` API for observing gauge metrics on demand via a callback does not support handles. + +## Trade-offs and mitigations + +The addition of additional label values, for handles, is not essential for pre-aggregation purposes, so it may be seen as non-essential in that regard. However, API support for pre-defined labels also benefits program readability because it allows metric handles to be defined once in the source, rather than once per call site. + +This benefit could be extended even further, as a potential future improvement. Instead of creating one handle per instance of a metric with pre-defined values, it may be even more efficient to support pre-defining a set of label values for use constructing multiple metric handles. Consider the code for declaring three metrics: + +``` + var gauge = metric.NewFloat64Gauge("example.com/gauge", metric.WithKeys("a", "b", "c")) + var counter = metric.NewFloat64Cumulative("example.com/counter", metric.WithKeys("a", "b", "c")) + var measure = metric.NewFloat64Measure("example.com/measure", metric.WithKeys("a", "b", "c")) +``` + +and three handles: + +``` + gaugeHandle := gauge.GetHandleOrdered(1, 2, 3) // values for a, b, c + counterHandle := counter.GetHandleOrdered(1, 2, 3) // values for a, b, c + measureHandle := measure.GetHandleOrdered(1, 2, 3) // values for a, b, c +``` + +This can be potentially improved as by making the label map a first-class concept. This has the potential to further reduce the cost of getting a group of handles with the same map of labels: + +``` + var commonKeys = metric.DefineKeys("a", "b", "c") + var gauge = metric.NewFloat64Gauge("example.com/gauge", metric.WithKeys(commonKeys)) + var counter = metric.NewFloat64Cumulative("example.com/counter", metric.WithKeys(commonKeys)) + var measure = metric.NewFloat64Measure("example.com/measure", metric.WithKeys(commonKeys)) + + labelMap := commonKeys.Values(1, 2, 3) // values for a, b, c + gaugeHandle := gauge.GetHandle(labelMap) + counterHandle := counter.GetHandle(labelMap) + measureHandle := measure.GetHandle(labelMap) +``` + +## Open questions + +Should the additional scope concept shown above be implemented? + +### Metric `Attachment` support + +OpenCensus has the notion of a metric attachment, allowing the application to include additional information associated with the event, for sampling purposes. The position taken here is that additional label values on the metric handle (specified here) or the context are a suitable replacement. + +## Issues addressed + +[Agreements reached on handles and naming in the working group convened on 8/21/2019](https://docs.google.com/document/d/1d0afxe3J6bQT-I6UbRXeIYNcTIyBQv4axfjKF4yvAPA/edit#). + +[`record` should take a generic `Attachment` class instead of having tracing dependency](https://github.com/open-telemetry/opentelemetry-specification/issues/144) +