diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 68563bd4928..94a7dc0e312 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -26,9 +26,9 @@ """ - from abc import ABC, abstractmethod -from typing import List +from enum import Enum +from typing import List, Union from opentelemetry.metrics.time_series import ( CounterTimeSeries, @@ -37,10 +37,8 @@ ) from opentelemetry.trace import SpanContext -LabelKeys = List["LabelKey"] -LabelValues = List[str] - +# pylint: disable=unused-argument class Meter: """An interface to allow the recording of metrics. @@ -49,160 +47,118 @@ class Meter: for the exported metric are deferred. """ - def create_float_counter( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "FloatCounter": - """Creates a counter type metric that contains float values. - - Args: - name: The name of the counter. - description: Human readable description of the metric. - unit: Unit of the metric values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: A new `FloatCounter` - """ - - def create_int_counter( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "IntCounter": - """Creates a counter type metric that contains int values. - - Args: - name: The name of the counter. - description: Human readable description of the metric. - unit: Unit of the metric values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: - A new `IntCounter` - """ - - def create_float_gauge( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "FloatGauge": - """Creates a gauge type metric that contains float values. - - Args: - name: The name of the counter. - description: Human readable description of the metric. - unit: Unit of the metric values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: - A new `FloatGauge` - """ - - def create_int_gauge( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "IntGauge": - """Creates a gauge type metric that contains int values. - - Args: - name: The name of the counter. - description: Human readable description of the metric. - unit: Unit of the metric values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: - A new `IntGauge` - """ + # TODO: RecordBatch + + +class ValueType(Enum): + FLOAT = 0 + INT = 1 + + +def create_counter( + name: str, + description: str, + unit: str, + value_type: "ValueType", + is_bidirectional: bool = False, + label_keys: List[str] = None, + span_context: SpanContext = None, +) -> Union["FloatCounter", "IntCounter"]: + """Creates a counter metric with type value_type. + + By default, counter values can only go up (unidirectional). The API + should reject negative inputs to unidirectional counter metrics. + Counter metrics have a bidirectional option to allow for negative + inputs. + + Args: + name: The name of the counter. + description: Human readable description of the metric. + unit: Unit of the metric values. + value_type: The type of values being recorded by the metric. + is_bidirectional: Set to true to allow negative inputs. + label_keys: list of keys for the labels with dynamic values. + Order of the list is important as the same order must be used + on recording when suppling values for these labels. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. + + Returns: A new counter metric for values of the given value_type. + """ - def create_int_measure( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "IntMeasure": - """Creates a measure used to record raw int values. - Args: - name: The name of the measure. - description: Human readable description of this measure. - unit: Unit of the measure values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: - A new `IntMeasure` - """ +def create_gauge( + name: str, + description: str, + unit: str, + value_type: "ValueType", + is_unidirectional: bool = False, + label_keys: List[str] = None, + span_context: SpanContext = None, +) -> Union["FloatGauge", "IntGauge"]: + """Creates a gauge metric with type value_type. + + By default, gauge values can go both up and down (bidirectional). The API + allows for an optional unidirectional flag, in which when set will reject + descending update values. + + Args: + name: The name of the gauge. + description: Human readable description of the metric. + unit: Unit of the metric values. + value_type: The type of values being recorded by the metric. + is_unidirectional: Set to true to reject negative inputs. + label_keys: list of keys for the labels with dynamic values. + Order of the list is important as the same order must be used + on recording when suppling values for these labels. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. + + Returns: A new gauge metric for values of the given value_type. + """ - def create_float_measure( - self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None, - ) -> "FloatMeasure": - """Creates a Measure used to record raw float values. - Args: - name: the name of the measure - description: Human readable description of this measure. - unit: Unit of the measure values. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order MUST be used - on recording when suppling values for these labels. - span_context: The `SpanContext` that identifies the `Span` - that the metric is associated with. - - Returns: - A new `FloatMeasure` - """ +def create_measure( + name: str, + description: str, + unit: str, + value_type: "ValueType", + is_non_negative: bool = False, + label_keys: List[str] = None, + span_context: SpanContext = None, +) -> Union["FloatMeasure", "IntMeasure"]: + """Creates a measure metric with type value_type. + + Measure metrics represent raw statistics that are recorded. As an option, + measure metrics can be declared as non-negative. The API will reject + negative metric events for non-negative measures. + + Args: + name: The name of the measure. + description: Human readable description of the metric. + unit: Unit of the metric values. + value_type: The type of values being recorded by the metric. + is_non_negative: Set to true to reject negative inputs. + label_keys: list of keys for the labels with dynamic values. + Order of the list is important as the same order must be used + on recording when suppling values for these labels. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. + + Returns: A new measure metric for values of the given value_type. + """ class Metric(ABC): """Base class for various types of metrics. Metric class that inherit from this class are specialized with the type of - time series that the metric holds. Metric is constructed from the meter. + time series that the metric holds. """ @abstractmethod - def get_or_create_time_series(self, label_values: LabelValues) -> "object": - """Gets and returns a timeseries, a container for a cumulative value. + def get_or_create_time_series(self, label_values: List[str]) -> "object": + """Gets a timeseries, used for repeated-use of metrics instruments. If the provided label values are not already associated with this metric, a new timeseries is returned, otherwise it returns the existing @@ -215,7 +171,7 @@ def get_or_create_time_series(self, label_values: LabelValues) -> "object": with the return timeseries. """ - def remove_time_series(self, label_values: LabelValues) -> None: + def remove_time_series(self, label_values: List[str]) -> None: """Removes the timeseries from the Metric, if present. The timeseries with matching label values will be removed. @@ -229,89 +185,54 @@ def clear(self) -> None: class FloatCounter(Metric): - """A counter type metric that holds float values. - - Cumulative values can go up or stay the same, but can never go down. - Cumulative values cannot be negative. - """ + """A counter type metric that holds float values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "CounterTimeSeries": - """Gets a `CounterTimeSeries` with a cumulative float value.""" + """Gets a `CounterTimeSeries` with a float value.""" class IntCounter(Metric): - """A counter type metric that holds int values. - - Cumulative values can go up or stay the same, but can never go down. - Cumulative values cannot be negative. - """ + """A counter type metric that holds int values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "CounterTimeSeries": - """Gets a `CounterTimeSeries` with a cumulative int value.""" + """Gets a `CounterTimeSeries` with an int value.""" class FloatGauge(Metric): - """A gauge type metric that holds float values. - - Cumulative value can go both up and down. Values can be negative. - """ + """A gauge type metric that holds float values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "GaugeTimeSeries": - """Gets a `GaugeTimeSeries` with a cumulative float value.""" + """Gets a `GaugeTimeSeries` with a float value.""" class IntGauge(Metric): - """A gauge type metric that holds int values. - - Cumulative value can go both up and down. Values can be negative. - """ + """A gauge type metric that holds int values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "GaugeTimeSeries": - """Gets a `GaugeTimeSeries` with a cumulative int value.""" + """Gets a `GaugeTimeSeries` with an int value.""" class FloatMeasure(Metric): - """A measure type metric that holds float values. - - Measure metrics represent raw statistics that are recorded. - """ + """A measure type metric that holds float values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "MeasureTimeSeries": - """Gets a `MeasureTimeSeries` with a cumulated float value.""" + """Gets a `MeasureTimeSeries` with a float value.""" class IntMeasure(Metric): - """A measure type metric that holds int values. - - Measure metrics represent raw statistics that are recorded. - """ + """A measure type metric that holds int values.""" def get_or_create_time_series( - self, label_values: LabelValues + self, label_values: List[str] ) -> "MeasureTimeSeries": - """Gets a `MeasureTimeSeries` with a cumulated int value.""" - - -class LabelKey: - """The label keys associated with the metric. - - :type key: str - :param key: the key for the label - - :type description: str - :param description: description of the label - """ - - def __init__(self, key: str, description: str) -> None: - self.key = key - self.description = description + """Gets a `MeasureTimeSeries` with an int value.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index c9c55f01b81..a07610b2ea9 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -13,24 +13,20 @@ # limitations under the License. # pylint: skip-file -from opentelemetry.metrics import LabelKey, LabelValue, Meter +from opentelemetry import metrics -METER = Meter() -LABEL_KEYS = [ - LabelKey("environment", "the environment the application is running in") -] +METER = metrics.Meter() COUNTER = METER.create_int_counter( - "sum numbers", # pragma: no cover + "sum numbers", "sum numbers over time", "number", - LABEL_KEYS, + metrics.ValueType.FLOAT, + ["environment"], ) -LABEL_VALUE_TESTING = [LabelValue("Testing")] -LABEL_VALUE_STAGING = [LabelValue("Staging")] # Metrics sent to some exporter -METRIC_TESTING = COUNTER.get_or_create_time_series(LABEL_VALUE_TESTING) -METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_STAGING) +METRIC_TESTING = COUNTER.get_or_create_time_series("Testing") +METRIC_STAGING = COUNTER.get_or_create_time_series("Staging") for i in range(100): METRIC_STAGING.add(i) diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 3c82e14d536..ada44da704b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -13,33 +13,21 @@ # limitations under the License. # pylint: skip-file -from opentelemetry.metrics import LabelKey, LabelValue, MeasureBatch, Meter -from opentelemetry.metrics.aggregation import LastValueAggregation +from opentelemetry import metrics -METER = Meter() -LABEL_KEYS = [ - LabelKey("environment", "the environment the application is running in") -] -MEASURE = METER.create_float_measure( +METER = metrics.Meter() +MEASURE = metrics.create_measure( "idle_cpu_percentage", "cpu idle over time", "percentage", - LABEL_KEYS, - LastValueAggregation, + metrics.ValueType.FLOAT, + ["environment"], ) -LABEL_VALUE_TESTING = [LabelValue("Testing")] -LABEL_VALUE_STAGING = [LabelValue("Staging")] # Metrics sent to some exporter -MEASURE_METRIC_TESTING = MEASURE.get_or_create_time_series(LABEL_VALUE_TESTING) -MEASURE_METRIC_STAGING = MEASURE.get_or_create_time_series(LABEL_VALUE_STAGING) +MEASURE_METRIC_TESTING = MEASURE.get_or_create_time_series("Testing") +MEASURE_METRIC_STAGING = MEASURE.get_or_create_time_series("Staging") # record individual measures STATISTIC = 100 MEASURE_METRIC_STAGING.record(STATISTIC) - -# record multiple observed values -BATCH = MeasureBatch() -BATCH.record( - [(MEASURE_METRIC_TESTING, STATISTIC), (MEASURE_METRIC_STAGING, STATISTIC)] -) diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/time_series.py index b14ef973ad4..25ad9efe775 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -17,12 +17,9 @@ class CounterTimeSeries: def add(self, value: typing.Union[float, int]) -> None: - """Adds the given value to the current value. Cannot be negative.""" + """Adds the given value to the current value. - def set(self, value: typing.Union[float, int]) -> None: - """Sets the current value to the given value. - - The given value must be larger than the current recorded value. + The input value cannot be negative if not bidirectional. """ @@ -33,7 +30,4 @@ def set(self, value: typing.Union[float, int]) -> None: class MeasureTimeSeries: def record(self, value: typing.Union[float, int]) -> None: - """Records the given value to this measure. - - Logic depends on type of aggregation used for this measure. - """ + """Records the given value to this measure."""