From 6ca42741178ac2eccf4718b1df78f211d6f94070 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 29 Jul 2019 15:16:38 -0700 Subject: [PATCH 01/88] Create functions Comments for Meter More comments Add more comments Fix typos --- .../distributedcontext/__init__.py | 4 + .../src/opentelemetry/metrics/__init__.py | 511 ++++++++++++++++++ 2 files changed, 515 insertions(+) diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py index d853a7bcf6..b2f19c0744 100644 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py +++ b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py @@ -11,3 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +# TODO +class DistributedContext: + pass diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index d853a7bcf6..a0a49c4ee7 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -11,3 +11,514 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +import typing + +from opentelemetry.context import SpanContext +from opentelemetry.distributedcontext import DistributedContext +from opentelemetry.resources import Resource + + +class Meter: + """An interface to allow the recording of measurements and metrics. + + :class:`Measurement`s are used for recording raw values, in which the + aggregation and labels for the exported metric are defered. This should be + used to record measurements like "server_latency" or "received_bytes", + where the value of interest is the recorded value itself. + + :class:`Metric`s are used for recording pre-defined aggregation, or already + aggregated data. This should be used to report metrics like cpu/memory + usage, in which the type of aggregation is already defined, or simple + metrics like "queue_length". + + Initialization of the :class:`Meter` is done through the `meter()` function + (see below). The meter instance has a singleton implementation. + """ + + def create_double_counter(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> CounterDouble: + """Creates a counter type metric that contains double values. + + Args: + name: The name of the counter. + options: An optional :class:`.MetricOptions` used to populate + details about the counter. + + Returns: A new :class:`.CounterDouble` + """ + + def create_long_counter(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> CounterLong: + """Creates a counter type metric that contains long values. + + Args: + name: The name of the counter. + options: An optional :class:`.MetricOptions` used to populate + details about the counter. + + Returns: + A new :class:`.CounterLong` + """ + + def create_double_gauge(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> GaugeDouble: + """Creates a gauge type metric that contains double values. + + Args: + name: The name of the gauge. + options: An optional :class:`.MetricOptions` used to populate + details about the gauge. + + Returns: + A new :class:`.GaugeDouble` + """ + + def create_long_gauge(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> GaugeLong: + """Creates a gauge type metric that contains long values. + + Args: + name: The name of the gauge. + options: An optional :class:`.MetricOptions` used to populate + details about the gauge. + + Returns: + A new :class:`.GaugeLong` + """ + + def create_measure(self, + name: str, + options: typing.Optional[MeasureOptions] = None) \ + -> Measure: + """Creates a Measure used to record raw :class:`.Measurement`s. + + Args: + name: the name of the measure + options: An optional :class:`.MeasureOptions` used to populate + details about the measure + + Returns: + A :class:`.Measure` + """ + + def record(self, + measurements: typing.List[Measurement], + options: typing.Optional[RecordOptions] = None) -> None: + """A function use to record a set of :class:`.Measurement`s. + + The API is built with the idea that measurement aggregation will occur + asynchronously. Typical library records multiple measurements at once, + so this function accepts a collection of measurements so the library + can batch all of them that need to be recorded. + + Args: + measurements: The collection of measurements to record. options: + An optional :class:`.RecordOptions` used to populate details during + recording. + + Returns: None + """ + +_METER: typing.Optional[Meter] = None +_METER_FACTORY: typing.Optional[ + typing.Callable[[typing.Type[Meter]], typing.Optional[Meter]]] = None + + +def meter() -> Meter: + """Gets the current global :class:`.Meter` object. + + If there isn't one set yet, a default will be loaded. + """ + global _METER, _METER_FACTORY # pylint:disable=global-statement + + if _METER is None: + # pylint:disable=protected-access + _METER = loader._load_impl(Meter, _METER_FACTORY) + del _METER_FACTORY + + return _METER + + +def set_preferred_meter_implementation( + factory: typing.Callable[ + [typing.Type[Meter]], typing.Optional[Meter]] + ) -> None: + """Set the factory to be used to create the :class:`.Meter`. + + See :mod:`opentelemetry.loader` for details. + + This function may not be called after a meter is already loaded. + + Args: + factory: Callback that should create a new :class:`.Meter` instance. + """ + global _METER_FACTORY # pylint:disable=global-statement + + if _METER: + raise RuntimeError("Meter already loaded.") + + _METER_FACTORY = factory + + +class MetricOptions: + def __init__(self, + description: str, + unit: str, + label_keys: typing.List[LabelKey], + contstant_labels: typing.Dict[LabelKey, LabelValue], + component: str, + resource: Resource) -> MetricOptions: + """Optional info used when creating a :class:`.Metric`. + + Args: + 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 + label_values: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this + Metric. + """ + self.description = description + self.unit = unit + self.label_keys = label_keys + self.label_values = label_values + self.component = component + self.resource = resource + + +class LabelKey: + + def __init__(self, + key: str, + description: str) -> LabelKey: + self.key = key + self.description = description + + +class LabelValue: + + def __init__(self, + value: str) -> LabelValue: + self.value = value + + +class MeasureOptions: + def __init__(self, + description: str, + unit: str, + measure_type: MeasureType = MeasureType.DOUBLE) -> MeasureOptions: + """Optional info used when creating a :class:`.Measure`. + + Args: + description: Human readable description of this measure. + unit: Unit of the measure values. + measure_type: Type of the measure. Can be one of two values - + `LONG` and `DOUBLE`. Default type is `DOUBLE`. + """ + self.description = description + self.unit = unit + self.measure_type = measure_type + + +class MeasureType(int): + DOUBLE = 0 + LONG = 1 + + +class RecordOptions: + + def __init__(self, + distributed_context: DistributedContext, + span_context: SpanContext) -> RecordOptions: + """Optional info used when recording :class:`.Measurement`s. + + Args: + distributed_context: Explicit :class:`.DistributedContext` to use + instead of the current context. Context is used to add dimensions + for the resulting metric calculated out of the provided + measurements. + span_context: the :class:`.SpanContext` that identified the + :class:`.Span` for which the measurements are associated with. + """ + + +class Measure: + + def __init(self, + name: str, + options: typing.Optional[RecordOptions] = None) -> Measure: + """Used to create raw :class:`.Measurement`s. + + A contract between the API exposing the raw measurement and SDK + aggregating these values into the :class:`.Metric`. Measure is + constructed from the :class:`.Meter` class by providing a set of + :class:`.MeasureOptions`. + """ + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.measure_type = options.measure_type + + def create_double_measurement(self, + value: float) -> Measurement: + """Creates a measurement that contains double values. + + Args: + value: The value of the measurement. + + Returns: + A new :class:`.Measurement` + """ + + def create_long_measurement(self, + value: long) -> Measurement: + """Creates a measurement that contains long values. + + Args: + value: The value of the measurement. + + Returns: + A new :class:`.Measurement` + """ + + +class Measurement: + """An empty interface that represents a single value. + + This single value is recorded for the :class:`.Measure` that created + this measurement. + """ + + +class Metric: + """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 + :class:`.Meter` class, by providing a set of :class:`.MetricOptions`. + """ + + def get_or_create_time_series(self, + label_values: typing.List[LabelValue]) \ + -> None: + """Gets and returns a `TimeSeries`, a container for a cumulative value. + + If the provided label values are not already associated with this + metric, a new timeseries is returned, otherwise it returns the existing + timeseries with the exact label values. The timeseries returned + contains logic and behaviour specific to the type of metric that + overrides this function. + + Args: + label_values: A map of :class:`.LabelValue`s that will be + associated with the return timeseries. + """ + raise NotImplementedError + + def get_default_time_series(self) -> None: + """Returns a `TimeSeries`, a container for a cumulative value. + + The timeseries will have all its labels not set (default). + """ + raise NotImplementedError + + def set_call_back(self, updater_function: typing.Callable) -> None: + """Sets a callback that gets executed every time prior to exporting. + + This function MUST set the value of the :class:`.Metric` to the + value that will be exported. + + args: + updater_function: The callback function to execute. + """ + + def remove_time_series(self, + label_values: typing.List[LabelValue]) -> None: + """Removes the `TimeSeries` from the :class:`.Metric`, if it is present. + + The timeseries with matching :class:`.LabelValue`s will be removed. + + args: + label_values: The list of label values to match against. + """ + + def clear(self) -> None: + """Removes all `TimeSeries` from the :class:`.Metric`.""" + + +class CounterDouble(Metric): + + def __init__(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> CounterDouble: + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + + def get_or_create_time_series(self, + label_values: typing.List[LabelValue]) \ + -> TimeSeries: + """Gets and returns a `TimeSeries`, for a `CounterDouble` metric.""" + + def get_default_time_series(self) -> TimeSeries: + """Returns a `TimeSeries`, for a `CounterDouble` metric.""" + + + class TimeSeries: + + def add(self, value: float) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + def set(self, value: float) -> None: + """Sets the current value to the given value. + + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ + + +class CounterLong(Metric): + + def __init__(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> CounterLong: + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + + def get_or_create_time_series(self, + label_values: typing.List[LabelValue]) \ + -> TimeSeries: + """Gets and returns a `TimeSeries`, for a `CounterLong` metric.""" + + def get_default_time_series(self) -> 'TimeSeries': + """Returns a `TimeSeries`, for a `CounterLong` metric.""" + + + class TimeSeries: + + def add(self, value: float) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + def set(self, value: float) -> None: + """Sets the current value to the given value. + + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ + + +class GaugeDouble(Metric): + + def __init__(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> GaugeDouble: + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + + def get_or_create_time_series(self, + label_values: typing.List[LabelValue]) \ + -> TimeSeries: + """Gets and returns a `TimeSeries`, for a `GaugeDouble` metric.""" + + def get_default_time_series(self) -> TimeSeries: + """Returns a `TimeSeries`, for a `GaugeDouble` metric.""" + + + class TimeSeries: + + def add(self, value: float) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + def set(self, value: float) -> None: + """Sets the current value to the given value. + + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ + + +class GaugeLong(Metric): + + def __init__(self, + name: str, + options: typing.Optional[MetricOptions] = None) \ + -> GaugeLong: + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + + def get_or_create_time_series(self, + label_values: typing.List[LabelValue]) \ + -> TimeSeries: + """Gets and returns a `TimeSeries`, for a `GaugeLong` metric.""" + + def get_default_time_series(self) -> TimeSeries: + """Returns a `TimeSeries`, for a `GaugeLong` metric.""" + + + class TimeSeries: + + def add(self, value: float) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + def set(self, value: float) -> None: + """Sets the current value to the given value. + + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ From b23cec1e3bf246b99dd2873e614f6afcdbf496dd Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 30 Jul 2019 17:08:01 -0700 Subject: [PATCH 02/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 759 +++++++++--------- 1 file changed, 379 insertions(+), 380 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index a0a49c4ee7..97b8336378 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -14,138 +14,140 @@ import typing +from opentelemetry import loader from opentelemetry.context import SpanContext from opentelemetry.distributedcontext import DistributedContext from opentelemetry.resources import Resource class Meter: - """An interface to allow the recording of measurements and metrics. + """An interface to allow the recording of measurements and metrics. - :class:`Measurement`s are used for recording raw values, in which the - aggregation and labels for the exported metric are defered. This should be - used to record measurements like "server_latency" or "received_bytes", - where the value of interest is the recorded value itself. + :class:`Measurement`s are used for recording raw values, in which the + aggregation and labels for the exported metric are defered. This should be + used to record measurements like "server_latency" or "received_bytes", + where the value of interest is the recorded value itself. - :class:`Metric`s are used for recording pre-defined aggregation, or already - aggregated data. This should be used to report metrics like cpu/memory - usage, in which the type of aggregation is already defined, or simple - metrics like "queue_length". + :class:`Metric`s are used for recording pre-defined aggregation, or already + aggregated data. This should be used to report metrics like cpu/memory + usage, in which the type of aggregation is already defined, or simple + metrics like "queue_length". - Initialization of the :class:`Meter` is done through the `meter()` function - (see below). The meter instance has a singleton implementation. - """ + Initialization of the :class:`Meter` is done through the `meter()` function + (see below). The meter instance has a singleton implementation. + """ - def create_double_counter(self, + def create_double_counter(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'CounterDouble': + """Creates a counter type metric that contains double values. + + Args: + name: The name of the counter. + options: An optional :class:`.MetricOptions` used to populate + details about the counter. + + Returns: A new :class:`.CounterDouble` + """ + + def create_long_counter(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'CounterLong': + """Creates a counter type metric that contains long values. + + Args: + name: The name of the counter. + options: An optional :class:`.MetricOptions` used to populate + details about the counter. + + Returns: + A new :class:`.CounterLong` + """ + + def create_double_gauge(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'GaugeDouble': + """Creates a gauge type metric that contains double values. + + Args: + name: The name of the gauge. + options: An optional :class:`.MetricOptions` used to populate + details about the gauge. + + Returns: + A new :class:`.GaugeDouble` + """ + + def create_long_gauge(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'GaugeLong': + """Creates a gauge type metric that contains long values. + + Args: + name: The name of the gauge. + options: An optional :class:`.MetricOptions` used to populate + details about the gauge. + + Returns: + A new :class:`.GaugeLong` + """ + + def create_measure(self, name: str, - options: typing.Optional[MetricOptions] = None) \ - -> CounterDouble: - """Creates a counter type metric that contains double values. + options: typing.Optional['MeasureOptions'] = None + ) -> 'Measure': + """Creates a Measure used to record raw :class:`.Measurement`s. - Args: - name: The name of the counter. - options: An optional :class:`.MetricOptions` used to populate - details about the counter. + Args: + name: the name of the measure + options: An optional :class:`.MeasureOptions` used to populate + details about the measure - Returns: A new :class:`.CounterDouble` - """ + Returns: + A :class:`.Measure` + """ + + def record(self, + measurements: typing.List['Measurement'], + options: typing.Optional['RecordOptions'] = None) -> None: + """A function use to record a set of :class:`.Measurement`s. + + The API is built with the idea that measurement aggregation will occur + asynchronously. Typical library records multiple measurements at once, + so this function accepts a collection of measurements so the library + can batch all of them that need to be recorded. + + Args: + measurements: The collection of measurements to record. options: + An optional :class:`.RecordOptions` used to populate details during + recording. + + Returns: None + """ - def create_long_counter(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> CounterLong: - """Creates a counter type metric that contains long values. - - Args: - name: The name of the counter. - options: An optional :class:`.MetricOptions` used to populate - details about the counter. - - Returns: - A new :class:`.CounterLong` - """ - - def create_double_gauge(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> GaugeDouble: - """Creates a gauge type metric that contains double values. - - Args: - name: The name of the gauge. - options: An optional :class:`.MetricOptions` used to populate - details about the gauge. - - Returns: - A new :class:`.GaugeDouble` - """ - - def create_long_gauge(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> GaugeLong: - """Creates a gauge type metric that contains long values. - - Args: - name: The name of the gauge. - options: An optional :class:`.MetricOptions` used to populate - details about the gauge. - - Returns: - A new :class:`.GaugeLong` - """ - - def create_measure(self, - name: str, - options: typing.Optional[MeasureOptions] = None) \ - -> Measure: - """Creates a Measure used to record raw :class:`.Measurement`s. - - Args: - name: the name of the measure - options: An optional :class:`.MeasureOptions` used to populate - details about the measure - - Returns: - A :class:`.Measure` - """ - - def record(self, - measurements: typing.List[Measurement], - options: typing.Optional[RecordOptions] = None) -> None: - """A function use to record a set of :class:`.Measurement`s. - - The API is built with the idea that measurement aggregation will occur - asynchronously. Typical library records multiple measurements at once, - so this function accepts a collection of measurements so the library - can batch all of them that need to be recorded. - - Args: - measurements: The collection of measurements to record. options: - An optional :class:`.RecordOptions` used to populate details during - recording. - - Returns: None - """ _METER: typing.Optional[Meter] = None _METER_FACTORY: typing.Optional[ - typing.Callable[[typing.Type[Meter]], typing.Optional[Meter]]] = None + typing.Callable[[typing.Type[Meter]], typing.Optional[Meter]]] = None def meter() -> Meter: - """Gets the current global :class:`.Meter` object. + """Gets the current global :class:`.Meter` object. If there isn't one set yet, a default will be loaded. """ - global _METER, _METER_FACTORY # pylint:disable=global-statement + global _METER, _METER_FACTORY # pylint:disable=global-statement - if _METER is None: + if _METER is None: # pylint:disable=protected-access - _METER = loader._load_impl(Meter, _METER_FACTORY) - del _METER_FACTORY + _METER = loader._load_impl(Meter, _METER_FACTORY) + del _METER_FACTORY - return _METER + return _METER def set_preferred_meter_implementation( @@ -169,356 +171,353 @@ def set_preferred_meter_implementation( _METER_FACTORY = factory -class MetricOptions: - def __init__(self, - description: str, - unit: str, - label_keys: typing.List[LabelKey], - contstant_labels: typing.Dict[LabelKey, LabelValue], - component: str, - resource: Resource) -> MetricOptions: - """Optional info used when creating a :class:`.Metric`. - - Args: - 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 - label_values: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this - Metric. - """ - self.description = description - self.unit = unit - self.label_keys = label_keys - self.label_values = label_values - self.component = component - self.resource = resource - - class LabelKey: - def __init__(self, - key: str, - description: str) -> LabelKey: - self.key = key - self.description = description + def __init__(self, + key: str, + description: str) -> 'LabelKey': + self.key = key + self.description = description class LabelValue: - def __init__(self, - value: str) -> LabelValue: - self.value = value + def __init__(self, + value: str) -> 'LabelValue': + self.value = value -class MeasureOptions: - def __init__(self, - description: str, - unit: str, - measure_type: MeasureType = MeasureType.DOUBLE) -> MeasureOptions: - """Optional info used when creating a :class:`.Measure`. - - Args: - description: Human readable description of this measure. - unit: Unit of the measure values. - measure_type: Type of the measure. Can be one of two values - - `LONG` and `DOUBLE`. Default type is `DOUBLE`. - """ - self.description = description - self.unit = unit - self.measure_type = measure_type +class MetricOptions: + def __init__(self, + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: typing.Dict['LabelKey', 'LabelValue'], + component: str, + resource: 'Resource') -> 'MetricOptions': + """Optional info used when creating a :class:`.Metric`. + + Args: + 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 + constant_labels: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this + Metric. + """ + self.description = description + self.unit = unit + self.label_keys = label_keys + self.constant_labels = constant_labels + self.component = component + self.resource = resource class MeasureType(int): - DOUBLE = 0 - LONG = 1 + DOUBLE = 0 + LONG = 1 + + +class MeasureOptions: + def __init__(self, + description: str, + unit: str, + measure_type: 'MeasureType' = MeasureType.DOUBLE + ) -> 'MeasureOptions': + """Optional info used when creating a :class:`.Measure`. + + Args: + description: Human readable description of this measure. + unit: Unit of the measure values. + measure_type: Type of the measure. Can be one of two values - + `LONG` and `DOUBLE`. Default type is `DOUBLE`. + """ + self.description = description + self.unit = unit + self.measure_type = measure_type class RecordOptions: - def __init__(self, - distributed_context: DistributedContext, - span_context: SpanContext) -> RecordOptions: - """Optional info used when recording :class:`.Measurement`s. - - Args: - distributed_context: Explicit :class:`.DistributedContext` to use - instead of the current context. Context is used to add dimensions - for the resulting metric calculated out of the provided - measurements. - span_context: the :class:`.SpanContext` that identified the - :class:`.Span` for which the measurements are associated with. - """ + def __init__(self, + distributed_context: 'DistributedContext', + span_context: 'SpanContext') -> 'RecordOptions': + """Optional info used when recording :class:`.Measurement`s. + Args: + distributed_context: Explicit :class:`.DistributedContext` to use + instead of the current context. Context is used to add dimensions + for the resulting metric calculated out of the provided + measurements. + span_context: the :class:`.SpanContext` that identified the + :class:`.Span` for which the measurements are associated with. + """ -class Measure: - def __init(self, - name: str, - options: typing.Optional[RecordOptions] = None) -> Measure: - """Used to create raw :class:`.Measurement`s. +class Measurement: + """An empty interface that represents a single value. + + This single value is recorded for the :class:`.Measure` that created + this measurement. + """ - A contract between the API exposing the raw measurement and SDK - aggregating these values into the :class:`.Metric`. Measure is - constructed from the :class:`.Meter` class by providing a set of - :class:`.MeasureOptions`. - """ - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.measure_type = options.measure_type - def create_double_measurement(self, - value: float) -> Measurement: - """Creates a measurement that contains double values. +class Measure: - Args: - value: The value of the measurement. + def __init(self, + name: str, + options: typing.Optional[RecordOptions] = None) -> 'Measure': + """Used to create raw :class:`.Measurement`s. - Returns: - A new :class:`.Measurement` - """ + A contract between the API exposing the raw measurement and SDK + aggregating these values into the :class:`.Metric`. Measure is + constructed from the :class:`.Meter` class by providing a set of + :class:`.MeasureOptions`. + """ + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.measure_type = options.measure_type - def create_long_measurement(self, - value: long) -> Measurement: - """Creates a measurement that contains long values. + def create_double_measurement(self, + value: float) -> 'Measurement': + """Creates a measurement that contains double values. - Args: - value: The value of the measurement. + Args: + value: The value of the measurement. - Returns: - A new :class:`.Measurement` - """ + Returns: + A new :class:`.Measurement` + """ + def create_long_measurement(self, + value: int) -> 'Measurement': + """Creates a measurement that contains long values. -class Measurement: - """An empty interface that represents a single value. + Args: + value: The value of the measurement. - This single value is recorded for the :class:`.Measure` that created - this measurement. - """ + Returns: + A new :class:`.Measurement` + """ class Metric: - """Base class for various types of metrics. + """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 - :class:`.Meter` class, by providing a set of :class:`.MetricOptions`. - """ + Metric class that inherit from this class are specialized with the type of + time series that the metric holds. Metric is constructed from the + :class:`.Meter` class, by providing a set of :class:`.MetricOptions`. + """ - def get_or_create_time_series(self, - label_values: typing.List[LabelValue]) \ - -> None: - """Gets and returns a `TimeSeries`, a container for a cumulative value. + def get_or_create_time_series(self, + label_values: typing.List['LabelValue'] + ) -> None: + """Gets and returns a `TimeSeries`, a container for a cumulative value. - If the provided label values are not already associated with this - metric, a new timeseries is returned, otherwise it returns the existing - timeseries with the exact label values. The timeseries returned - contains logic and behaviour specific to the type of metric that - overrides this function. + If the provided label values are not already associated with this + metric, a new timeseries is returned, otherwise it returns the existing + timeseries with the exact label values. The timeseries returned + contains logic and behaviour specific to the type of metric that + overrides this function. - Args: - label_values: A map of :class:`.LabelValue`s that will be - associated with the return timeseries. - """ - raise NotImplementedError + Args: + label_values: A map of :class:`.LabelValue`s that will be + associated with the return timeseries. + """ + raise NotImplementedError - def get_default_time_series(self) -> None: - """Returns a `TimeSeries`, a container for a cumulative value. + def get_default_time_series(self) -> None: + """Returns a `TimeSeries`, a container for a cumulative value. - The timeseries will have all its labels not set (default). - """ - raise NotImplementedError + The timeseries will have all its labels not set (default). + """ + raise NotImplementedError - def set_call_back(self, updater_function: typing.Callable) -> None: - """Sets a callback that gets executed every time prior to exporting. + def set_call_back(self, updater_function: typing.Callable) -> None: + """Sets a callback that gets executed every time prior to exporting. - This function MUST set the value of the :class:`.Metric` to the - value that will be exported. + This function MUST set the value of the :class:`.Metric` to the + value that will be exported. - args: - updater_function: The callback function to execute. - """ - - def remove_time_series(self, - label_values: typing.List[LabelValue]) -> None: - """Removes the `TimeSeries` from the :class:`.Metric`, if it is present. + args: + updater_function: The callback function to execute. + """ - The timeseries with matching :class:`.LabelValue`s will be removed. + def remove_time_series(self, + label_values: typing.List['LabelValue']) -> None: + """Removes the `TimeSeries` from the :class:`.Metric`, if it is present. - args: - label_values: The list of label values to match against. - """ + The timeseries with matching :class:`.LabelValue`s will be removed. - def clear(self) -> None: - """Removes all `TimeSeries` from the :class:`.Metric`.""" - + args: + label_values: The list of label values to match against. + """ -class CounterDouble(Metric): + def clear(self) -> None: + """Removes all `TimeSeries` from the :class:`.Metric`.""" - def __init__(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> CounterDouble: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.label_values = options.label_values - self.component = options.component - self.resource = options.resource - def get_or_create_time_series(self, - label_values: typing.List[LabelValue]) \ - -> TimeSeries: - """Gets and returns a `TimeSeries`, for a `CounterDouble` metric.""" +class CounterDouble(Metric): - def get_default_time_series(self) -> TimeSeries: - """Returns a `TimeSeries`, for a `CounterDouble` metric.""" + def __init__(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'CounterDouble': + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + def get_or_create_time_series(self, + label_values: typing.List['LabelValue'] + ) -> 'CounterDouble.TimeSeries': + """Gets and returns a `TimeSeries`, for a `CounterDouble` metric.""" - class TimeSeries: + def get_default_time_series(self) -> 'CounterDouble.TimeSeries': + """Returns a `TimeSeries`, for a `CounterDouble` metric.""" - def add(self, value: float) -> None: - """Adds the given value to the current value. + class TimeSeries: - The values cannot be negative. - """ + def add(self, value: float) -> None: + """Adds the given value to the current value. - def set(self, value: float) -> None: - """Sets the current value to the given value. + The values cannot be negative. + """ - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + def set(self, value: float) -> None: + """Sets the current value to the given value. + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ -class CounterLong(Metric): - def __init__(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> CounterLong: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.label_values = options.label_values - self.component = options.component - self.resource = options.resource +class CounterLong(Metric): - def get_or_create_time_series(self, - label_values: typing.List[LabelValue]) \ - -> TimeSeries: - """Gets and returns a `TimeSeries`, for a `CounterLong` metric.""" + def __init__(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'CounterLong': + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource - def get_default_time_series(self) -> 'TimeSeries': - """Returns a `TimeSeries`, for a `CounterLong` metric.""" + def get_or_create_time_series(self, + label_values: typing.List['LabelValue'] + ) -> 'CounterLong.TimeSeries': + """Gets and returns a `TimeSeries`, for a `CounterLong` metric.""" + def get_default_time_series(self) -> 'CounterLong.TimeSeries': + """Returns a `TimeSeries`, for a `CounterLong` metric.""" - class TimeSeries: + class TimeSeries: - def add(self, value: float) -> None: - """Adds the given value to the current value. + def add(self, value: float) -> None: + """Adds the given value to the current value. - The values cannot be negative. - """ + The values cannot be negative. + """ - def set(self, value: float) -> None: - """Sets the current value to the given value. + def set(self, value: float) -> None: + """Sets the current value to the given value. - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ class GaugeDouble(Metric): - def __init__(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> GaugeDouble: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.label_values = options.label_values - self.component = options.component - self.resource = options.resource - - def get_or_create_time_series(self, - label_values: typing.List[LabelValue]) \ - -> TimeSeries: - """Gets and returns a `TimeSeries`, for a `GaugeDouble` metric.""" + def __init__(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'GaugeDouble': + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource - def get_default_time_series(self) -> TimeSeries: - """Returns a `TimeSeries`, for a `GaugeDouble` metric.""" + def get_or_create_time_series(self, + label_values: typing.List['LabelValue'] + ) -> 'GaugeDouble.TimeSeries': + """Gets and returns a `TimeSeries`, for a `GaugeDouble` metric.""" + def get_default_time_series(self) -> 'GaugeDouble.TimeSeries': + """Returns a `TimeSeries`, for a `GaugeDouble` metric.""" - class TimeSeries: + class TimeSeries: - def add(self, value: float) -> None: - """Adds the given value to the current value. + def add(self, value: float) -> None: + """Adds the given value to the current value. - The values cannot be negative. - """ + The values cannot be negative. + """ - def set(self, value: float) -> None: - """Sets the current value to the given value. + def set(self, value: float) -> None: + """Sets the current value to the given value. - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ class GaugeLong(Metric): - def __init__(self, - name: str, - options: typing.Optional[MetricOptions] = None) \ - -> GaugeLong: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.label_values = options.label_values - self.component = options.component - self.resource = options.resource - - def get_or_create_time_series(self, - label_values: typing.List[LabelValue]) \ - -> TimeSeries: - """Gets and returns a `TimeSeries`, for a `GaugeLong` metric.""" - - def get_default_time_series(self) -> TimeSeries: - """Returns a `TimeSeries`, for a `GaugeLong` metric.""" - - - class TimeSeries: - - def add(self, value: float) -> None: - """Adds the given value to the current value. - - The values cannot be negative. - """ - - def set(self, value: float) -> None: - """Sets the current value to the given value. - - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + def __init__(self, + name: str, + options: typing.Optional['MetricOptions'] = None + ) -> 'GaugeLong': + self.name = name + if options: + self.description = options.description + self.unit = options.unit + self.label_keys = options.label_keys + self.label_values = options.label_values + self.component = options.component + self.resource = options.resource + + def get_or_create_time_series(self, + label_values: typing.List['LabelValue'] + ) -> 'GaugeLong.TimeSeries': + """Gets and returns a `TimeSeries`, for a `GaugeLong` metric.""" + + def get_default_time_series(self) -> 'GaugeLong.TimeSeries': + """Returns a `TimeSeries`, for a `GaugeLong` metric.""" + + class TimeSeries: + + def add(self, value: float) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + def set(self, value: float) -> None: + """Sets the current value to the given value. + + The given value must be larger than the current recorded value. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ From 981eecea1ad1a1360acfa5dbf238471c5018b284 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 30 Jul 2019 17:29:42 -0700 Subject: [PATCH 03/88] Fix lint --- .../src/opentelemetry/metrics/__init__.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 97b8336378..579788034f 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -15,7 +15,7 @@ import typing from opentelemetry import loader -from opentelemetry.context import SpanContext +from opentelemetry.trace import SpanContext from opentelemetry.distributedcontext import DistributedContext from opentelemetry.resources import Resource @@ -175,7 +175,7 @@ class LabelKey: def __init__(self, key: str, - description: str) -> 'LabelKey': + description: str) -> None: self.key = key self.description = description @@ -183,7 +183,7 @@ def __init__(self, class LabelValue: def __init__(self, - value: str) -> 'LabelValue': + value: str) -> None: self.value = value @@ -194,7 +194,7 @@ def __init__(self, label_keys: typing.List['LabelKey'], constant_labels: typing.Dict['LabelKey', 'LabelValue'], component: str, - resource: 'Resource') -> 'MetricOptions': + resource: 'Resource') -> None: """Optional info used when creating a :class:`.Metric`. Args: @@ -217,7 +217,7 @@ def __init__(self, self.resource = resource -class MeasureType(int): +class MeasureType: DOUBLE = 0 LONG = 1 @@ -227,7 +227,7 @@ def __init__(self, description: str, unit: str, measure_type: 'MeasureType' = MeasureType.DOUBLE - ) -> 'MeasureOptions': + ) -> None: """Optional info used when creating a :class:`.Measure`. Args: @@ -245,7 +245,7 @@ class RecordOptions: def __init__(self, distributed_context: 'DistributedContext', - span_context: 'SpanContext') -> 'RecordOptions': + span_context: 'SpanContext') -> None: """Optional info used when recording :class:`.Measurement`s. Args: @@ -268,9 +268,9 @@ class Measurement: class Measure: - def __init(self, - name: str, - options: typing.Optional[RecordOptions] = None) -> 'Measure': + def __init__(self, + name: str, + options: typing.Optional['MeasureOptions'] = None) -> None: """Used to create raw :class:`.Measurement`s. A contract between the API exposing the raw measurement and SDK @@ -317,7 +317,7 @@ class Metric: def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> None: + ) -> 'object': """Gets and returns a `TimeSeries`, a container for a cumulative value. If the provided label values are not already associated with this @@ -332,7 +332,7 @@ def get_or_create_time_series(self, """ raise NotImplementedError - def get_default_time_series(self) -> None: + def get_default_time_series(self) -> 'object': """Returns a `TimeSeries`, a container for a cumulative value. The timeseries will have all its labels not set (default). @@ -351,7 +351,7 @@ def set_call_back(self, updater_function: typing.Callable) -> None: def remove_time_series(self, label_values: typing.List['LabelValue']) -> None: - """Removes the `TimeSeries` from the :class:`.Metric`, if it is present. + """Removes the `TimeSeries` from the :class:`.Metric`, if present. The timeseries with matching :class:`.LabelValue`s will be removed. @@ -368,13 +368,13 @@ class CounterDouble(Metric): def __init__(self, name: str, options: typing.Optional['MetricOptions'] = None - ) -> 'CounterDouble': + ) -> None: self.name = name if options: self.description = options.description self.unit = options.unit self.label_keys = options.label_keys - self.label_values = options.label_values + self.constant_labels = options.constant_labels self.component = options.component self.resource = options.resource @@ -408,13 +408,13 @@ class CounterLong(Metric): def __init__(self, name: str, options: typing.Optional['MetricOptions'] = None - ) -> 'CounterLong': + ) -> None: self.name = name if options: self.description = options.description self.unit = options.unit self.label_keys = options.label_keys - self.label_values = options.label_values + self.constant_labels = options.constant_labels self.component = options.component self.resource = options.resource @@ -448,13 +448,13 @@ class GaugeDouble(Metric): def __init__(self, name: str, options: typing.Optional['MetricOptions'] = None - ) -> 'GaugeDouble': + ) -> None: self.name = name if options: self.description = options.description self.unit = options.unit self.label_keys = options.label_keys - self.label_values = options.label_values + self.constant_labels = options.constant_labels self.component = options.component self.resource = options.resource @@ -488,13 +488,13 @@ class GaugeLong(Metric): def __init__(self, name: str, options: typing.Optional['MetricOptions'] = None - ) -> 'GaugeLong': + ) -> None: self.name = name if options: self.description = options.description self.unit = options.unit self.label_keys = options.label_keys - self.label_values = options.label_values + self.constant_labels = options.constant_labels self.component = options.component self.resource = options.resource From 8ea9709a7774d6a41b2f0c0b0a2f8a282749fc0d Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 30 Jul 2019 17:43:05 -0700 Subject: [PATCH 04/88] fix typing --- .../src/opentelemetry/distributedcontext/__init__.py | 1 + opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py index b2f19c0744..482f81e67c 100644 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py +++ b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + # TODO class DistributedContext: pass diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 579788034f..3ad216542e 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -339,7 +339,7 @@ def get_default_time_series(self) -> 'object': """ raise NotImplementedError - def set_call_back(self, updater_function: typing.Callable) -> None: + def set_call_back(self, updater_function: typing.Callable[..., None]) -> None: """Sets a callback that gets executed every time prior to exporting. This function MUST set the value of the :class:`.Metric` to the From 00b4f114e70a9006e98034358557cd90a3c9dc64 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 12:38:39 -0700 Subject: [PATCH 05/88] Remove options, constructors, seperate labels --- .../src/opentelemetry/metrics/__init__.py | 395 ++++++------------ .../src/opentelemetry/metrics/label_key.py | 30 ++ .../src/opentelemetry/metrics/label_value.py | 25 ++ 3 files changed, 192 insertions(+), 258 deletions(-) create mode 100644 opentelemetry-api/src/opentelemetry/metrics/label_key.py create mode 100644 opentelemetry-api/src/opentelemetry/metrics/label_value.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3ad216542e..2be7bb9c3b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -15,9 +15,11 @@ import typing from opentelemetry import loader -from opentelemetry.trace import SpanContext from opentelemetry.distributedcontext import DistributedContext +from opentelemetry.metrics.label_key import LabelKey +from opentelemetry.metrics.label_key import LabelValue from opentelemetry.resources import Resource +from opentelemetry.trace import SpanContext class Meter: @@ -32,35 +34,58 @@ class Meter: aggregated data. This should be used to report metrics like cpu/memory usage, in which the type of aggregation is already defined, or simple metrics like "queue_length". - - Initialization of the :class:`Meter` is done through the `meter()` function - (see below). The meter instance has a singleton implementation. """ def create_double_counter(self, name: str, - options: typing.Optional['MetricOptions'] = None + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None ) -> 'CounterDouble': """Creates a counter type metric that contains double values. Args: name: The name of the counter. - options: An optional :class:`.MetricOptions` used to populate - details about 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 + constant_labels: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this metric. Returns: A new :class:`.CounterDouble` """ def create_long_counter(self, name: str, - options: typing.Optional['MetricOptions'] = None + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None ) -> 'CounterLong': """Creates a counter type metric that contains long values. Args: name: The name of the counter. - options: An optional :class:`.MetricOptions` used to populate - details about 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 + constant_labels: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this metric. Returns: A new :class:`.CounterLong` @@ -68,14 +93,27 @@ def create_long_counter(self, def create_double_gauge(self, name: str, - options: typing.Optional['MetricOptions'] = None + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None ) -> 'GaugeDouble': """Creates a gauge type metric that contains double values. Args: name: The name of the gauge. - options: An optional :class:`.MetricOptions` used to populate - details about the gauge. + 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 + constant_labels: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this metric. Returns: A new :class:`.GaugeDouble` @@ -83,14 +121,27 @@ def create_double_gauge(self, def create_long_gauge(self, name: str, - options: typing.Optional['MetricOptions'] = None + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None ) -> 'GaugeLong': """Creates a gauge type metric that contains long values. Args: name: The name of the gauge. - options: An optional :class:`.MetricOptions` used to populate - details about the gauge. + 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 + constant_labels: A map of constant labels that will be used for + all of the TimeSeries created from the Metric. + component: The name of the component that reports this metric. + Resource: Sets the :class:`.Resource` associated with this metric. Returns: A new :class:`.GaugeLong` @@ -98,14 +149,18 @@ def create_long_gauge(self, def create_measure(self, name: str, - options: typing.Optional['MeasureOptions'] = None + description: str, + unit: str, + measure_type: 'MeasureType' = MeasureType.DOUBLE ) -> 'Measure': """Creates a Measure used to record raw :class:`.Measurement`s. Args: name: the name of the measure - options: An optional :class:`.MeasureOptions` used to populate - details about the measure + description: Human readable description of this measure. + unit: Unit of the measure values. + measure_type: Type of the measure. Can be one of two values - + `LONG` and `DOUBLE`. Default type is `DOUBLE`. Returns: A :class:`.Measure` @@ -113,8 +168,9 @@ def create_measure(self, def record(self, measurements: typing.List['Measurement'], - options: typing.Optional['RecordOptions'] = None) -> None: - """A function use to record a set of :class:`.Measurement`s. + span_context: 'SpanContext' = None + ) -> None: + """Records a set of `Measurement`s. The API is built with the idea that measurement aggregation will occur asynchronously. Typical library records multiple measurements at once, @@ -122,140 +178,17 @@ def record(self, can batch all of them that need to be recorded. Args: - measurements: The collection of measurements to record. options: - An optional :class:`.RecordOptions` used to populate details during - recording. + measurements: The collection of measurements to record. + span_context: the :class:`.SpanContext` that identified the + :class:`.Span` for which the measurements are associated with. Returns: None """ -_METER: typing.Optional[Meter] = None -_METER_FACTORY: typing.Optional[ - typing.Callable[[typing.Type[Meter]], typing.Optional[Meter]]] = None - - -def meter() -> Meter: - """Gets the current global :class:`.Meter` object. - - If there isn't one set yet, a default will be loaded. - """ - global _METER, _METER_FACTORY # pylint:disable=global-statement - - if _METER is None: - # pylint:disable=protected-access - _METER = loader._load_impl(Meter, _METER_FACTORY) - del _METER_FACTORY - - return _METER - - -def set_preferred_meter_implementation( - factory: typing.Callable[ - [typing.Type[Meter]], typing.Optional[Meter]] - ) -> None: - """Set the factory to be used to create the :class:`.Meter`. - - See :mod:`opentelemetry.loader` for details. - - This function may not be called after a meter is already loaded. - - Args: - factory: Callback that should create a new :class:`.Meter` instance. - """ - global _METER_FACTORY # pylint:disable=global-statement - - if _METER: - raise RuntimeError("Meter already loaded.") - - _METER_FACTORY = factory - - -class LabelKey: - - def __init__(self, - key: str, - description: str) -> None: - self.key = key - self.description = description - - -class LabelValue: - - def __init__(self, - value: str) -> None: - self.value = value - - -class MetricOptions: - def __init__(self, - description: str, - unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: typing.Dict['LabelKey', 'LabelValue'], - component: str, - resource: 'Resource') -> None: - """Optional info used when creating a :class:`.Metric`. - - Args: - 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 - constant_labels: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this - Metric. - """ - self.description = description - self.unit = unit - self.label_keys = label_keys - self.constant_labels = constant_labels - self.component = component - self.resource = resource - - class MeasureType: - DOUBLE = 0 - LONG = 1 - - -class MeasureOptions: - def __init__(self, - description: str, - unit: str, - measure_type: 'MeasureType' = MeasureType.DOUBLE - ) -> None: - """Optional info used when creating a :class:`.Measure`. - - Args: - description: Human readable description of this measure. - unit: Unit of the measure values. - measure_type: Type of the measure. Can be one of two values - - `LONG` and `DOUBLE`. Default type is `DOUBLE`. - """ - self.description = description - self.unit = unit - self.measure_type = measure_type - - -class RecordOptions: - - def __init__(self, - distributed_context: 'DistributedContext', - span_context: 'SpanContext') -> None: - """Optional info used when recording :class:`.Measurement`s. - - Args: - distributed_context: Explicit :class:`.DistributedContext` to use - instead of the current context. Context is used to add dimensions - for the resulting metric calculated out of the provided - measurements. - span_context: the :class:`.SpanContext` that identified the - :class:`.Span` for which the measurements are associated with. - """ + FLOAT = 0 + INT = 1 class Measurement: @@ -267,26 +200,16 @@ class Measurement: class Measure: + """Used to create raw :class:`.Measurement`s. - def __init__(self, - name: str, - options: typing.Optional['MeasureOptions'] = None) -> None: - """Used to create raw :class:`.Measurement`s. - - A contract between the API exposing the raw measurement and SDK - aggregating these values into the :class:`.Metric`. Measure is - constructed from the :class:`.Meter` class by providing a set of - :class:`.MeasureOptions`. - """ - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.measure_type = options.measure_type + A contract between the API exposing the raw measurement and SDK + aggregating these values into the :class:`.Metric`. Measure is + constructed from the :class:`.Meter` class. + """ - def create_double_measurement(self, - value: float) -> 'Measurement': - """Creates a measurement that contains double values. + def create_float_measurement(self, + value: float) -> 'Measurement': + """Creates a measurement that contains float values. Args: value: The value of the measurement. @@ -295,9 +218,9 @@ def create_double_measurement(self, A new :class:`.Measurement` """ - def create_long_measurement(self, + def create_int_measurement(self, value: int) -> 'Measurement': - """Creates a measurement that contains long values. + """Creates a measurement that contains int values. Args: value: The value of the measurement. @@ -363,28 +286,20 @@ def clear(self) -> None: """Removes all `TimeSeries` from the :class:`.Metric`.""" -class CounterDouble(Metric): - - def __init__(self, - name: str, - options: typing.Optional['MetricOptions'] = None - ) -> None: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.constant_labels = options.constant_labels - self.component = options.component - self.resource = options.resource +class CounterFloat(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. + """ def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'CounterDouble.TimeSeries': - """Gets and returns a `TimeSeries`, for a `CounterDouble` metric.""" + ) -> 'CounterFloat.TimeSeries': + """Gets and returns a `TimeSeries`, for a `CounterFloat` metric.""" - def get_default_time_series(self) -> 'CounterDouble.TimeSeries': - """Returns a `TimeSeries`, for a `CounterDouble` metric.""" + def get_default_time_series(self) -> 'CounterFloat.TimeSeries': + """Returns a `TimeSeries`, for a `CounterFloat` metric.""" class TimeSeries: @@ -403,38 +318,30 @@ def set(self, value: float) -> None: """ -class CounterLong(Metric): - - def __init__(self, - name: str, - options: typing.Optional['MetricOptions'] = None - ) -> None: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.constant_labels = options.constant_labels - self.component = options.component - self.resource = options.resource +class CounterInt(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. + """ def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'CounterLong.TimeSeries': - """Gets and returns a `TimeSeries`, for a `CounterLong` metric.""" + ) -> 'CounterInt.TimeSeries': + """Gets and returns a `TimeSeries`, for a `CounterInt` metric.""" - def get_default_time_series(self) -> 'CounterLong.TimeSeries': - """Returns a `TimeSeries`, for a `CounterLong` metric.""" + def get_default_time_series(self) -> 'CounterInt.TimeSeries': + """Returns a `TimeSeries`, for a `CounterInt` metric.""" class TimeSeries: - def add(self, value: float) -> None: + def add(self, value: int) -> None: """Adds the given value to the current value. The values cannot be negative. """ - def set(self, value: float) -> None: + def set(self, value: int) -> None: """Sets the current value to the given value. The given value must be larger than the current recorded value. In @@ -443,81 +350,53 @@ def set(self, value: float) -> None: """ -class GaugeDouble(Metric): - - def __init__(self, - name: str, - options: typing.Optional['MetricOptions'] = None - ) -> None: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.constant_labels = options.constant_labels - self.component = options.component - self.resource = options.resource +class GaugeFloat(Metric): + """A gauge type metric that holds float values. + + Cumulative value can go both up and down. Values can be negative. + """ def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'GaugeDouble.TimeSeries': - """Gets and returns a `TimeSeries`, for a `GaugeDouble` metric.""" + ) -> 'GaugeFloat.TimeSeries': + """Gets and returns a `TimeSeries`, for a `GaugeFloat` metric.""" - def get_default_time_series(self) -> 'GaugeDouble.TimeSeries': - """Returns a `TimeSeries`, for a `GaugeDouble` metric.""" + def get_default_time_series(self) -> 'GaugeFloat.TimeSeries': + """Returns a `TimeSeries`, for a `GaugeFloat` metric.""" class TimeSeries: def add(self, value: float) -> None: """Adds the given value to the current value. - The values cannot be negative. + The values can be negative. """ def set(self, value: float) -> None: - """Sets the current value to the given value. + """Sets the current value to the given value.""" - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ +class GaugeInt(Metric): + """A gauge type metric that holds int values. -class GaugeLong(Metric): - - def __init__(self, - name: str, - options: typing.Optional['MetricOptions'] = None - ) -> None: - self.name = name - if options: - self.description = options.description - self.unit = options.unit - self.label_keys = options.label_keys - self.constant_labels = options.constant_labels - self.component = options.component - self.resource = options.resource + Cumulative value can go both up and down. Values can be negative. + """ def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'GaugeLong.TimeSeries': - """Gets and returns a `TimeSeries`, for a `GaugeLong` metric.""" + ) -> 'GaugeInt.TimeSeries': + """Gets and returns a `TimeSeries`, for a `GaugeInt` metric.""" - def get_default_time_series(self) -> 'GaugeLong.TimeSeries': - """Returns a `TimeSeries`, for a `GaugeLong` metric.""" + def get_default_time_series(self) -> 'GaugeInt.TimeSeries': + """Returns a `TimeSeries`, for a `GaugeInt` metric.""" class TimeSeries: - def add(self, value: float) -> None: + def add(self, value: int) -> None: """Adds the given value to the current value. - The values cannot be negative. + The values can be negative. """ - def set(self, value: float) -> None: - """Sets the current value to the given value. - - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + def set(self, value: int) -> None: + """Sets the current value to the given value.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_key.py b/opentelemetry-api/src/opentelemetry/metrics/label_key.py new file mode 100644 index 0000000000..baa5618aa8 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/label_key.py @@ -0,0 +1,30 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +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 + diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_value.py b/opentelemetry-api/src/opentelemetry/metrics/label_value.py new file mode 100644 index 0000000000..0006129f04 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/label_value.py @@ -0,0 +1,25 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class LabelValue(object): + """The label values associated with a TimeSeries. + + :type value: str + :param value: the value for the label + """ + def __init__(self, + value: str) -> None: + self.value = value + From 34c87ce9bc42f7142d399f140c648220296748f3 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 13:07:50 -0700 Subject: [PATCH 06/88] Consistent naming for float and int --- .../src/opentelemetry/metrics/__init__.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 2be7bb9c3b..363a7ce30b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -36,7 +36,7 @@ class Meter: metrics like "queue_length". """ - def create_double_counter(self, + def create_float_counter(self, name: str, description: str, unit: str, @@ -45,8 +45,8 @@ def create_double_counter(self, typing.Dict['LabelKey', 'LabelValue'] = None, component: str = None, resource: 'Resource' = None - ) -> 'CounterDouble': - """Creates a counter type metric that contains double values. + ) -> 'CounterFloat': + """Creates a counter type metric that contains float values. Args: name: The name of the counter. @@ -60,10 +60,10 @@ def create_double_counter(self, component: The name of the component that reports this metric. Resource: Sets the :class:`.Resource` associated with this metric. - Returns: A new :class:`.CounterDouble` + Returns: A new :class:`.CounterFloat` """ - def create_long_counter(self, + def create_int_counter(self, name: str, description: str, unit: str, @@ -72,8 +72,8 @@ def create_long_counter(self, typing.Dict['LabelKey', 'LabelValue'] = None, component: str = None, resource: 'Resource' = None - ) -> 'CounterLong': - """Creates a counter type metric that contains long values. + ) -> 'CounterInt': + """Creates a counter type metric that contains int values. Args: name: The name of the counter. @@ -88,10 +88,10 @@ def create_long_counter(self, Resource: Sets the :class:`.Resource` associated with this metric. Returns: - A new :class:`.CounterLong` + A new :class:`.CounterInt` """ - def create_double_gauge(self, + def create_float_gauge(self, name: str, description: str, unit: str, @@ -100,8 +100,8 @@ def create_double_gauge(self, typing.Dict['LabelKey', 'LabelValue'] = None, component: str = None, resource: 'Resource' = None - ) -> 'GaugeDouble': - """Creates a gauge type metric that contains double values. + ) -> 'GaugeFloat': + """Creates a gauge type metric that contains float values. Args: name: The name of the gauge. @@ -116,10 +116,10 @@ def create_double_gauge(self, Resource: Sets the :class:`.Resource` associated with this metric. Returns: - A new :class:`.GaugeDouble` + A new :class:`.GaugeFloat` """ - def create_long_gauge(self, + def create_int_gauge(self, name: str, description: str, unit: str, @@ -128,8 +128,8 @@ def create_long_gauge(self, typing.Dict['LabelKey', 'LabelValue'] = None, component: str = None, resource: 'Resource' = None - ) -> 'GaugeLong': - """Creates a gauge type metric that contains long values. + ) -> 'GaugeInt': + """Creates a gauge type metric that contains int values. Args: name: The name of the gauge. @@ -144,7 +144,7 @@ def create_long_gauge(self, Resource: Sets the :class:`.Resource` associated with this metric. Returns: - A new :class:`.GaugeLong` + A new :class:`.GaugeInt` """ def create_measure(self, From df8ae342de92c06bb1a11b45415006d3bf44b81b Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 13:48:50 -0700 Subject: [PATCH 07/88] Abstract time series --- .../src/opentelemetry/metrics/__init__.py | 116 +++++------------- .../src/opentelemetry/metrics/time_series.py | 44 +++++++ 2 files changed, 77 insertions(+), 83 deletions(-) create mode 100644 opentelemetry-api/src/opentelemetry/metrics/time_series.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 363a7ce30b..22b7304f07 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -12,12 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +The OpenTelemetry metrics API describes the classes used to report raw +measurements, as well as metrics with known aggregation and labels. + +The :class:`.Meter` class is used to construct ::class`.Measure`s to +record raw measurements and :class`.Metric`s to record metrics with +predefined aggregation. + +See https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-metrics.md +for the specifications. + + +""" + import typing from opentelemetry import loader from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics.label_key import LabelKey from opentelemetry.metrics.label_key import LabelValue +from opentelemetry.metrics.time_series import CounterTimeSeries from opentelemetry.resources import Resource from opentelemetry.trace import SpanContext @@ -207,8 +222,8 @@ class Measure: constructed from the :class:`.Meter` class. """ - def create_float_measurement(self, - value: float) -> 'Measurement': + def create_measurement(self, + value: typing.Union[float, int]) -> 'Measurement': """Creates a measurement that contains float values. Args: @@ -218,17 +233,6 @@ def create_float_measurement(self, A new :class:`.Measurement` """ - def create_int_measurement(self, - value: int) -> 'Measurement': - """Creates a measurement that contains int values. - - Args: - value: The value of the measurement. - - Returns: - A new :class:`.Measurement` - """ - class Metric: """Base class for various types of metrics. @@ -295,27 +299,11 @@ class CounterFloat(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'CounterFloat.TimeSeries': - """Gets and returns a `TimeSeries`, for a `CounterFloat` metric.""" - - def get_default_time_series(self) -> 'CounterFloat.TimeSeries': - """Returns a `TimeSeries`, for a `CounterFloat` metric.""" - - class TimeSeries: - - def add(self, value: float) -> None: - """Adds the given value to the current value. + ) -> 'CounterTimeSeries': + """Gets a `CounterTimeSeries' with a cumulated float value.""" - The values cannot be negative. - """ - - def set(self, value: float) -> None: - """Sets the current value to the given value. - - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + def get_default_time_series(self) -> 'CounterTimeSeries': + """Returns a `CounterTimeSeries' with a cumulated float value.""" class CounterInt(Metric): @@ -327,28 +315,11 @@ class CounterInt(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'CounterInt.TimeSeries': - """Gets and returns a `TimeSeries`, for a `CounterInt` metric.""" - - def get_default_time_series(self) -> 'CounterInt.TimeSeries': - """Returns a `TimeSeries`, for a `CounterInt` metric.""" - - class TimeSeries: - - def add(self, value: int) -> None: - """Adds the given value to the current value. - - The values cannot be negative. - """ - - def set(self, value: int) -> None: - """Sets the current value to the given value. - - The given value must be larger than the current recorded value. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. - """ + ) -> 'CounterTimeSeries': + """Gets a `CounterTimeSeries' with a cumulated int value.""" + def get_default_time_series(self) -> 'CounterTimeSeries': + """Returns a `CounterTimeSeries' with a cumulated int value.""" class GaugeFloat(Metric): """A gauge type metric that holds float values. @@ -358,22 +329,11 @@ class GaugeFloat(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'GaugeFloat.TimeSeries': - """Gets and returns a `TimeSeries`, for a `GaugeFloat` metric.""" + ) -> 'GaugeTimeSeries': + """Gets a `GaugeTimeSeries` with a cumulated float value.""" - def get_default_time_series(self) -> 'GaugeFloat.TimeSeries': - """Returns a `TimeSeries`, for a `GaugeFloat` metric.""" - - class TimeSeries: - - def add(self, value: float) -> None: - """Adds the given value to the current value. - - The values can be negative. - """ - - def set(self, value: float) -> None: - """Sets the current value to the given value.""" + def get_default_time_series(self) -> 'GaugeTimeSeries': + """Returns a `GaugeTimeSeries` with a cumulated float value.""" class GaugeInt(Metric): @@ -384,19 +344,9 @@ class GaugeInt(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] - ) -> 'GaugeInt.TimeSeries': - """Gets and returns a `TimeSeries`, for a `GaugeInt` metric.""" - - def get_default_time_series(self) -> 'GaugeInt.TimeSeries': - """Returns a `TimeSeries`, for a `GaugeInt` metric.""" - - class TimeSeries: - - def add(self, value: int) -> None: - """Adds the given value to the current value. + ) -> 'GaugeTimeSeries': + """Gets a `GaugeTimeSeries` with a cumulated int value.""" - The values can be negative. - """ + def get_default_time_series(self) -> 'GaugeTimeSeries': + """Returns a `GaugeTimeSeries with a cumulated int value.""" - def set(self, value: int) -> None: - """Sets the current value to the given value.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/time_series.py new file mode 100644 index 0000000000..1a29454376 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -0,0 +1,44 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import typing + + +class CounterTimeSeries: + + def add(self, value: typing.Union[float, int]) -> None: + """Adds the given value to the current value. + + The values cannot be negative. + """ + + 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. In + general should be used in combination with `SetCallback` where the + recorded value is guaranteed to be monotonically increasing. + """ + + +class GaugeTimeSeries: + + def add(self, value: typing.Union[float, int]) -> None: + """Adds the given value to the current value. + + The values can be negative. + """ + + def set(self, value: typing.Union[float, int]) -> None: + """Sets the current value to the given value.""" From a2561acdc85c67c46ec31c6afca648140e948917 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 15:19:36 -0700 Subject: [PATCH 08/88] Use ABC --- .../src/opentelemetry/metrics/__init__.py | 87 ++++++++++--------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 22b7304f07..95fd8533e9 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -23,9 +23,9 @@ See https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-metrics.md for the specifications. - """ +import abc import typing from opentelemetry import loader @@ -52,15 +52,15 @@ class Meter: """ def create_float_counter(self, - name: str, - description: str, - unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None - ) -> 'CounterFloat': + name: str, + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None + ) -> 'CounterFloat': """Creates a counter type metric that contains float values. Args: @@ -79,15 +79,15 @@ def create_float_counter(self, """ def create_int_counter(self, - name: str, - description: str, - unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None - ) -> 'CounterInt': + name: str, + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None + ) -> 'CounterInt': """Creates a counter type metric that contains int values. Args: @@ -107,15 +107,15 @@ def create_int_counter(self, """ def create_float_gauge(self, - name: str, - description: str, - unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None - ) -> 'GaugeFloat': + name: str, + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + comonent: str = None, + resource: 'Resource' = None + ) -> 'GaugeFloat': """Creates a gauge type metric that contains float values. Args: @@ -135,15 +135,15 @@ def create_float_gauge(self, """ def create_int_gauge(self, - name: str, - description: str, - unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None - ) -> 'GaugeInt': + name: str, + description: str, + unit: str, + label_keys: typing.List['LabelKey'], + constant_labels: \ + typing.Dict['LabelKey', 'LabelValue'] = None, + component: str = None, + resource: 'Resource' = None + ) -> 'GaugeInt': """Creates a gauge type metric that contains int values. Args: @@ -166,7 +166,7 @@ def create_measure(self, name: str, description: str, unit: str, - measure_type: 'MeasureType' = MeasureType.DOUBLE + measure_type: 'MeasureType', ) -> 'Measure': """Creates a Measure used to record raw :class:`.Measurement`s. @@ -175,7 +175,7 @@ def create_measure(self, description: Human readable description of this measure. unit: Unit of the measure values. measure_type: Type of the measure. Can be one of two values - - `LONG` and `DOUBLE`. Default type is `DOUBLE`. + `FLOAT` and `INT`. Default type is `FLOAT`. Returns: A :class:`.Measure` @@ -183,6 +183,7 @@ def create_measure(self, def record(self, measurements: typing.List['Measurement'], + distributed_context = 'DistributedContext' = None span_context: 'SpanContext' = None ) -> None: """Records a set of `Measurement`s. @@ -224,7 +225,7 @@ class Measure: def create_measurement(self, value: typing.Union[float, int]) -> 'Measurement': - """Creates a measurement that contains float values. + """Creates a measurement with type corresponding to the `measure`'s type. Args: value: The value of the measurement. @@ -234,7 +235,7 @@ def create_measurement(self, """ -class Metric: +class Metric(abc.ABC): """Base class for various types of metrics. Metric class that inherit from this class are specialized with the type of @@ -242,6 +243,7 @@ class Metric: :class:`.Meter` class, by providing a set of :class:`.MetricOptions`. """ + @abc.abstractmethod def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'object': @@ -257,14 +259,13 @@ def get_or_create_time_series(self, label_values: A map of :class:`.LabelValue`s that will be associated with the return timeseries. """ - raise NotImplementedError + @abc.abstractmethod def get_default_time_series(self) -> 'object': """Returns a `TimeSeries`, a container for a cumulative value. The timeseries will have all its labels not set (default). """ - raise NotImplementedError def set_call_back(self, updater_function: typing.Callable[..., None]) -> None: """Sets a callback that gets executed every time prior to exporting. From 1ece493e8fdc18af63cb016e822943fd9c5dc3d7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 15:22:06 -0700 Subject: [PATCH 09/88] Fix typo --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 95fd8533e9..b13a9ce524 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -183,7 +183,7 @@ def create_measure(self, def record(self, measurements: typing.List['Measurement'], - distributed_context = 'DistributedContext' = None + distributed_context: 'DistributedContext' = None, span_context: 'SpanContext' = None ) -> None: """Records a set of `Measurement`s. From ce9268a6a4b4f2bc770422352603e7fa1b9b602d Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 6 Aug 2019 16:13:53 -0700 Subject: [PATCH 10/88] Fix docs --- .../src/opentelemetry/metrics/__init__.py | 98 ++++++++++--------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index b13a9ce524..4ec648bcd3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -16,23 +16,26 @@ The OpenTelemetry metrics API describes the classes used to report raw measurements, as well as metrics with known aggregation and labels. -The :class:`.Meter` class is used to construct ::class`.Measure`s to -record raw measurements and :class`.Metric`s to record metrics with +The :class:`.Meter` class is used to construct :class:`.Measure` s to +record raw measurements and :class:`.Metric` s to record metrics with predefined aggregation. -See https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-metrics.md -for the specifications. +See the `metrics api`_ spec for terminology and context clarification. + +.. _metrics api: + https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-metrics.md + """ import abc import typing -from opentelemetry import loader from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics.label_key import LabelKey -from opentelemetry.metrics.label_key import LabelValue +from opentelemetry.metrics.label_value import LabelValue from opentelemetry.metrics.time_series import CounterTimeSeries +from opentelemetry.metrics.time_series import GaugeTimeSeries from opentelemetry.resources import Resource from opentelemetry.trace import SpanContext @@ -40,12 +43,12 @@ class Meter: """An interface to allow the recording of measurements and metrics. - :class:`Measurement`s are used for recording raw values, in which the + :class:`.Measurement` s are used for recording raw values, in which the aggregation and labels for the exported metric are defered. This should be used to record measurements like "server_latency" or "received_bytes", where the value of interest is the recorded value itself. - :class:`Metric`s are used for recording pre-defined aggregation, or already + :class:`.Metric` s are used for recording pre-defined aggregation, or already aggregated data. This should be used to report metrics like cpu/memory usage, in which the type of aggregation is already defined, or simple metrics like "queue_length". @@ -67,11 +70,11 @@ def create_float_counter(self, 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 + 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 - constant_labels: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. + constant_labels: A map of constant labels that will be used for \ + all of the TimeSeries created from the Metric. \ component: The name of the component that reports this metric. Resource: Sets the :class:`.Resource` associated with this metric. @@ -94,11 +97,11 @@ def create_int_counter(self, 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 + 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 - constant_labels: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. + constant_labels: A map of constant labels that will be used for \ + all of the TimeSeries created from the Metric. \ component: The name of the component that reports this metric. Resource: Sets the :class:`.Resource` associated with this metric. @@ -119,14 +122,14 @@ def create_float_gauge(self, """Creates a gauge type metric that contains float values. Args: - name: The name of the gauge. + 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 + 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 - constant_labels: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. + constant_labels: A map of constant labels that will be used for \ + all of the TimeSeries created from the Metric. \ component: The name of the component that reports this metric. Resource: Sets the :class:`.Resource` associated with this metric. @@ -147,14 +150,14 @@ def create_int_gauge(self, """Creates a gauge type metric that contains int values. Args: - name: The name of the gauge. + 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 + 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 - constant_labels: A map of constant labels that will be used for - all of the TimeSeries created from the Metric. + constant_labels: A map of constant labels that will be used for \ + all of the TimeSeries created from the Metric. \ component: The name of the component that reports this metric. Resource: Sets the :class:`.Resource` associated with this metric. @@ -168,13 +171,13 @@ def create_measure(self, unit: str, measure_type: 'MeasureType', ) -> 'Measure': - """Creates a Measure used to record raw :class:`.Measurement`s. + """Creates a Measure used to record raw :class:`.Measurement` s. Args: name: the name of the measure description: Human readable description of this measure. unit: Unit of the measure values. - measure_type: Type of the measure. Can be one of two values - + measure_type: Type of the measure. Can be one of two values - \ `FLOAT` and `INT`. Default type is `FLOAT`. Returns: @@ -186,7 +189,7 @@ def record(self, distributed_context: 'DistributedContext' = None, span_context: 'SpanContext' = None ) -> None: - """Records a set of `Measurement`s. + """Records a set of :class:`.Measurement` s. The API is built with the idea that measurement aggregation will occur asynchronously. Typical library records multiple measurements at once, @@ -216,7 +219,7 @@ class Measurement: class Measure: - """Used to create raw :class:`.Measurement`s. + """Used to create raw :class:`.Measurement` s. A contract between the API exposing the raw measurement and SDK aggregating these values into the :class:`.Metric`. Measure is @@ -224,8 +227,9 @@ class Measure: """ def create_measurement(self, - value: typing.Union[float, int]) -> 'Measurement': - """Creates a measurement with type corresponding to the `measure`'s type. + value: typing.Union[float, int] + ) -> 'Measurement': + """Creates a measurement with type corresponding to the measure's type. Args: value: The value of the measurement. @@ -240,14 +244,14 @@ class Metric(abc.ABC): Metric class that inherit from this class are specialized with the type of time series that the metric holds. Metric is constructed from the - :class:`.Meter` class, by providing a set of :class:`.MetricOptions`. + :class:`.Meter` class. """ @abc.abstractmethod def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'object': - """Gets and returns a `TimeSeries`, a container for a cumulative value. + """Gets and returns a timeseries, a container for a cumulative value. If the provided label values are not already associated with this metric, a new timeseries is returned, otherwise it returns the existing @@ -256,13 +260,13 @@ def get_or_create_time_series(self, overrides this function. Args: - label_values: A map of :class:`.LabelValue`s that will be + label_values: A map of :class:`.LabelValue` s that will be \ associated with the return timeseries. """ @abc.abstractmethod def get_default_time_series(self) -> 'object': - """Returns a `TimeSeries`, a container for a cumulative value. + """Returns a timeseries, a container for a cumulative value. The timeseries will have all its labels not set (default). """ @@ -279,16 +283,16 @@ def set_call_back(self, updater_function: typing.Callable[..., None]) -> None: def remove_time_series(self, label_values: typing.List['LabelValue']) -> None: - """Removes the `TimeSeries` from the :class:`.Metric`, if present. + """Removes the timeseries from the :class:`.Metric`, if present. - The timeseries with matching :class:`.LabelValue`s will be removed. + The timeseries with matching :class:`.LabelValue` s will be removed. args: label_values: The list of label values to match against. """ def clear(self) -> None: - """Removes all `TimeSeries` from the :class:`.Metric`.""" + """Removes all timeseries from the :class:`.Metric`.""" class CounterFloat(Metric): @@ -301,10 +305,10 @@ class CounterFloat(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'CounterTimeSeries': - """Gets a `CounterTimeSeries' with a cumulated float value.""" + """Gets a :class:`.CounterTimeSeries` with a cumulated float value.""" def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a `CounterTimeSeries' with a cumulated float value.""" + """Returns a :class:`.CounterTimeSeries` with a cumulated float value.""" class CounterInt(Metric): @@ -317,10 +321,10 @@ class CounterInt(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'CounterTimeSeries': - """Gets a `CounterTimeSeries' with a cumulated int value.""" + """Gets a :class:`.CounterTimeSeries` with a cumulated int value.""" def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a `CounterTimeSeries' with a cumulated int value.""" + """Returns a :class:`.CounterTimeSeries` with a cumulated int value.""" class GaugeFloat(Metric): """A gauge type metric that holds float values. @@ -331,10 +335,10 @@ class GaugeFloat(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'GaugeTimeSeries': - """Gets a `GaugeTimeSeries` with a cumulated float value.""" + """Gets a :class:`.GaugeTimeSeries` with a cumulated float value.""" def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a `GaugeTimeSeries` with a cumulated float value.""" + """Returns a :class:`.GaugeTimeSeries` with a cumulated float value.""" class GaugeInt(Metric): @@ -346,8 +350,8 @@ class GaugeInt(Metric): def get_or_create_time_series(self, label_values: typing.List['LabelValue'] ) -> 'GaugeTimeSeries': - """Gets a `GaugeTimeSeries` with a cumulated int value.""" + """Gets a :class:`.GaugeTimeSeries` with a cumulated int value.""" def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a `GaugeTimeSeries with a cumulated int value.""" + """Returns a :class:`.GaugeTimeSeries` with a cumulated int value.""" From f5f9f016f3c1255426453d9d3a0f5f3a95b8eb64 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 7 Aug 2019 21:51:08 -0700 Subject: [PATCH 11/88] seperate measure classes --- .../src/opentelemetry/metrics/__init__.py | 89 +++++++++++++++---- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 4ec648bcd3..539c16de3f 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -165,23 +165,38 @@ def create_int_gauge(self, A new :class:`.GaugeInt` """ - def create_measure(self, - name: str, - description: str, - unit: str, - measure_type: 'MeasureType', - ) -> 'Measure': - """Creates a Measure used to record raw :class:`.Measurement` s. + def create_int_measure(self, + name: str, + description: str, + unit: str + ) -> 'IntMeasure': + """Creates a measure used to record raw :class:`.Measurement` s. + The measurements created from this measure will have type INT. Args: name: the name of the measure description: Human readable description of this measure. unit: Unit of the measure values. - measure_type: Type of the measure. Can be one of two values - \ - `FLOAT` and `INT`. Default type is `FLOAT`. Returns: - A :class:`.Measure` + A :class:`.IntMeasure` + """ + + def create_float_measure(self, + name: str, + description: str, + unit: str + ) -> 'FloatMeasure': + """Creates a Measure used to record raw :class:`.Measurement` s. + The measurements created from this measure will have type INT. + + Args: + name: the name of the measure + description: Human readable description of this measure. + unit: Unit of the measure values. + + Returns: + A :class:`.FloatMeasure` """ def record(self, @@ -205,11 +220,6 @@ def record(self, """ -class MeasureType: - FLOAT = 0 - INT = 1 - - class Measurement: """An empty interface that represents a single value. @@ -218,24 +228,65 @@ class Measurement: """ -class Measure: +class FloatMeasurement(Measurement): + """A :class:`.Measurement` with an INT value.""" + + +class IntMeasurement(Measurement): + """A :class:`.Measurement` with an INT value.""" + + +class Measure(abc.ABC): """Used to create raw :class:`.Measurement` s. A contract between the API exposing the raw measurement and SDK aggregating these values into the :class:`.Metric`. Measure is constructed from the :class:`.Meter` class. """ - + @abc.abstractmethod def create_measurement(self, value: typing.Union[float, int] ) -> 'Measurement': - """Creates a measurement with type corresponding to the measure's type. + """Creates a measurement. + + The type of the value in the measurement will correspond to the type + of measure that overrides this method. + + Args: + value: The value of the measurement. + + Returns: + A new :class:`.Measurement` + """ + +class FloatMeasure(Measure): + """Used to create raw :class:`.FloatMeasurement` s.""" + + def create_measurement(self, + value: int, + ) -> 'FloatMeasurement': + """Creates a measurement with a FLOAT type. + + Args: + value: The value of the measurement. + + Returns: + A new :class:`.FloatMeasurement` + """ + +class IntMeasure(Measure): + """Used to create raw :class:`.IntMeasurement` s.""" + + def create_measurement(self, + value: int, + ) -> 'IntMeasurement': + """Creates a measurement with an INT type. Args: value: The value of the measurement. Returns: - A new :class:`.Measurement` + A new :class:`.IntMeasurement` """ From 74a181537dbab71bcf127ba80703903d532f4860 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 7 Aug 2019 22:01:59 -0700 Subject: [PATCH 12/88] Add examples --- .../metrics/examples/aggregated.py | 25 ++++++++++++++++ .../metrics/examples/pre_aggregated.py | 27 +++++++++++++++++ .../src/opentelemetry/metrics/examples/raw.py | 29 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py create mode 100644 opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py create mode 100644 opentelemetry-api/src/opentelemetry/metrics/examples/raw.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py new file mode 100644 index 0000000000..0103e9f2d7 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py @@ -0,0 +1,25 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import psutil + +from opentelemetry.distributedcontext import DistributedContext +from opentelemetry.metrics import Meter +from opentelemetry.metrics.label_key import LabelKey + +meter = Meter() +label_keys = [LabelKey("environment", "the environment the application is running in")] +memory_metric = meter.create_int_gauge("available_memory", "available memory over time", "bytes", label_keys) +label_values = ["Testing"] +memory_metric.setCallBack(lambda: memory_metric.getOrCreateTimeSeries(label_values).set(psutil.virtual_memory().available)) \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py new file mode 100644 index 0000000000..c40da35926 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -0,0 +1,27 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from opentelemetry.distributedcontext import DistributedContext +from opentelemetry.metrics import Meter +from opentelemetry.metrics.label_key import LabelKey + +meter = Meter() +label_keys = [LabelKey("environment", "the environment the application is running in")] +sum_metric = meter.create_int_counter("sum numbers", "sum numbers over time", "number", label_keys) +label_values = ["Testing"] +sum_time_series = sum_metric.getOrCreateTimeSeries(label_values) + +for i in range(100): + sum_time_series.add(i) \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py new file mode 100644 index 0000000000..1c394147ef --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -0,0 +1,29 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import psutil +import time + +from opentelemetry.distributedcontext import DistributedContext +from opentelemetry.metrics import Meter + +meter = Meter() +measure = meter.create_float_measure("cpu_usage", "cpu usage over time", "percentage") + +measurements = [] +for i in range(100): + measurements.append(measure.createMeasurement(psutil.cpu_percent())) + time.sleep(1) + + meter.record(measurements, distributed_context=DistributedContext.get_current()) \ No newline at end of file From 0a0b8eef0b2181a52f86d938b735c209662a6417 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 8 Aug 2019 09:23:33 -0700 Subject: [PATCH 13/88] fix lint --- .../distributedcontext/__init__.py | 18 ----------- .../src/opentelemetry/metrics/__init__.py | 30 +++++++++---------- .../metrics/examples/aggregated.py | 17 +++++++---- .../metrics/examples/pre_aggregated.py | 17 ++++++----- .../src/opentelemetry/metrics/examples/raw.py | 15 +++++----- .../src/opentelemetry/metrics/label_key.py | 1 - .../src/opentelemetry/metrics/label_value.py | 3 +- .../src/opentelemetry/metrics/time_series.py | 2 +- 8 files changed, 46 insertions(+), 57 deletions(-) delete mode 100644 opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py deleted file mode 100644 index 482f81e67c..0000000000 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# TODO -class DistributedContext: - pass diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 539c16de3f..db93964a0e 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -31,7 +31,6 @@ import abc import typing -from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics.label_key import LabelKey from opentelemetry.metrics.label_value import LabelValue from opentelemetry.metrics.time_series import CounterTimeSeries @@ -48,10 +47,10 @@ class Meter: used to record measurements like "server_latency" or "received_bytes", where the value of interest is the recorded value itself. - :class:`.Metric` s are used for recording pre-defined aggregation, or already - aggregated data. This should be used to report metrics like cpu/memory - usage, in which the type of aggregation is already defined, or simple - metrics like "queue_length". + :class:`.Metric` s are used for recording pre-defined aggregation, or + already aggregated data. This should be used to report metrics like + cpu/memory usage, in which the type of aggregation is already defined, or + simple metrics like "queue_length". """ def create_float_counter(self, @@ -201,7 +200,6 @@ def create_float_measure(self, def record(self, measurements: typing.List['Measurement'], - distributed_context: 'DistributedContext' = None, span_context: 'SpanContext' = None ) -> None: """Records a set of :class:`.Measurement` s. @@ -214,6 +212,7 @@ def record(self, Args: measurements: The collection of measurements to record. span_context: the :class:`.SpanContext` that identified the + # TODO: DistributedContext :class:`.Span` for which the measurements are associated with. Returns: None @@ -245,7 +244,7 @@ class Measure(abc.ABC): """ @abc.abstractmethod def create_measurement(self, - value: typing.Union[float, int] + value: typing.Any ) -> 'Measurement': """Creates a measurement. @@ -263,8 +262,8 @@ class FloatMeasure(Measure): """Used to create raw :class:`.FloatMeasurement` s.""" def create_measurement(self, - value: int, - ) -> 'FloatMeasurement': + value: int, + ) -> 'FloatMeasurement': """Creates a measurement with a FLOAT type. Args: @@ -322,7 +321,9 @@ def get_default_time_series(self) -> 'object': The timeseries will have all its labels not set (default). """ - def set_call_back(self, updater_function: typing.Callable[..., None]) -> None: + def set_call_back(self, + updater_function: typing.Callable[..., None] + ) -> None: """Sets a callback that gets executed every time prior to exporting. This function MUST set the value of the :class:`.Metric` to the @@ -348,7 +349,7 @@ def clear(self) -> None: class CounterFloat(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. """ @@ -359,12 +360,12 @@ def get_or_create_time_series(self, """Gets a :class:`.CounterTimeSeries` with a cumulated float value.""" def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a :class:`.CounterTimeSeries` with a cumulated float value.""" + """Returns a :class:`.CounterTimeSeries` with a float value.""" class CounterInt(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. """ @@ -379,7 +380,7 @@ def get_default_time_series(self) -> 'CounterTimeSeries': class GaugeFloat(Metric): """A gauge type metric that holds float values. - + Cumulative value can go both up and down. Values can be negative. """ @@ -405,4 +406,3 @@ def get_or_create_time_series(self, def get_default_time_series(self) -> 'GaugeTimeSeries': """Returns a :class:`.GaugeTimeSeries` with a cumulated int value.""" - diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py index 0103e9f2d7..7c627ea842 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py @@ -14,12 +14,17 @@ import psutil -from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics import Meter from opentelemetry.metrics.label_key import LabelKey -meter = Meter() -label_keys = [LabelKey("environment", "the environment the application is running in")] -memory_metric = meter.create_int_gauge("available_memory", "available memory over time", "bytes", label_keys) -label_values = ["Testing"] -memory_metric.setCallBack(lambda: memory_metric.getOrCreateTimeSeries(label_values).set(psutil.virtual_memory().available)) \ No newline at end of file +METER = Meter() +LABEL_KEYS = [LabelKey("environment", + "the environment the application is running in")] +MEMORY_METRIC = METER.create_int_gauge("available_memory", + "available memory over time", + "bytes", + LABEL_KEYS) +LABEL_VALUES = ["Testing"] +MEMORY_METRIC.setCallBack(lambda: MEMORY_METRIC \ + .getOrCreateTimeSeries(LABEL_VALUES) \ + .set(psutil.virtual_memory().available)) diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index c40da35926..ba76fcc5c2 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -13,15 +13,18 @@ # limitations under the License. -from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics import Meter from opentelemetry.metrics.label_key import LabelKey -meter = Meter() -label_keys = [LabelKey("environment", "the environment the application is running in")] -sum_metric = meter.create_int_counter("sum numbers", "sum numbers over time", "number", label_keys) -label_values = ["Testing"] -sum_time_series = sum_metric.getOrCreateTimeSeries(label_values) +METER = Meter() +LABEL_KEYS = [LabelKey("environment", + "the environment the application is running in")] +SUM_METRIC = METER.create_int_counter("sum numbers", + "sum numbers over time", + "number", + LABEL_KEYS) +LABEL_VALUES = ["Testing"] +SUM_TIME_SERIES = SUM_METRIC.getOrCreateTimeSeries(LABEL_VALUES) for i in range(100): - sum_time_series.add(i) \ No newline at end of file + SUM_TIME_SERIES.add(i) \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 1c394147ef..c61f5d2b31 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -12,18 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -import psutil import time +import psutil -from opentelemetry.distributedcontext import DistributedContext from opentelemetry.metrics import Meter -meter = Meter() -measure = meter.create_float_measure("cpu_usage", "cpu usage over time", "percentage") +METER = Meter() +MEASURE = METER.create_float_measure("cpu_usage", + "cpu usage over time", + "percentage") -measurements = [] +MEASUREMENTS = [] for i in range(100): - measurements.append(measure.createMeasurement(psutil.cpu_percent())) + MEASUREMENTS.append(MEASURE.createMeasurement(psutil.cpu_percent())) time.sleep(1) - meter.record(measurements, distributed_context=DistributedContext.get_current()) \ No newline at end of file + METER.record(MEASUREMENTS) diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_key.py b/opentelemetry-api/src/opentelemetry/metrics/label_key.py index baa5618aa8..8c351d8338 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/label_key.py +++ b/opentelemetry-api/src/opentelemetry/metrics/label_key.py @@ -27,4 +27,3 @@ def __init__(self, description: str) -> None: self.key = key self.description = description - diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_value.py b/opentelemetry-api/src/opentelemetry/metrics/label_value.py index 0006129f04..c9dc9654b1 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/label_value.py +++ b/opentelemetry-api/src/opentelemetry/metrics/label_value.py @@ -13,7 +13,7 @@ # limitations under the License. -class LabelValue(object): +class LabelValue: """The label values associated with a TimeSeries. :type value: str @@ -22,4 +22,3 @@ class LabelValue(object): def __init__(self, value: str) -> None: self.value = value - diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/time_series.py index 1a29454376..dcd32e9439 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -33,7 +33,7 @@ def set(self, value: typing.Union[float, int]) -> None: class GaugeTimeSeries: - + def add(self, value: typing.Union[float, int]) -> None: """Adds the given value to the current value. From 555bf50783e62b049d770274084e1e109fa2de49 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 14 Aug 2019 15:35:27 -0700 Subject: [PATCH 14/88] Update to RFC 0003 --- .../src/opentelemetry/metrics/__init__.py | 330 +++++++----------- .../metrics/{label_key.py => aggregation.py} | 24 +- .../metrics/examples/aggregated.py | 30 -- .../metrics/examples/pre_aggregated.py | 15 +- .../src/opentelemetry/metrics/examples/raw.py | 24 +- .../src/opentelemetry/metrics/label_value.py | 24 -- .../src/opentelemetry/metrics/time_series.py | 25 +- 7 files changed, 183 insertions(+), 289 deletions(-) rename opentelemetry-api/src/opentelemetry/metrics/{label_key.py => aggregation.py} (55%) delete mode 100644 opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py delete mode 100644 opentelemetry-api/src/opentelemetry/metrics/label_value.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index db93964a0e..55c27980cb 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,40 +28,31 @@ """ -import abc -import typing +from abc import ABC, abstractmethod +from typing import Any, Callable, Dict, List -from opentelemetry.metrics.label_key import LabelKey -from opentelemetry.metrics.label_value import LabelValue +from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries from opentelemetry.metrics.time_series import GaugeTimeSeries -from opentelemetry.resources import Resource -from opentelemetry.trace import SpanContext +from opentelemetry.metrics.time_series import MeasureTimeSeries +LabelKeys = List['LabelKey'] +LabelValues = List['LabelValue'] -class Meter: - """An interface to allow the recording of measurements and metrics. - :class:`.Measurement` s are used for recording raw values, in which the - aggregation and labels for the exported metric are defered. This should be - used to record measurements like "server_latency" or "received_bytes", - where the value of interest is the recorded value itself. +class Meter: + """An interface to allow the recording of metrics. - :class:`.Metric` s are used for recording pre-defined aggregation, or - already aggregated data. This should be used to report metrics like - cpu/memory usage, in which the type of aggregation is already defined, or - simple metrics like "queue_length". + `Metric` s are used for recording pre-defined aggregation (gauge and + counter), or raw values (measure) in which the aggregation and labels + for the exported metric are deferred. """ def create_float_counter(self, name: str, description: str, unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None + label_keys: LabelKeys, ) -> 'CounterFloat': """Creates a counter type metric that contains float values. @@ -69,26 +60,18 @@ def create_float_counter(self, 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 - constant_labels: A map of constant labels that will be used for \ - all of the TimeSeries created from the Metric. \ - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this metric. - - Returns: A new :class:`.CounterFloat` + 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. + + Returns: A new `CounterFloat` """ def create_int_counter(self, name: str, description: str, unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None + label_keys: LabelKeys, ) -> 'CounterInt': """Creates a counter type metric that contains int values. @@ -96,27 +79,19 @@ def create_int_counter(self, 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 - constant_labels: A map of constant labels that will be used for \ - all of the TimeSeries created from the Metric. \ - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this metric. + 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. Returns: - A new :class:`.CounterInt` + A new `CounterInt` """ def create_float_gauge(self, name: str, description: str, unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - comonent: str = None, - resource: 'Resource' = None + label_keys: LabelKeys, ) -> 'GaugeFloat': """Creates a gauge type metric that contains float values. @@ -124,27 +99,19 @@ def create_float_gauge(self, 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 - constant_labels: A map of constant labels that will be used for \ - all of the TimeSeries created from the Metric. \ - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this metric. + 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. Returns: - A new :class:`.GaugeFloat` + A new `GaugeFloat` """ def create_int_gauge(self, name: str, description: str, unit: str, - label_keys: typing.List['LabelKey'], - constant_labels: \ - typing.Dict['LabelKey', 'LabelValue'] = None, - component: str = None, - resource: 'Resource' = None + label_keys: LabelKeys ) -> 'GaugeInt': """Creates a gauge type metric that contains int values. @@ -152,144 +119,59 @@ def create_int_gauge(self, 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 - constant_labels: A map of constant labels that will be used for \ - all of the TimeSeries created from the Metric. \ - component: The name of the component that reports this metric. - Resource: Sets the :class:`.Resource` associated with this metric. + 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. + Returns: - A new :class:`.GaugeInt` + A new `GaugeInt` """ def create_int_measure(self, name: str, description: str, - unit: str - ) -> 'IntMeasure': - """Creates a measure used to record raw :class:`.Measurement` s. - The measurements created from this measure will have type INT. + unit: str, + label_keys: LabelKeys, + aggregation: 'Aggregation' + ) -> 'MeasureInt': + """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. Returns: - A :class:`.IntMeasure` + A new `MeasureInt` """ def create_float_measure(self, name: str, description: str, - unit: str - ) -> 'FloatMeasure': - """Creates a Measure used to record raw :class:`.Measurement` s. - The measurements created from this measure will have type INT. - - Args: - name: the name of the measure - description: Human readable description of this measure. - unit: Unit of the measure values. - - Returns: - A :class:`.FloatMeasure` - """ - - def record(self, - measurements: typing.List['Measurement'], - span_context: 'SpanContext' = None - ) -> None: - """Records a set of :class:`.Measurement` s. - - The API is built with the idea that measurement aggregation will occur - asynchronously. Typical library records multiple measurements at once, - so this function accepts a collection of measurements so the library - can batch all of them that need to be recorded. - - Args: - measurements: The collection of measurements to record. - span_context: the :class:`.SpanContext` that identified the - # TODO: DistributedContext - :class:`.Span` for which the measurements are associated with. - - Returns: None - """ - - -class Measurement: - """An empty interface that represents a single value. - - This single value is recorded for the :class:`.Measure` that created - this measurement. - """ - - -class FloatMeasurement(Measurement): - """A :class:`.Measurement` with an INT value.""" - - -class IntMeasurement(Measurement): - """A :class:`.Measurement` with an INT value.""" - - -class Measure(abc.ABC): - """Used to create raw :class:`.Measurement` s. - - A contract between the API exposing the raw measurement and SDK - aggregating these values into the :class:`.Metric`. Measure is - constructed from the :class:`.Meter` class. - """ - @abc.abstractmethod - def create_measurement(self, - value: typing.Any - ) -> 'Measurement': - """Creates a measurement. - - The type of the value in the measurement will correspond to the type - of measure that overrides this method. - - Args: - value: The value of the measurement. - - Returns: - A new :class:`.Measurement` - """ - -class FloatMeasure(Measure): - """Used to create raw :class:`.FloatMeasurement` s.""" - - def create_measurement(self, - value: int, - ) -> 'FloatMeasurement': - """Creates a measurement with a FLOAT type. - - Args: - value: The value of the measurement. - - Returns: - A new :class:`.FloatMeasurement` - """ - -class IntMeasure(Measure): - """Used to create raw :class:`.IntMeasurement` s.""" - - def create_measurement(self, - value: int, - ) -> 'IntMeasurement': - """Creates a measurement with an INT type. + unit: str, + label_keys: LabelKeys, + aggregation: 'Aggregation' + ) -> 'MeasureFloat': + """Creates a Measure used to record raw float values. Args: - value: The value of the measurement. + 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. Returns: - A new :class:`.IntMeasurement` + A new `MeasureFloat` """ -class Metric(abc.ABC): +class Metric(ABC): """Base class for various types of metrics. Metric class that inherit from this class are specialized with the type of @@ -297,9 +179,9 @@ class Metric(abc.ABC): :class:`.Meter` class. """ - @abc.abstractmethod + @abstractmethod def get_or_create_time_series(self, - label_values: typing.List['LabelValue'] + label_values: LabelValues ) -> 'object': """Gets and returns a timeseries, a container for a cumulative value. @@ -310,11 +192,11 @@ def get_or_create_time_series(self, overrides this function. Args: - label_values: A map of :class:`.LabelValue` s that will be \ - associated with the return timeseries. + label_values: A map of `LabelValue` s that will be associated + with the return timeseries. """ - @abc.abstractmethod + @abstractmethod def get_default_time_series(self) -> 'object': """Returns a timeseries, a container for a cumulative value. @@ -326,25 +208,25 @@ def set_call_back(self, ) -> None: """Sets a callback that gets executed every time prior to exporting. - This function MUST set the value of the :class:`.Metric` to the - value that will be exported. + This function MUST set the value of the `Metric` to the value that + that will be exported. args: updater_function: The callback function to execute. """ def remove_time_series(self, - label_values: typing.List['LabelValue']) -> None: - """Removes the timeseries from the :class:`.Metric`, if present. + label_values: LabelValues) -> None: + """Removes the timeseries from the Metric, if present. - The timeseries with matching :class:`.LabelValue` s will be removed. + The timeseries with matching `LabelValue` s will be removed. args: label_values: The list of label values to match against. """ def clear(self) -> None: - """Removes all timeseries from the :class:`.Metric`.""" + """Removes all timeseries from the `Metric`.""" class CounterFloat(Metric): @@ -355,12 +237,12 @@ class CounterFloat(Metric): """ def get_or_create_time_series(self, - label_values: typing.List['LabelValue'] + label_values: LabelValues ) -> 'CounterTimeSeries': - """Gets a :class:`.CounterTimeSeries` with a cumulated float value.""" + """Gets a CounterTimeSeries with a cumulative float value.""" def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a :class:`.CounterTimeSeries` with a float value.""" + """Returns a `CounterTimeSeries` with a cumulative float value.""" class CounterInt(Metric): @@ -371,12 +253,12 @@ class CounterInt(Metric): """ def get_or_create_time_series(self, - label_values: typing.List['LabelValue'] + label_values: LabelValues ) -> 'CounterTimeSeries': - """Gets a :class:`.CounterTimeSeries` with a cumulated int value.""" + """Gets a `CounterTimeSeries` with a cumulative int value.""" def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a :class:`.CounterTimeSeries` with a cumulated int value.""" + """Returns a `CounterTimeSeries` with a cumulative int value.""" class GaugeFloat(Metric): """A gauge type metric that holds float values. @@ -385,12 +267,12 @@ class GaugeFloat(Metric): """ def get_or_create_time_series(self, - label_values: typing.List['LabelValue'] + label_values: LabelValues ) -> 'GaugeTimeSeries': - """Gets a :class:`.GaugeTimeSeries` with a cumulated float value.""" + """Gets a `GaugeTimeSeries` with a cumulative float value.""" def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a :class:`.GaugeTimeSeries` with a cumulated float value.""" + """Returns a `GaugeTimeSeries` with a cumulative float value.""" class GaugeInt(Metric): @@ -400,9 +282,65 @@ class GaugeInt(Metric): """ def get_or_create_time_series(self, - label_values: typing.List['LabelValue'] + label_values: LabelValues ) -> 'GaugeTimeSeries': - """Gets a :class:`.GaugeTimeSeries` with a cumulated int value.""" + """Gets a `GaugeTimeSeries` with a cumulative int value.""" def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a :class:`.GaugeTimeSeries` with a cumulated int value.""" + """Returns a `GaugeTimeSeries` with a cumulative int value.""" + + +class MeasureFloat(Metric): + """A measure type metric that holds float values. + + Measure metrics represent raw statistics that are recorded. + """ + + def get_or_create_time_series(self, + label_values: LabelValues + ) -> 'MeasureTimeSeries': + """Gets a `MeasureTimeSeries` with a cumulated float value.""" + + def get_default_time_series(self) -> 'MeasureTimeSeries': + """Returns a `MeasureTimeSeries` with a cumulated float value.""" + + +class MeasureInt(Metric): + """A measure type metric that holds int values. + + Measure metrics represent raw statistics that are recorded. + """ + + def get_or_create_time_series(self, + label_values: LabelValues + ) -> 'MeasureTimeSeries': + """Gets a `.MeasureTimeSeries` with a cumulated int value.""" + + def get_default_time_series(self) -> 'MeasureTimeSeries': + """Returns 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 + +class LabelValue: + """The label values associated with a TimeSeries. + + :type value: str + :param value: the value for the label + """ + def __init__(self, + value: str) -> None: + self.value = value \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_key.py b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py similarity index 55% rename from opentelemetry-api/src/opentelemetry/metrics/label_key.py rename to opentelemetry-api/src/opentelemetry/metrics/aggregation.py index 8c351d8338..5bd8b2f27d 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/label_key.py +++ b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py @@ -12,18 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +#TODO +class BaseAggregation: + """The base class for all aggregation types.""" -class LabelKey: - """The label keys associated with the metric. +class CountAggregation(BaseAggregation): + """An aggregation representing a count of values.""" - :type key: str - :param key: the key for the label +class DistributonAggregation(BaseAggregation): + """An aggregation representing a distribution.""" - :type description: str - :param description: description of the label - """ - def __init__(self, - key: str, - description: str) -> None: - self.key = key - self.description = description +class LastValueAggregation(BaseAggregation): + """An aggregation representing the last value.""" + +class SumAggregation(BaseAggregation): + """An aggregation representing a sum of values.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py deleted file mode 100644 index 7c627ea842..0000000000 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/aggregated.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import psutil - -from opentelemetry.metrics import Meter -from opentelemetry.metrics.label_key import LabelKey - -METER = Meter() -LABEL_KEYS = [LabelKey("environment", - "the environment the application is running in")] -MEMORY_METRIC = METER.create_int_gauge("available_memory", - "available memory over time", - "bytes", - LABEL_KEYS) -LABEL_VALUES = ["Testing"] -MEMORY_METRIC.setCallBack(lambda: MEMORY_METRIC \ - .getOrCreateTimeSeries(LABEL_VALUES) \ - .set(psutil.virtual_memory().available)) diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index ba76fcc5c2..004d2943b3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -13,18 +13,23 @@ # limitations under the License. +from opentelemetry.metrics import LabelKey +from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter -from opentelemetry.metrics.label_key import LabelKey METER = Meter() LABEL_KEYS = [LabelKey("environment", "the environment the application is running in")] -SUM_METRIC = METER.create_int_counter("sum numbers", +COUNTER = METER.create_int_counter("sum numbers", "sum numbers over time", "number", LABEL_KEYS) -LABEL_VALUES = ["Testing"] -SUM_TIME_SERIES = SUM_METRIC.getOrCreateTimeSeries(LABEL_VALUES) +LABEL_VALUE_TESTING = [LabelValue("Testing")] +LABEL_VALUE_STAGING = [LabelValue("Staging")] + +# Metrics sent to some exporter +COUNTER_METRIC_TESTING = COUNTER.get_or_create_time_series(LABEL_VALUE_TESTING) +COUNTER_METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_STAGING) for i in range(100): - SUM_TIME_SERIES.add(i) \ No newline at end of file + COUNTER_METRIC_STAGING.add(i) \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index c61f5d2b31..66f5fe55f3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -12,19 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time import psutil +from opentelemetry.metrics import LabelKey +from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter +from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() -MEASURE = METER.create_float_measure("cpu_usage", - "cpu usage over time", - "percentage") +LABEL_KEYS = [LabelKey("environment", + "the environment the application is running in")] +MEASURE = METER.create_float_measure("idle_cpu_percentage", + "cpu idle over time", + "percentage", + LastValueAggregation) +LABEL_VALUE_TESTING = [LabelValue("Testing")] +LABEL_VALUE_STAGING = [LabelValue("Staging")] -MEASUREMENTS = [] -for i in range(100): - MEASUREMENTS.append(MEASURE.createMeasurement(psutil.cpu_percent())) - time.sleep(1) +# 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) - METER.record(MEASUREMENTS) +MEASURE_METRIC_STAGING.record(psutil.cpu_times_percent().idle) diff --git a/opentelemetry-api/src/opentelemetry/metrics/label_value.py b/opentelemetry-api/src/opentelemetry/metrics/label_value.py deleted file mode 100644 index c9dc9654b1..0000000000 --- a/opentelemetry-api/src/opentelemetry/metrics/label_value.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class LabelValue: - """The label values associated with a TimeSeries. - - :type value: str - :param value: the value for the label - """ - def __init__(self, - value: str) -> None: - self.value = value diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/time_series.py index dcd32e9439..e0e8eba22a 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -18,27 +18,26 @@ class CounterTimeSeries: def add(self, value: typing.Union[float, int]) -> None: - """Adds the given value to the current value. - - The values cannot be negative. - """ + """Adds the given value to the current value. Cannot be negative.""" 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. In - general should be used in combination with `SetCallback` where the - recorded value is guaranteed to be monotonically increasing. + The given value must be larger than the current recorded value. """ class GaugeTimeSeries: - def add(self, value: typing.Union[float, int]) -> None: - """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. Can be negative.""" - The values can be negative. - """ - def set(self, value: typing.Union[float, int]) -> None: - """Sets the current value to the given value.""" +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. + """ + \ No newline at end of file From d6b1113e19cbaf7f95f2b34eaebf5a72faae75d1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 14 Aug 2019 15:59:18 -0700 Subject: [PATCH 15/88] Add spancontext, measurebatch --- .../src/opentelemetry/metrics/__init__.py | 46 +++++++++++++++---- .../src/opentelemetry/metrics/examples/raw.py | 9 +++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 55c27980cb..128da5775d 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -35,6 +35,7 @@ from opentelemetry.metrics.time_series import CounterTimeSeries from opentelemetry.metrics.time_series import GaugeTimeSeries from opentelemetry.metrics.time_series import MeasureTimeSeries +from opentelemetry.trace import SpanContext LabelKeys = List['LabelKey'] LabelValues = List['LabelValue'] @@ -53,6 +54,7 @@ def create_float_counter(self, description: str, unit: str, label_keys: LabelKeys, + span_context: SpanContext = None ) -> 'CounterFloat': """Creates a counter type metric that contains float values. @@ -63,6 +65,8 @@ def create_float_counter(self, 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 identified the `Span` + for which the metrics are associated with. Returns: A new `CounterFloat` """ @@ -72,6 +76,7 @@ def create_int_counter(self, description: str, unit: str, label_keys: LabelKeys, + span_context: SpanContext = None ) -> 'CounterInt': """Creates a counter type metric that contains int values. @@ -82,6 +87,8 @@ def create_int_counter(self, 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 identified the `Span` + for which the metrics are associated with. Returns: A new `CounterInt` @@ -92,6 +99,7 @@ def create_float_gauge(self, description: str, unit: str, label_keys: LabelKeys, + span_context: SpanContext = None ) -> 'GaugeFloat': """Creates a gauge type metric that contains float values. @@ -102,6 +110,8 @@ def create_float_gauge(self, 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 identified the `Span` + for which the metrics are associated with. Returns: A new `GaugeFloat` @@ -111,7 +121,8 @@ def create_int_gauge(self, name: str, description: str, unit: str, - label_keys: LabelKeys + label_keys: LabelKeys, + span_context: SpanContext = None ) -> 'GaugeInt': """Creates a gauge type metric that contains int values. @@ -122,7 +133,8 @@ def create_int_gauge(self, 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 identified the `Span` + for which the metrics are associated with. Returns: A new `GaugeInt` @@ -133,17 +145,21 @@ def create_int_measure(self, description: str, unit: str, label_keys: LabelKeys, - aggregation: 'Aggregation' + aggregation: 'Aggregation', + span_context: SpanContext = None, ) -> 'MeasureInt': """Creates a measure used to record raw int values. Args: - name: the name of the measure + 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. + aggregation: The type of aggregation to use for this measure metric. + span_context: The `SpanContext` that identified the `Span` + for which the metrics are associated with. Returns: A new `MeasureInt` @@ -154,7 +170,8 @@ def create_float_measure(self, description: str, unit: str, label_keys: LabelKeys, - aggregation: 'Aggregation' + aggregation: 'Aggregation', + span_context: SpanContext = None, ) -> 'MeasureFloat': """Creates a Measure used to record raw float values. @@ -165,6 +182,9 @@ def create_float_measure(self, 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. + aggregation: The type of aggregation to use for this measure metric. + span_context: The `SpanContext` that identified the `Span` + for which the metrics are associated with. Returns: A new `MeasureFloat` @@ -175,8 +195,7 @@ 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 - :class:`.Meter` class. + time series that the metric holds. Metric is constructed from the meter. """ @abstractmethod @@ -204,7 +223,7 @@ def get_default_time_series(self) -> 'object': """ def set_call_back(self, - updater_function: typing.Callable[..., None] + updater_function: Callable[..., None] ) -> None: """Sets a callback that gets executed every time prior to exporting. @@ -320,6 +339,17 @@ def get_default_time_series(self) -> 'MeasureTimeSeries': """Returns a `.MeasureTimeSeries` with a cumulated int value.""" +class MeasureBatch: + + def record(metric_pairs): + """Records multiple observed values simultaneously. + + Args: + metric_pairs: A list of tuples containing the `Metric` and value + to be recorded. + """ + + class LabelKey: """The label keys associated with the metric. diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 66f5fe55f3..2e2c84d21a 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -17,6 +17,7 @@ from opentelemetry.metrics import LabelKey from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter +from opentelemetry.metrics import MeasureBatch from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() @@ -33,4 +34,10 @@ 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_STAGING.record(psutil.cpu_times_percent().idle) +# record individual measures +idle = psutil.cpu_times_percent().idle +MEASURE_METRIC_STAGING.record(idle) + +# record multiple observed values +batch = MeasureBatch() +batch.record([(MEASURE_METRIC_TESTING, idle), (MEASURE_METRIC_STAGING, idle)]) From 18cfc7162cf62be7e19f9d150be133e0e390c01d Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 15 Aug 2019 11:04:23 -0700 Subject: [PATCH 16/88] Fix docs --- docs/index.rst | 1 + docs/opentelemetry.metrics.aggregation.rst | 7 +++++ docs/opentelemetry.metrics.rst | 15 +++++++++ docs/opentelemetry.metrics.time_series.rst | 7 +++++ .../src/opentelemetry/metrics/__init__.py | 31 +++++++++---------- .../src/opentelemetry/metrics/aggregation.py | 10 +++--- 6 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 docs/opentelemetry.metrics.aggregation.rst create mode 100644 docs/opentelemetry.metrics.rst create mode 100644 docs/opentelemetry.metrics.time_series.rst diff --git a/docs/index.rst b/docs/index.rst index 8991f2b5df..4d968ccd64 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,7 @@ abstract types for OpenTelemetry implementations. opentelemetry.context opentelemetry.loader + opentelemetry.metrics opentelemetry.trace diff --git a/docs/opentelemetry.metrics.aggregation.rst b/docs/opentelemetry.metrics.aggregation.rst new file mode 100644 index 0000000000..c97f1bf72e --- /dev/null +++ b/docs/opentelemetry.metrics.aggregation.rst @@ -0,0 +1,7 @@ +opentelemetry.metrics.aggregation module +========================================== + +.. automodule:: opentelemetry.metrics.aggregation + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/opentelemetry.metrics.rst b/docs/opentelemetry.metrics.rst new file mode 100644 index 0000000000..b388627133 --- /dev/null +++ b/docs/opentelemetry.metrics.rst @@ -0,0 +1,15 @@ +opentelemetry.metrics package +============================= + +Submodules +---------- + +.. toctree:: + + opentelemetry.metrics.aggregation + opentelemetry.metrics.time_series + +Module contents +--------------- + +.. automodule:: opentelemetry.metrics diff --git a/docs/opentelemetry.metrics.time_series.rst b/docs/opentelemetry.metrics.time_series.rst new file mode 100644 index 0000000000..976a81a821 --- /dev/null +++ b/docs/opentelemetry.metrics.time_series.rst @@ -0,0 +1,7 @@ +opentelemetry.metrics.time\_series module +========================================== + +.. automodule:: opentelemetry.metrics.time_series + :members: + :undoc-members: + :show-inheritance: diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 128da5775d..444f2b79a2 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -16,9 +16,8 @@ The OpenTelemetry metrics API describes the classes used to report raw measurements, as well as metrics with known aggregation and labels. -The :class:`.Meter` class is used to construct :class:`.Measure` s to -record raw measurements and :class:`.Metric` s to record metrics with -predefined aggregation. +The `Meter` class is used to construct `Metric` s to record raw statistics +as well as metrics with predefined aggregation. See the `metrics api`_ spec for terminology and context clarification. @@ -212,7 +211,7 @@ def get_or_create_time_series(self, Args: label_values: A map of `LabelValue` s that will be associated - with the return timeseries. + with the return timeseries. """ @abstractmethod @@ -258,7 +257,7 @@ class CounterFloat(Metric): def get_or_create_time_series(self, label_values: LabelValues ) -> 'CounterTimeSeries': - """Gets a CounterTimeSeries with a cumulative float value.""" + """Gets a `CounterTimeSeries` with a cumulative float value.""" def get_default_time_series(self) -> 'CounterTimeSeries': """Returns a `CounterTimeSeries` with a cumulative float value.""" @@ -316,8 +315,8 @@ class MeasureFloat(Metric): """ def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'MeasureTimeSeries': + label_values: LabelValues + ) -> 'MeasureTimeSeries': """Gets a `MeasureTimeSeries` with a cumulated float value.""" def get_default_time_series(self) -> 'MeasureTimeSeries': @@ -331,22 +330,22 @@ class MeasureInt(Metric): """ def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'MeasureTimeSeries': - """Gets a `.MeasureTimeSeries` with a cumulated int value.""" + label_values: LabelValues + ) -> 'MeasureTimeSeries': + """Gets a `MeasureTimeSeries` with a cumulated int value.""" def get_default_time_series(self) -> 'MeasureTimeSeries': - """Returns a `.MeasureTimeSeries` with a cumulated int value.""" + """Returns a `MeasureTimeSeries` with a cumulated int value.""" class MeasureBatch: - def record(metric_pairs): + def record(self, metric_pairs): """Records multiple observed values simultaneously. Args: metric_pairs: A list of tuples containing the `Metric` and value - to be recorded. + to be recorded. """ @@ -360,8 +359,8 @@ class LabelKey: :param description: description of the label """ def __init__(self, - key: str, - description: str) -> None: + key: str, + description: str) -> None: self.key = key self.description = description @@ -372,5 +371,5 @@ class LabelValue: :param value: the value for the label """ def __init__(self, - value: str) -> None: + value: str) -> None: self.value = value \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py index 5bd8b2f27d..e2c1fd5e8b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py +++ b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py @@ -13,17 +13,17 @@ # limitations under the License. #TODO -class BaseAggregation: +class Aggregation: """The base class for all aggregation types.""" -class CountAggregation(BaseAggregation): +class CountAggregation(Aggregation): """An aggregation representing a count of values.""" -class DistributonAggregation(BaseAggregation): +class DistributonAggregation(Aggregation): """An aggregation representing a distribution.""" -class LastValueAggregation(BaseAggregation): +class LastValueAggregation(Aggregation): """An aggregation representing the last value.""" -class SumAggregation(BaseAggregation): +class SumAggregation(Aggregation): """An aggregation representing a sum of values.""" From a44cb478d4967b3fcc0ff4c90ff8e0662862faa4 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 12:47:21 -0700 Subject: [PATCH 17/88] Fix comments --- docs/opentelemetry.metrics.aggregation.rst | 4 +- docs/opentelemetry.metrics.time_series.rst | 4 +- .../distributedcontext/__init__.py | 13 ++++ .../src/opentelemetry/metrics/__init__.py | 61 ++++--------------- 4 files changed, 28 insertions(+), 54 deletions(-) create mode 100644 opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py diff --git a/docs/opentelemetry.metrics.aggregation.rst b/docs/opentelemetry.metrics.aggregation.rst index c97f1bf72e..99848381e3 100644 --- a/docs/opentelemetry.metrics.aggregation.rst +++ b/docs/opentelemetry.metrics.aggregation.rst @@ -2,6 +2,4 @@ opentelemetry.metrics.aggregation module ========================================== .. automodule:: opentelemetry.metrics.aggregation - :members: - :undoc-members: - :show-inheritance: + diff --git a/docs/opentelemetry.metrics.time_series.rst b/docs/opentelemetry.metrics.time_series.rst index 976a81a821..16297d7eac 100644 --- a/docs/opentelemetry.metrics.time_series.rst +++ b/docs/opentelemetry.metrics.time_series.rst @@ -2,6 +2,4 @@ opentelemetry.metrics.time\_series module ========================================== .. automodule:: opentelemetry.metrics.time_series - :members: - :undoc-members: - :show-inheritance: + diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py new file mode 100644 index 0000000000..ee1b702864 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 444f2b79a2..65cf358bd3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -64,8 +64,8 @@ def create_float_counter(self, 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 identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `CounterFloat` """ @@ -86,8 +86,8 @@ def create_int_counter(self, 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 identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `CounterInt` @@ -109,8 +109,8 @@ def create_float_gauge(self, 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 identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `GaugeFloat` @@ -132,8 +132,8 @@ def create_int_gauge(self, 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 identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `GaugeInt` @@ -157,8 +157,8 @@ def create_int_measure(self, Order of the list is important as the same order MUST be used on recording when suppling values for these labels. aggregation: The type of aggregation to use for this measure metric. - span_context: The `SpanContext` that identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `MeasureInt` @@ -182,8 +182,8 @@ def create_float_measure(self, Order of the list is important as the same order MUST be used on recording when suppling values for these labels. aggregation: The type of aggregation to use for this measure metric. - span_context: The `SpanContext` that identified the `Span` - for which the metrics are associated with. + span_context: The `SpanContext` that identifies the `Span` + that the metric is associated with. Returns: A new `MeasureFloat` @@ -214,25 +214,6 @@ def get_or_create_time_series(self, with the return timeseries. """ - @abstractmethod - def get_default_time_series(self) -> 'object': - """Returns a timeseries, a container for a cumulative value. - - The timeseries will have all its labels not set (default). - """ - - def set_call_back(self, - updater_function: Callable[..., None] - ) -> None: - """Sets a callback that gets executed every time prior to exporting. - - This function MUST set the value of the `Metric` to the value that - that will be exported. - - args: - updater_function: The callback function to execute. - """ - def remove_time_series(self, label_values: LabelValues) -> None: """Removes the timeseries from the Metric, if present. @@ -259,9 +240,6 @@ def get_or_create_time_series(self, ) -> 'CounterTimeSeries': """Gets a `CounterTimeSeries` with a cumulative float value.""" - def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a `CounterTimeSeries` with a cumulative float value.""" - class CounterInt(Metric): """A counter type metric that holds int values. @@ -275,8 +253,6 @@ def get_or_create_time_series(self, ) -> 'CounterTimeSeries': """Gets a `CounterTimeSeries` with a cumulative int value.""" - def get_default_time_series(self) -> 'CounterTimeSeries': - """Returns a `CounterTimeSeries` with a cumulative int value.""" class GaugeFloat(Metric): """A gauge type metric that holds float values. @@ -289,9 +265,6 @@ def get_or_create_time_series(self, ) -> 'GaugeTimeSeries': """Gets a `GaugeTimeSeries` with a cumulative float value.""" - def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a `GaugeTimeSeries` with a cumulative float value.""" - class GaugeInt(Metric): """A gauge type metric that holds int values. @@ -304,9 +277,6 @@ def get_or_create_time_series(self, ) -> 'GaugeTimeSeries': """Gets a `GaugeTimeSeries` with a cumulative int value.""" - def get_default_time_series(self) -> 'GaugeTimeSeries': - """Returns a `GaugeTimeSeries` with a cumulative int value.""" - class MeasureFloat(Metric): """A measure type metric that holds float values. @@ -319,9 +289,6 @@ def get_or_create_time_series(self, ) -> 'MeasureTimeSeries': """Gets a `MeasureTimeSeries` with a cumulated float value.""" - def get_default_time_series(self) -> 'MeasureTimeSeries': - """Returns a `MeasureTimeSeries` with a cumulated float value.""" - class MeasureInt(Metric): """A measure type metric that holds int values. @@ -334,9 +301,6 @@ def get_or_create_time_series(self, ) -> 'MeasureTimeSeries': """Gets a `MeasureTimeSeries` with a cumulated int value.""" - def get_default_time_series(self) -> 'MeasureTimeSeries': - """Returns a `MeasureTimeSeries` with a cumulated int value.""" - class MeasureBatch: @@ -364,6 +328,7 @@ def __init__(self, self.key = key self.description = description + class LabelValue: """The label values associated with a TimeSeries. From 94eaad904412f4716d864f8813978c77c47dc7f8 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:01:42 -0700 Subject: [PATCH 18/88] fix lint --- .../opentelemetry/distributedcontext/__init__.py | 2 +- .../src/opentelemetry/metrics/__init__.py | 8 ++++---- .../metrics/examples/pre_aggregated.py | 12 ++++++------ .../src/opentelemetry/metrics/examples/raw.py | 13 ++++++------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py index ee1b702864..d853a7bcf6 100644 --- a/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py +++ b/opentelemetry-api/src/opentelemetry/distributedcontext/__init__.py @@ -10,4 +10,4 @@ # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -# limitations under the License. \ No newline at end of file +# limitations under the License. diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 65cf358bd3..eae913ecfb 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -156,7 +156,7 @@ def create_int_measure(self, 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. - aggregation: The type of aggregation to use for this measure metric. + aggregation: The type of aggregation to use for this metric. span_context: The `SpanContext` that identifies the `Span` that the metric is associated with. @@ -181,7 +181,7 @@ def create_float_measure(self, 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. - aggregation: The type of aggregation to use for this measure metric. + aggregation: The type of aggregation to use for this metric. span_context: The `SpanContext` that identifies the `Span` that the metric is associated with. @@ -304,7 +304,7 @@ def get_or_create_time_series(self, class MeasureBatch: - def record(self, metric_pairs): + def record(self, metric_pairs) -> None: """Records multiple observed values simultaneously. Args: @@ -337,4 +337,4 @@ class LabelValue: """ def __init__(self, value: str) -> None: - self.value = value \ No newline at end of file + self.value = value diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index 004d2943b3..f6ad137b39 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -18,12 +18,12 @@ from opentelemetry.metrics import Meter METER = Meter() -LABEL_KEYS = [LabelKey("environment", +LABEL_KEYS = [LabelKey("environment", "the environment the application is running in")] -COUNTER = METER.create_int_counter("sum numbers", - "sum numbers over time", - "number", - LABEL_KEYS) +COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover + "sum numbers over time", + "number", + LABEL_KEYS) LABEL_VALUE_TESTING = [LabelValue("Testing")] LABEL_VALUE_STAGING = [LabelValue("Staging")] @@ -32,4 +32,4 @@ COUNTER_METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_STAGING) for i in range(100): - COUNTER_METRIC_STAGING.add(i) \ No newline at end of file + COUNTER_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 2e2c84d21a..0c72ccb3fd 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import psutil - from opentelemetry.metrics import LabelKey from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter @@ -23,9 +21,10 @@ METER = Meter() LABEL_KEYS = [LabelKey("environment", "the environment the application is running in")] -MEASURE = METER.create_float_measure("idle_cpu_percentage", +MEASURE = METER.create_float_measure("idle_cpu_percentage", # pragma: no cover "cpu idle over time", "percentage", + LABEL_KEYS, LastValueAggregation) LABEL_VALUE_TESTING = [LabelValue("Testing")] LABEL_VALUE_STAGING = [LabelValue("Staging")] @@ -35,9 +34,9 @@ MEASURE_METRIC_STAGING = MEASURE.get_or_create_time_series(LABEL_VALUE_STAGING) # record individual measures -idle = psutil.cpu_times_percent().idle -MEASURE_METRIC_STAGING.record(idle) +STATISTIC = 100 +MEASURE_METRIC_STAGING.record(STATISTIC) # record multiple observed values -batch = MeasureBatch() -batch.record([(MEASURE_METRIC_TESTING, idle), (MEASURE_METRIC_STAGING, idle)]) +BATCH = MeasureBatch() +BATCH.record([(MEASURE_METRIC_TESTING, STATISTIC), (MEASURE_METRIC_STAGING, STATISTIC)]) From fc251b2c47ac30101dc61e96bbd93636936ec8fb Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:15:04 -0700 Subject: [PATCH 19/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 4 +- .../metrics/examples/pre_aggregated.py | 26 ++++++------- .../src/opentelemetry/metrics/examples/raw.py | 37 ++++++++++--------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index eae913ecfb..3cc635c19d 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, List +from typing import Any, Callable, Dict, List, Tuple from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries @@ -304,7 +304,7 @@ def get_or_create_time_series(self, class MeasureBatch: - def record(self, metric_pairs) -> None: + def record(self, metric_pairs: List[Tuple]) -> None: """Records multiple observed values simultaneously. Args: diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index f6ad137b39..ec019a16f3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -18,18 +18,18 @@ from opentelemetry.metrics import Meter METER = Meter() -LABEL_KEYS = [LabelKey("environment", - "the environment the application is running in")] -COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover - "sum numbers over time", - "number", - LABEL_KEYS) -LABEL_VALUE_TESTING = [LabelValue("Testing")] -LABEL_VALUE_STAGING = [LabelValue("Staging")] +# LABEL_KEYS = [LabelKey("environment", +# "the environment the application is running in")] +# COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover +# "sum numbers over time", +# "number", +# LABEL_KEYS) +# LABEL_VALUE_TESTING = [LabelValue("Testing")] +# LABEL_VALUE_STAGING = [LabelValue("Staging")] -# Metrics sent to some exporter -COUNTER_METRIC_TESTING = COUNTER.get_or_create_time_series(LABEL_VALUE_TESTING) -COUNTER_METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_STAGING) +# # Metrics sent to some exporter +# COUNTER_METRIC_TESTING = COUNTER.get_or_create_time_series(LABEL_VALUE_TESTING) +# COUNTER_METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_STAGING) -for i in range(100): - COUNTER_METRIC_STAGING.add(i) +# for i in range(100): +# COUNTER_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 0c72ccb3fd..9bdc704a6d 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -19,24 +19,25 @@ from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() -LABEL_KEYS = [LabelKey("environment", - "the environment the application is running in")] -MEASURE = METER.create_float_measure("idle_cpu_percentage", # pragma: no cover - "cpu idle over time", - "percentage", - LABEL_KEYS, - LastValueAggregation) -LABEL_VALUE_TESTING = [LabelValue("Testing")] -LABEL_VALUE_STAGING = [LabelValue("Staging")] +# LABEL_KEYS = [LabelKey("environment", +# "the environment the application is running in")] +# MEASURE = METER.create_float_measure("idle_cpu_percentage", +# "cpu idle over time", +# "percentage", +# LABEL_KEYS, +# LastValueAggregation) +# 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) +# # 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) -# record individual measures -STATISTIC = 100 -MEASURE_METRIC_STAGING.record(STATISTIC) +# # 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)]) +# # record multiple observed values +# BATCH = MeasureBatch() +# BATCH.record([(MEASURE_METRIC_TESTING, STATISTIC), \ +# (MEASURE_METRIC_STAGING, STATISTIC)]) From 262f312eafe65577423151acbf3b53b8432615fa Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:21:06 -0700 Subject: [PATCH 20/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3cc635c19d..a9cda03596 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, List, Tuple +from typing import Any, Callable, Dict, List, Tuple, Union from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries @@ -304,12 +304,14 @@ def get_or_create_time_series(self, class MeasureBatch: - def record(self, metric_pairs: List[Tuple]) -> None: + def record(self, + metrics: List['Metric'], + values: List[Union[float, int]]) -> None: """Records multiple observed values simultaneously. Args: - metric_pairs: A list of tuples containing the `Metric` and value - to be recorded. + metric: A list containing the `Metric` s to be recorded + values: A list containing the values to record """ From 66c0a56ac333d91bb71c714c64c3f9fe9e887ccb Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:26:38 -0700 Subject: [PATCH 21/88] skip examples --- .../metrics/examples/pre_aggregated.py | 2 +- .../src/opentelemetry/metrics/examples/raw.py | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index ec019a16f3..c175815999 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - +# pylint: skip-file from opentelemetry.metrics import LabelKey from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 9bdc704a6d..95a1be9eaa 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: skip-file from opentelemetry.metrics import LabelKey from opentelemetry.metrics import LabelValue from opentelemetry.metrics import Meter @@ -19,25 +20,25 @@ from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() -# LABEL_KEYS = [LabelKey("environment", -# "the environment the application is running in")] -# MEASURE = METER.create_float_measure("idle_cpu_percentage", -# "cpu idle over time", -# "percentage", -# LABEL_KEYS, -# LastValueAggregation) -# LABEL_VALUE_TESTING = [LabelValue("Testing")] -# LABEL_VALUE_STAGING = [LabelValue("Staging")] +LABEL_KEYS = [LabelKey("environment", + "the environment the application is running in")] +MEASURE = METER.create_float_measure("idle_cpu_percentage", + "cpu idle over time", + "percentage", + LABEL_KEYS, + LastValueAggregation) +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) +# 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) -# # record individual measures -# STATISTIC = 100 -# MEASURE_METRIC_STAGING.record(STATISTIC) +# 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)]) +# record multiple observed values +BATCH = MeasureBatch() +BATCH.record([(MEASURE_METRIC_TESTING, STATISTIC), \ + (MEASURE_METRIC_STAGING, STATISTIC)]) From e2c4a7eb646191b591a75a0c842f7917e27a39ce Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:32:24 -0700 Subject: [PATCH 22/88] white space --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index a9cda03596..3c0a08c907 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -304,7 +304,7 @@ def get_or_create_time_series(self, class MeasureBatch: - def record(self, + def record(self, metrics: List['Metric'], values: List[Union[float, int]]) -> None: """Records multiple observed values simultaneously. From 2fb76468ad2ae8b5186ab392fe05c7c54c1f7262 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 13:41:38 -0700 Subject: [PATCH 23/88] fix spacing --- .../src/opentelemetry/metrics/__init__.py | 2 +- .../src/opentelemetry/metrics/aggregation.py | 6 ++++- .../metrics/examples/pre_aggregated.py | 26 +++++++++---------- .../src/opentelemetry/metrics/examples/raw.py | 4 +-- .../src/opentelemetry/metrics/time_series.py | 1 - 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3c0a08c907..5b349e4d81 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, List, Tuple, Union +from typing import List, Union from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries diff --git a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py index e2c1fd5e8b..36f744b614 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py +++ b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py @@ -12,18 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -#TODO + class Aggregation: """The base class for all aggregation types.""" + class CountAggregation(Aggregation): """An aggregation representing a count of values.""" + class DistributonAggregation(Aggregation): """An aggregation representing a distribution.""" + class LastValueAggregation(Aggregation): """An aggregation representing the last value.""" + class SumAggregation(Aggregation): """An aggregation representing a sum of values.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index c175815999..61feb2d239 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -18,18 +18,18 @@ from opentelemetry.metrics import Meter METER = Meter() -# LABEL_KEYS = [LabelKey("environment", -# "the environment the application is running in")] -# COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover -# "sum numbers over time", -# "number", -# LABEL_KEYS) -# LABEL_VALUE_TESTING = [LabelValue("Testing")] -# LABEL_VALUE_STAGING = [LabelValue("Staging")] +LABEL_KEYS = [LabelKey("environment", + "the environment the application is running in")] +COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover + "sum numbers over time", + "number", + LABEL_KEYS) +LABEL_VALUE_TESTING = [LabelValue("Testing")] +LABEL_VALUE_STAGING = [LabelValue("Staging")] -# # Metrics sent to some exporter -# COUNTER_METRIC_TESTING = COUNTER.get_or_create_time_series(LABEL_VALUE_TESTING) -# COUNTER_METRIC_STAGING = COUNTER.get_or_create_time_series(LABEL_VALUE_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) -# for i in range(100): -# COUNTER_METRIC_STAGING.add(i) +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 95a1be9eaa..dc9840b53c 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -40,5 +40,5 @@ # record multiple observed values BATCH = MeasureBatch() -BATCH.record([(MEASURE_METRIC_TESTING, STATISTIC), \ - (MEASURE_METRIC_STAGING, STATISTIC)]) +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 e0e8eba22a..b6cc5af5cc 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -40,4 +40,3 @@ def record(self, value: typing.Union[float, int]) -> None: Logic depends on type of aggregation used for this measure. """ - \ No newline at end of file From eb711cb806ee766c97f9d7d28edc1ecf1e163662 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 16:25:12 -0700 Subject: [PATCH 24/88] fix imports --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 3 +-- opentelemetry-api/src/opentelemetry/metrics/examples/raw.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 5b349e4d81..3e63f40ff9 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,13 +28,12 @@ """ from abc import ABC, abstractmethod -from typing import List, Union - from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries from opentelemetry.metrics.time_series import GaugeTimeSeries from opentelemetry.metrics.time_series import MeasureTimeSeries from opentelemetry.trace import SpanContext +from typing import List, Union LabelKeys = List['LabelKey'] LabelValues = List['LabelValue'] diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index dc9840b53c..930523e001 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -15,8 +15,8 @@ # pylint: skip-file from opentelemetry.metrics import LabelKey from opentelemetry.metrics import LabelValue -from opentelemetry.metrics import Meter from opentelemetry.metrics import MeasureBatch +from opentelemetry.metrics import Meter from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() From baa3a323f8e19e5d17f3b8c1da00c23d75eec3a7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 22 Aug 2019 16:39:08 -0700 Subject: [PATCH 25/88] fix imports --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3e63f40ff9..8edf0efa20 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,13 +27,16 @@ """ -from abc import ABC, abstractmethod +from abc import ABC +from abc import abstractmethod +from typing import List +from typing import Union + from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import CounterTimeSeries from opentelemetry.metrics.time_series import GaugeTimeSeries from opentelemetry.metrics.time_series import MeasureTimeSeries from opentelemetry.trace import SpanContext -from typing import List, Union LabelKeys = List['LabelKey'] LabelValues = List['LabelValue'] From 211b20cdcd98a7f9d9cfd615b3a91e90b8776156 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 3 Sep 2019 11:22:07 -0700 Subject: [PATCH 26/88] LabelValues to str --- .../src/opentelemetry/metrics/__init__.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 8edf0efa20..48c6d3f469 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -39,7 +39,7 @@ from opentelemetry.trace import SpanContext LabelKeys = List['LabelKey'] -LabelValues = List['LabelValue'] +LabelValues = List[str] class Meter: @@ -212,7 +212,7 @@ def get_or_create_time_series(self, overrides this function. Args: - label_values: A map of `LabelValue` s that will be associated + label_values: A list of label values that will be associated with the return timeseries. """ @@ -220,7 +220,7 @@ def remove_time_series(self, label_values: LabelValues) -> None: """Removes the timeseries from the Metric, if present. - The timeseries with matching `LabelValue` s will be removed. + The timeseries with matching label values will be removed. args: label_values: The list of label values to match against. @@ -332,13 +332,3 @@ def __init__(self, self.key = key self.description = description - -class LabelValue: - """The label values associated with a TimeSeries. - - :type value: str - :param value: the value for the label - """ - def __init__(self, - value: str) -> None: - self.value = value From bffe040b3e1400b58b2e2a042d647204e01006af Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 3 Sep 2019 11:35:51 -0700 Subject: [PATCH 27/88] Black formatting --- .../src/opentelemetry/metrics/__init__.py | 152 +++++++++--------- .../metrics/examples/pre_aggregated.py | 15 +- .../src/opentelemetry/metrics/examples/raw.py | 22 +-- .../src/opentelemetry/metrics/time_series.py | 3 - 4 files changed, 98 insertions(+), 94 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 48c6d3f469..3ec6915993 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -38,7 +38,7 @@ from opentelemetry.metrics.time_series import MeasureTimeSeries from opentelemetry.trace import SpanContext -LabelKeys = List['LabelKey'] +LabelKeys = List["LabelKey"] LabelValues = List[str] @@ -50,13 +50,14 @@ 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 - ) -> 'CounterFloat': + def create_float_counter( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + span_context: SpanContext = None, + ) -> "CounterFloat": """Creates a counter type metric that contains float values. Args: @@ -72,13 +73,14 @@ def create_float_counter(self, Returns: A new `CounterFloat` """ - def create_int_counter(self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None - ) -> 'CounterInt': + def create_int_counter( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + span_context: SpanContext = None, + ) -> "CounterInt": """Creates a counter type metric that contains int values. Args: @@ -95,13 +97,14 @@ def create_int_counter(self, A new `CounterInt` """ - def create_float_gauge(self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None - ) -> 'GaugeFloat': + def create_float_gauge( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + span_context: SpanContext = None, + ) -> "GaugeFloat": """Creates a gauge type metric that contains float values. Args: @@ -118,13 +121,14 @@ def create_float_gauge(self, A new `GaugeFloat` """ - def create_int_gauge(self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - span_context: SpanContext = None - ) -> 'GaugeInt': + def create_int_gauge( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + span_context: SpanContext = None, + ) -> "GaugeInt": """Creates a gauge type metric that contains int values. Args: @@ -141,14 +145,15 @@ def create_int_gauge(self, A new `GaugeInt` """ - def create_int_measure(self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - aggregation: 'Aggregation', - span_context: SpanContext = None, - ) -> 'MeasureInt': + def create_int_measure( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + aggregation: "Aggregation", + span_context: SpanContext = None, + ) -> "MeasureInt": """Creates a measure used to record raw int values. Args: @@ -166,14 +171,15 @@ def create_int_measure(self, A new `MeasureInt` """ - def create_float_measure(self, - name: str, - description: str, - unit: str, - label_keys: LabelKeys, - aggregation: 'Aggregation', - span_context: SpanContext = None, - ) -> 'MeasureFloat': + def create_float_measure( + self, + name: str, + description: str, + unit: str, + label_keys: LabelKeys, + aggregation: "Aggregation", + span_context: SpanContext = None, + ) -> "MeasureFloat": """Creates a Measure used to record raw float values. Args: @@ -200,9 +206,7 @@ class Metric(ABC): """ @abstractmethod - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'object': + def get_or_create_time_series(self, label_values: LabelValues) -> "object": """Gets and returns a timeseries, a container for a cumulative value. If the provided label values are not already associated with this @@ -216,8 +220,7 @@ def get_or_create_time_series(self, with the return timeseries. """ - def remove_time_series(self, - label_values: LabelValues) -> None: + def remove_time_series(self, label_values: LabelValues) -> None: """Removes the timeseries from the Metric, if present. The timeseries with matching label values will be removed. @@ -237,9 +240,9 @@ class CounterFloat(Metric): Cumulative values cannot be negative. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'CounterTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "CounterTimeSeries": """Gets a `CounterTimeSeries` with a cumulative float value.""" @@ -250,9 +253,9 @@ class CounterInt(Metric): Cumulative values cannot be negative. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'CounterTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "CounterTimeSeries": """Gets a `CounterTimeSeries` with a cumulative int value.""" @@ -262,9 +265,9 @@ class GaugeFloat(Metric): Cumulative value can go both up and down. Values can be negative. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'GaugeTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "GaugeTimeSeries": """Gets a `GaugeTimeSeries` with a cumulative float value.""" @@ -274,9 +277,9 @@ class GaugeInt(Metric): Cumulative value can go both up and down. Values can be negative. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'GaugeTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "GaugeTimeSeries": """Gets a `GaugeTimeSeries` with a cumulative int value.""" @@ -286,9 +289,9 @@ class MeasureFloat(Metric): Measure metrics represent raw statistics that are recorded. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'MeasureTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "MeasureTimeSeries": """Gets a `MeasureTimeSeries` with a cumulated float value.""" @@ -298,17 +301,16 @@ class MeasureInt(Metric): Measure metrics represent raw statistics that are recorded. """ - def get_or_create_time_series(self, - label_values: LabelValues - ) -> 'MeasureTimeSeries': + def get_or_create_time_series( + self, label_values: LabelValues + ) -> "MeasureTimeSeries": """Gets a `MeasureTimeSeries` with a cumulated int value.""" class MeasureBatch: - - def record(self, - metrics: List['Metric'], - values: List[Union[float, int]]) -> None: + def record( + self, metrics: List["Metric"], values: List[Union[float, int]] + ) -> None: """Records multiple observed values simultaneously. Args: @@ -326,9 +328,7 @@ class LabelKey: :type description: str :param description: description of the label """ - def __init__(self, - key: str, - description: str) -> None: + + def __init__(self, key: str, description: str) -> None: self.key = key self.description = description - diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index 61feb2d239..1f48d56f82 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -18,12 +18,15 @@ from opentelemetry.metrics import Meter METER = Meter() -LABEL_KEYS = [LabelKey("environment", - "the environment the application is running in")] -COUNTER = METER.create_int_counter("sum numbers", # pragma: no cover - "sum numbers over time", - "number", - LABEL_KEYS) +LABEL_KEYS = [ + LabelKey("environment", "the environment the application is running in") +] +COUNTER = METER.create_int_counter( + "sum numbers", # pragma: no cover + "sum numbers over time", + "number", + LABEL_KEYS, +) LABEL_VALUE_TESTING = [LabelValue("Testing")] LABEL_VALUE_STAGING = [LabelValue("Staging")] diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 930523e001..6eac549d09 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -20,13 +20,16 @@ from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() -LABEL_KEYS = [LabelKey("environment", - "the environment the application is running in")] -MEASURE = METER.create_float_measure("idle_cpu_percentage", - "cpu idle over time", - "percentage", - LABEL_KEYS, - LastValueAggregation) +LABEL_KEYS = [ + LabelKey("environment", "the environment the application is running in") +] +MEASURE = METER.create_float_measure( + "idle_cpu_percentage", + "cpu idle over time", + "percentage", + LABEL_KEYS, + LastValueAggregation, +) LABEL_VALUE_TESTING = [LabelValue("Testing")] LABEL_VALUE_STAGING = [LabelValue("Staging")] @@ -40,5 +43,6 @@ # record multiple observed values BATCH = MeasureBatch() -BATCH.record([(MEASURE_METRIC_TESTING, STATISTIC), - (MEASURE_METRIC_STAGING, STATISTIC)]) +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 b6cc5af5cc..b14ef973ad 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -16,7 +16,6 @@ class CounterTimeSeries: - def add(self, value: typing.Union[float, int]) -> None: """Adds the given value to the current value. Cannot be negative.""" @@ -28,13 +27,11 @@ def set(self, value: typing.Union[float, int]) -> None: class GaugeTimeSeries: - def set(self, value: typing.Union[float, int]) -> None: """Sets the current value to the given value. Can be negative.""" class MeasureTimeSeries: - def record(self, value: typing.Union[float, int]) -> None: """Records the given value to this measure. From 0759b9afdc3536aec24303a430ecaa87988b5b45 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 3 Sep 2019 11:44:01 -0700 Subject: [PATCH 28/88] fix isort --- .../src/opentelemetry/metrics/__init__.py | 14 +++++++------- .../metrics/examples/pre_aggregated.py | 4 +--- .../src/opentelemetry/metrics/examples/raw.py | 5 +---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3ec6915993..d6e03f7741 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,15 +27,15 @@ """ -from abc import ABC -from abc import abstractmethod -from typing import List -from typing import Union +from abc import ABC, abstractmethod +from typing import List, Union from opentelemetry.metrics.aggregation import Aggregation -from opentelemetry.metrics.time_series import CounterTimeSeries -from opentelemetry.metrics.time_series import GaugeTimeSeries -from opentelemetry.metrics.time_series import MeasureTimeSeries +from opentelemetry.metrics.time_series import ( + CounterTimeSeries, + GaugeTimeSeries, + MeasureTimeSeries, +) from opentelemetry.trace import SpanContext LabelKeys = List["LabelKey"] diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index 1f48d56f82..c9c55f01b8 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -13,9 +13,7 @@ # limitations under the License. # pylint: skip-file -from opentelemetry.metrics import LabelKey -from opentelemetry.metrics import LabelValue -from opentelemetry.metrics import Meter +from opentelemetry.metrics import LabelKey, LabelValue, Meter METER = Meter() LABEL_KEYS = [ diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index 6eac549d09..3c82e14d53 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -13,10 +13,7 @@ # limitations under the License. # pylint: skip-file -from opentelemetry.metrics import LabelKey -from opentelemetry.metrics import LabelValue -from opentelemetry.metrics import MeasureBatch -from opentelemetry.metrics import Meter +from opentelemetry.metrics import LabelKey, LabelValue, MeasureBatch, Meter from opentelemetry.metrics.aggregation import LastValueAggregation METER = Meter() From 44d62f8fe60fe7437bfa6d87c4c0e67311136b7c Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 12 Sep 2019 14:00:33 -0700 Subject: [PATCH 29/88] Remove aggregation --- .../src/opentelemetry/metrics/__init__.py | 5 --- .../src/opentelemetry/metrics/aggregation.py | 33 ------------------- 2 files changed, 38 deletions(-) delete mode 100644 opentelemetry-api/src/opentelemetry/metrics/aggregation.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index d6e03f7741..75f56f0545 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -30,7 +30,6 @@ from abc import ABC, abstractmethod from typing import List, Union -from opentelemetry.metrics.aggregation import Aggregation from opentelemetry.metrics.time_series import ( CounterTimeSeries, GaugeTimeSeries, @@ -151,7 +150,6 @@ def create_int_measure( description: str, unit: str, label_keys: LabelKeys, - aggregation: "Aggregation", span_context: SpanContext = None, ) -> "MeasureInt": """Creates a measure used to record raw int values. @@ -163,7 +161,6 @@ def create_int_measure( 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. - aggregation: The type of aggregation to use for this metric. span_context: The `SpanContext` that identifies the `Span` that the metric is associated with. @@ -177,7 +174,6 @@ def create_float_measure( description: str, unit: str, label_keys: LabelKeys, - aggregation: "Aggregation", span_context: SpanContext = None, ) -> "MeasureFloat": """Creates a Measure used to record raw float values. @@ -189,7 +185,6 @@ def create_float_measure( 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. - aggregation: The type of aggregation to use for this metric. span_context: The `SpanContext` that identifies the `Span` that the metric is associated with. diff --git a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py b/opentelemetry-api/src/opentelemetry/metrics/aggregation.py deleted file mode 100644 index 36f744b614..0000000000 --- a/opentelemetry-api/src/opentelemetry/metrics/aggregation.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class Aggregation: - """The base class for all aggregation types.""" - - -class CountAggregation(Aggregation): - """An aggregation representing a count of values.""" - - -class DistributonAggregation(Aggregation): - """An aggregation representing a distribution.""" - - -class LastValueAggregation(Aggregation): - """An aggregation representing the last value.""" - - -class SumAggregation(Aggregation): - """An aggregation representing a sum of values.""" From c5ab2df38927c07774749789d356a8985d60a09b Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 12 Sep 2019 14:09:59 -0700 Subject: [PATCH 30/88] Fix names --- .../src/opentelemetry/metrics/__init__.py | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 75f56f0545..45e12f2b74 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -56,7 +56,7 @@ def create_float_counter( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "CounterFloat": + ) -> "FloatCounter": """Creates a counter type metric that contains float values. Args: @@ -69,7 +69,7 @@ def create_float_counter( span_context: The `SpanContext` that identifies the `Span` that the metric is associated with. - Returns: A new `CounterFloat` + Returns: A new `FloatCounter` """ def create_int_counter( @@ -79,7 +79,7 @@ def create_int_counter( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "CounterInt": + ) -> "IntCounter": """Creates a counter type metric that contains int values. Args: @@ -93,7 +93,7 @@ def create_int_counter( that the metric is associated with. Returns: - A new `CounterInt` + A new `IntCounter` """ def create_float_gauge( @@ -103,7 +103,7 @@ def create_float_gauge( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "GaugeFloat": + ) -> "FloatGauge": """Creates a gauge type metric that contains float values. Args: @@ -117,7 +117,7 @@ def create_float_gauge( that the metric is associated with. Returns: - A new `GaugeFloat` + A new `FloatGauge` """ def create_int_gauge( @@ -127,7 +127,7 @@ def create_int_gauge( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "GaugeInt": + ) -> "IntGauge": """Creates a gauge type metric that contains int values. Args: @@ -141,7 +141,7 @@ def create_int_gauge( that the metric is associated with. Returns: - A new `GaugeInt` + A new `IntGauge` """ def create_int_measure( @@ -151,7 +151,7 @@ def create_int_measure( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "MeasureInt": + ) -> "IntMeasure": """Creates a measure used to record raw int values. Args: @@ -165,7 +165,7 @@ def create_int_measure( that the metric is associated with. Returns: - A new `MeasureInt` + A new `IntMeasure` """ def create_float_measure( @@ -175,7 +175,7 @@ def create_float_measure( unit: str, label_keys: LabelKeys, span_context: SpanContext = None, - ) -> "MeasureFloat": + ) -> "FloatMeasure": """Creates a Measure used to record raw float values. Args: @@ -189,7 +189,7 @@ def create_float_measure( that the metric is associated with. Returns: - A new `MeasureFloat` + A new `FloatMeasure` """ @@ -228,7 +228,7 @@ def clear(self) -> None: """Removes all timeseries from the `Metric`.""" -class CounterFloat(Metric): +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. @@ -241,7 +241,7 @@ def get_or_create_time_series( """Gets a `CounterTimeSeries` with a cumulative float value.""" -class CounterInt(Metric): +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. @@ -254,7 +254,7 @@ def get_or_create_time_series( """Gets a `CounterTimeSeries` with a cumulative int value.""" -class GaugeFloat(Metric): +class FloatGauge(Metric): """A gauge type metric that holds float values. Cumulative value can go both up and down. Values can be negative. @@ -266,7 +266,7 @@ def get_or_create_time_series( """Gets a `GaugeTimeSeries` with a cumulative float value.""" -class GaugeInt(Metric): +class IntGauge(Metric): """A gauge type metric that holds int values. Cumulative value can go both up and down. Values can be negative. @@ -278,7 +278,7 @@ def get_or_create_time_series( """Gets a `GaugeTimeSeries` with a cumulative int value.""" -class MeasureFloat(Metric): +class FloatMeasure(Metric): """A measure type metric that holds float values. Measure metrics represent raw statistics that are recorded. @@ -290,7 +290,7 @@ def get_or_create_time_series( """Gets a `MeasureTimeSeries` with a cumulated float value.""" -class MeasureInt(Metric): +class IntMeasure(Metric): """A measure type metric that holds int values. Measure metrics represent raw statistics that are recorded. @@ -302,18 +302,6 @@ def get_or_create_time_series( """Gets a `MeasureTimeSeries` with a cumulated int value.""" -class MeasureBatch: - def record( - self, metrics: List["Metric"], values: List[Union[float, int]] - ) -> None: - """Records multiple observed values simultaneously. - - Args: - metric: A list containing the `Metric` s to be recorded - values: A list containing the values to record - """ - - class LabelKey: """The label keys associated with the metric. From 50d2de55ce6c1b7b6179c31d8883729b105702eb Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 12 Sep 2019 14:12:21 -0700 Subject: [PATCH 31/88] Remove aggregation from docs --- docs/opentelemetry.metrics.aggregation.rst | 5 ----- docs/opentelemetry.metrics.rst | 1 - 2 files changed, 6 deletions(-) delete mode 100644 docs/opentelemetry.metrics.aggregation.rst diff --git a/docs/opentelemetry.metrics.aggregation.rst b/docs/opentelemetry.metrics.aggregation.rst deleted file mode 100644 index 99848381e3..0000000000 --- a/docs/opentelemetry.metrics.aggregation.rst +++ /dev/null @@ -1,5 +0,0 @@ -opentelemetry.metrics.aggregation module -========================================== - -.. automodule:: opentelemetry.metrics.aggregation - diff --git a/docs/opentelemetry.metrics.rst b/docs/opentelemetry.metrics.rst index b388627133..2d025d3197 100644 --- a/docs/opentelemetry.metrics.rst +++ b/docs/opentelemetry.metrics.rst @@ -6,7 +6,6 @@ Submodules .. toctree:: - opentelemetry.metrics.aggregation opentelemetry.metrics.time_series Module contents From d79bc7d8eb4b01ee4c0c98faec91fa478699fa23 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 12 Sep 2019 14:20:30 -0700 Subject: [PATCH 32/88] Fix lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 45e12f2b74..68563bd492 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod -from typing import List, Union +from typing import List from opentelemetry.metrics.time_series import ( CounterTimeSeries, From 7fb7cdfacda9b66e5fce44ecc5d0345173dd1b75 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 10:29:02 -0700 Subject: [PATCH 33/88] metric changes --- .../src/opentelemetry/metrics/__init__.py | 321 +++++++----------- .../metrics/examples/pre_aggregated.py | 16 +- .../src/opentelemetry/metrics/examples/raw.py | 25 +- .../src/opentelemetry/metrics/time_series.py | 14 +- 4 files changed, 138 insertions(+), 238 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 68563bd492..f496b838a3 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,9 +37,6 @@ ) from opentelemetry.trace import SpanContext -LabelKeys = List["LabelKey"] -LabelValues = List[str] - class Meter: """An interface to allow the recording of metrics. @@ -48,149 +45,107 @@ class Meter: counter), or raw values (measure) in which the aggregation and labels for the exported metric are deferred. """ + # TODO: RecordBatch + + +class ValueType(Enum): + FLOAT = 0 + INT = 1 + + +def create_counter( + name: str, + description: str, + unit: str, + value_type: "ValueType", + 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 metric. + 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. + 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 with corresponding value_type. + """ - 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` - """ - - 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_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. +def create_gauge( + self, + name: str, + description: str, + unit: str, + value_type: "ValueType", + 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. + 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 with corresponding value_type. + """ - 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( + self, + name: str, + description: str, + unit: str, + value_type: "ValueType", + 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. + 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 with corresponding value_type. + """ class Metric(ABC): @@ -201,8 +156,8 @@ class Metric(ABC): """ @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 and returns a timeseries, a container for a counter value. If the provided label values are not already associated with this metric, a new timeseries is returned, otherwise it returns the existing @@ -215,7 +170,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 +184,55 @@ 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 - """ + """Gets a `MeasureTimeSeries` with an int value.""" - def __init__(self, key: str, description: str) -> None: - self.key = key - self.description = description diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index c9c55f01b8..065c8e62bb 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") -] 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 3c82e14d53..e4035972c0 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -13,33 +13,22 @@ # 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 b14ef973ad..22ddf71ad1 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.""" - - 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. + """Adds the given value to the current 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.""" From 79322f5558d78e37f89f26d99a46145c17567abc Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 11:40:06 -0700 Subject: [PATCH 34/88] Typing --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index f496b838a3..4700d33983 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -61,7 +61,7 @@ def create_counter( bidirectional: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, -) -> Union[FloatCounter, IntCounter]: +) -> Union["FloatCounter", "IntCounter"]: """Creates a counter metric with type value_type. By default, counter values can only go up (unidirectional). The API @@ -94,7 +94,7 @@ def create_gauge( unidirectional: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, -) -> Union[FloatGauge, IntGauge]: +) -> Union["FloatGauge", "IntGauge"]: """Creates a gauge metric with type value_type. By default, gauge values can go both up and down (bidirectional). The API @@ -125,7 +125,7 @@ def create_measure( non_negative: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, -) -> Union[FloatMeasure, IntMeasure]: +) -> Union["FloatMeasure", "IntMeasure"]: """Creates a measure metric with type value_type. Measure metrics represent raw statistics that are recorded. As an option, From f9c7cc3830946760ed8b2c10e0d21ea888ca4a80 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 14:08:07 -0700 Subject: [PATCH 35/88] Fix lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index e597c7eee3..9a46e5e234 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -45,6 +45,7 @@ class Meter: counter), or raw values (measure) in which the aggregation and labels for the exported metric are deferred. """ + # TODO: RecordBatch @@ -86,7 +87,6 @@ def create_counter( def create_gauge( - self, name: str, description: str, unit: str, @@ -116,8 +116,8 @@ def create_gauge( Returns: A new gauge metric with corresponding value_type. """ + def create_measure( - self, name: str, description: str, unit: str, @@ -196,7 +196,7 @@ class IntCounter(Metric): """A counter type metric that holds int values.""" def get_or_create_time_series( - self, label_values: List[str], + self, label_values: List[str] ) -> "CounterTimeSeries": """Gets a `CounterTimeSeries` with an int value.""" From 44659294737c5cd835eb564f7d2c4f4ce3e10350 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 14:19:11 -0700 Subject: [PATCH 36/88] Fix lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- opentelemetry-api/src/opentelemetry/metrics/time_series.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 9a46e5e234..1213c7abc9 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -37,7 +37,7 @@ ) from opentelemetry.trace import SpanContext - +#pylint: disable=unused-argument class Meter: """An interface to allow the recording of metrics. diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/time_series.py index 22ddf71ad1..25ad9efe77 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/time_series.py @@ -18,7 +18,7 @@ class CounterTimeSeries: def add(self, value: typing.Union[float, int]) -> None: """Adds the given value to the current value. - + The input value cannot be negative if not bidirectional. """ From 695e596449066e667d6c142aaf274d6d57095ea2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 14:25:47 -0700 Subject: [PATCH 37/88] Add space --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 1213c7abc9..5e080b0d04 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -37,7 +37,7 @@ ) from opentelemetry.trace import SpanContext -#pylint: disable=unused-argument +# pylint: disable=unused-argument class Meter: """An interface to allow the recording of metrics. From ee43e3945e05c40d813e254e0beec3a538bb77f3 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 13 Sep 2019 14:32:34 -0700 Subject: [PATCH 38/88] Fix lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 1 + .../src/opentelemetry/metrics/examples/pre_aggregated.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 5e080b0d04..a17d48e218 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -37,6 +37,7 @@ ) from opentelemetry.trace import SpanContext + # pylint: disable=unused-argument class Meter: """An interface to allow the recording of metrics. diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py index 065c8e62bb..a07610b2ea 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -15,7 +15,7 @@ # pylint: skip-file from opentelemetry import metrics -METER = Meter() +METER = metrics.Meter() COUNTER = METER.create_int_counter( "sum numbers", "sum numbers over time", From e59f7c4981b4592fde09de481f9597e65a985ea6 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 16 Sep 2019 12:07:26 -0700 Subject: [PATCH 39/88] fix comments --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index a17d48e218..af0da981bc 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -153,12 +153,12 @@ 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: List[str]) -> "object": - """Gets and returns a timeseries, a container for a counter value. + """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 From 99460284800a91982e6238ea2f588f3237721d8f Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 16 Sep 2019 14:00:48 -0700 Subject: [PATCH 40/88] handle, recordbatch --- ...s.rst => opentelemetry.metrics.handle.rst} | 2 +- docs/opentelemetry.metrics.rst | 2 +- .../src/opentelemetry/metrics/__init__.py | 89 +++++++++++-------- .../metrics/examples/pre_aggregated.py | 6 +- .../src/opentelemetry/metrics/examples/raw.py | 7 +- .../metrics/{time_series.py => handle.py} | 6 +- 6 files changed, 65 insertions(+), 47 deletions(-) rename docs/{opentelemetry.metrics.time_series.rst => opentelemetry.metrics.handle.rst} (63%) rename opentelemetry-api/src/opentelemetry/metrics/{time_series.py => handle.py} (93%) diff --git a/docs/opentelemetry.metrics.time_series.rst b/docs/opentelemetry.metrics.handle.rst similarity index 63% rename from docs/opentelemetry.metrics.time_series.rst rename to docs/opentelemetry.metrics.handle.rst index 16297d7eac..236f40669a 100644 --- a/docs/opentelemetry.metrics.time_series.rst +++ b/docs/opentelemetry.metrics.handle.rst @@ -1,5 +1,5 @@ opentelemetry.metrics.time\_series module ========================================== -.. automodule:: opentelemetry.metrics.time_series +.. automodule:: opentelemetry.metrics.handle diff --git a/docs/opentelemetry.metrics.rst b/docs/opentelemetry.metrics.rst index 2d025d3197..289f1842ba 100644 --- a/docs/opentelemetry.metrics.rst +++ b/docs/opentelemetry.metrics.rst @@ -6,7 +6,7 @@ Submodules .. toctree:: - opentelemetry.metrics.time_series + opentelemetry.metrics.handle Module contents --------------- diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index af0da981bc..b0da507ceb 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,12 +28,12 @@ """ from abc import ABC, abstractmethod from enum import Enum -from typing import List, Union +from typing import List, Tuple, Union -from opentelemetry.metrics.time_series import ( - CounterTimeSeries, - GaugeTimeSeries, - MeasureTimeSeries, +from opentelemetry.metrics.handle import ( + CounterHandle, + GaugeHandle, + MeasureHandle, ) from opentelemetry.trace import SpanContext @@ -47,7 +47,21 @@ class Meter: for the exported metric are deferred. """ - # TODO: RecordBatch + def record_batch( + self, + record_tuples: List[Tuple["MeasureHandle", Union[float, int]]] + ) -> None: + """Atomically records a batch of `MeasureHandle` and value pairs. + + Recording batches will be restricted to measure metrics. Allows the + functionality of acting upon multiple metric handles with a single + API call. + + Args: + record_tuples: A list of pairs of `MeasureHandle`s and the + corresponding value to record for that handle. + """ + class ValueType(Enum): @@ -153,86 +167,87 @@ 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. + handle that the metric holds. """ @abstractmethod - def get_or_create_time_series(self, label_values: List[str]) -> "object": - """Gets a timeseries, used for repeated-use of metrics instruments. + def get_handle(self, label_values: List[str]) -> "object": + """Gets a handle, 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 - timeseries with the exact label values. The timeseries returned - contains logic and behaviour specific to the type of metric that - overrides this function. + Handles are useful to reduce the cost of repeatedly recording a metric + with a pre-defined set of label values. All metric kinds (counter, + gauge, measure) support declaring a set of required label keys. The + values corresponding to these keys should be specified in every handle. + "Unspecified" label values, in cases where a handle is requested but + a value was not provided are permitted. Args: label_values: A list of label values that will be associated - with the return timeseries. + with the return handle. """ - def remove_time_series(self, label_values: List[str]) -> None: - """Removes the timeseries from the Metric, if present. + def remove_handle(self, label_values: List[str]) -> None: + """Removes the handle from the Metric, if present. - The timeseries with matching label values will be removed. + The handle with matching label values will be removed. args: label_values: The list of label values to match against. """ def clear(self) -> None: - """Removes all timeseries from the `Metric`.""" + """Removes all handles from the `Metric`.""" class FloatCounter(Metric): """A counter type metric that holds float values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "CounterTimeSeries": - """Gets a `CounterTimeSeries` with a float value.""" + ) -> "CounterHandle": + """Gets a `CounterHandle` with a float value.""" class IntCounter(Metric): """A counter type metric that holds int values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "CounterTimeSeries": - """Gets a `CounterTimeSeries` with an int value.""" + ) -> "CounterHandle": + """Gets a `CounterHandle` with an int value.""" class FloatGauge(Metric): """A gauge type metric that holds float values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "GaugeTimeSeries": - """Gets a `GaugeTimeSeries` with a float value.""" + ) -> "GaugeHandle": + """Gets a `GaugeHandle` with a float value.""" class IntGauge(Metric): """A gauge type metric that holds int values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "GaugeTimeSeries": - """Gets a `GaugeTimeSeries` with an int value.""" + ) -> "GaugeHandle": + """Gets a `GaugeHandle` with an int value.""" class FloatMeasure(Metric): """A measure type metric that holds float values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "MeasureTimeSeries": - """Gets a `MeasureTimeSeries` with a float value.""" + ) -> "MeasureHandle": + """Gets a `MeasureHandle` with a float value.""" class IntMeasure(Metric): """A measure type metric that holds int values.""" - def get_or_create_time_series( + def get_handle( self, label_values: List[str] - ) -> "MeasureTimeSeries": - """Gets a `MeasureTimeSeries` with an int value.""" + ) -> "MeasureHandle": + """Gets a `MeasureHandle` 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 a07610b2ea..3cbf4d3d20 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py @@ -16,7 +16,7 @@ from opentelemetry import metrics METER = metrics.Meter() -COUNTER = METER.create_int_counter( +COUNTER = METER.create_counter( "sum numbers", "sum numbers over time", "number", @@ -25,8 +25,8 @@ ) # Metrics sent to some exporter -METRIC_TESTING = COUNTER.get_or_create_time_series("Testing") -METRIC_STAGING = COUNTER.get_or_create_time_series("Staging") +METRIC_TESTING = COUNTER.get_handle("Testing") +METRIC_STAGING = COUNTER.get_handle("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 ada44da704..fb09f053d0 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -25,9 +25,12 @@ ) # Metrics sent to some exporter -MEASURE_METRIC_TESTING = MEASURE.get_or_create_time_series("Testing") -MEASURE_METRIC_STAGING = MEASURE.get_or_create_time_series("Staging") +MEASURE_METRIC_TESTING = MEASURE.get_handle("Testing") +MEASURE_METRIC_STAGING = MEASURE.get_handle("Staging") # record individual measures STATISTIC = 100 MEASURE_METRIC_STAGING.record(STATISTIC) + +# Batch recording +meter.record_batch([tuple(MEASURE_METRIC_STAGING, STATISTIC)]) diff --git a/opentelemetry-api/src/opentelemetry/metrics/time_series.py b/opentelemetry-api/src/opentelemetry/metrics/handle.py similarity index 93% rename from opentelemetry-api/src/opentelemetry/metrics/time_series.py rename to opentelemetry-api/src/opentelemetry/metrics/handle.py index 25ad9efe77..8b53eebf6b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/time_series.py +++ b/opentelemetry-api/src/opentelemetry/metrics/handle.py @@ -15,7 +15,7 @@ import typing -class CounterTimeSeries: +class CounterHandle(): def add(self, value: typing.Union[float, int]) -> None: """Adds the given value to the current value. @@ -23,11 +23,11 @@ def add(self, value: typing.Union[float, int]) -> None: """ -class GaugeTimeSeries: +class GaugeHandle(): def set(self, value: typing.Union[float, int]) -> None: """Sets the current value to the given value. Can be negative.""" -class MeasureTimeSeries: +class MeasureHandle(): def record(self, value: typing.Union[float, int]) -> None: """Records the given value to this measure.""" From e1a3101d04495a8b4771171b472765cf2fe101e1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 16 Sep 2019 14:27:22 -0700 Subject: [PATCH 41/88] docs --- docs/opentelemetry.metrics.handle.rst | 2 +- .../src/opentelemetry/metrics/__init__.py | 30 +++++-------------- .../src/opentelemetry/metrics/examples/raw.py | 2 +- .../src/opentelemetry/metrics/handle.py | 6 ++-- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/docs/opentelemetry.metrics.handle.rst b/docs/opentelemetry.metrics.handle.rst index 236f40669a..826a0e4e5a 100644 --- a/docs/opentelemetry.metrics.handle.rst +++ b/docs/opentelemetry.metrics.handle.rst @@ -1,4 +1,4 @@ -opentelemetry.metrics.time\_series module +opentelemetry.metrics.handle module ========================================== .. automodule:: opentelemetry.metrics.handle diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index b0da507ceb..5008d21ba8 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -48,8 +48,7 @@ class Meter: """ def record_batch( - self, - record_tuples: List[Tuple["MeasureHandle", Union[float, int]]] + self, record_tuples: List[Tuple["MeasureHandle", Union[float, int]]] ) -> None: """Atomically records a batch of `MeasureHandle` and value pairs. @@ -58,12 +57,11 @@ def record_batch( API call. Args: - record_tuples: A list of pairs of `MeasureHandle`s and the + record_tuples: A list of pairs of `MeasureHandle` s and the corresponding value to record for that handle. """ - class ValueType(Enum): FLOAT = 0 INT = 1 @@ -202,52 +200,40 @@ def clear(self) -> None: class FloatCounter(Metric): """A counter type metric that holds float values.""" - def get_handle( - self, label_values: List[str] - ) -> "CounterHandle": + def get_handle(self, label_values: List[str]) -> "CounterHandle": """Gets a `CounterHandle` with a float value.""" class IntCounter(Metric): """A counter type metric that holds int values.""" - def get_handle( - self, label_values: List[str] - ) -> "CounterHandle": + def get_handle(self, label_values: List[str]) -> "CounterHandle": """Gets a `CounterHandle` with an int value.""" class FloatGauge(Metric): """A gauge type metric that holds float values.""" - def get_handle( - self, label_values: List[str] - ) -> "GaugeHandle": + def get_handle(self, label_values: List[str]) -> "GaugeHandle": """Gets a `GaugeHandle` with a float value.""" class IntGauge(Metric): """A gauge type metric that holds int values.""" - def get_handle( - self, label_values: List[str] - ) -> "GaugeHandle": + def get_handle(self, label_values: List[str]) -> "GaugeHandle": """Gets a `GaugeHandle` with an int value.""" class FloatMeasure(Metric): """A measure type metric that holds float values.""" - def get_handle( - self, label_values: List[str] - ) -> "MeasureHandle": + def get_handle(self, label_values: List[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" class IntMeasure(Metric): """A measure type metric that holds int values.""" - def get_handle( - self, label_values: List[str] - ) -> "MeasureHandle": + def get_handle(self, label_values: List[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with an int value.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py index fb09f053d0..501b2e5b61 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py @@ -33,4 +33,4 @@ MEASURE_METRIC_STAGING.record(STATISTIC) # Batch recording -meter.record_batch([tuple(MEASURE_METRIC_STAGING, STATISTIC)]) +METER.record_batch([tuple(MEASURE_METRIC_STAGING, STATISTIC)]) diff --git a/opentelemetry-api/src/opentelemetry/metrics/handle.py b/opentelemetry-api/src/opentelemetry/metrics/handle.py index 8b53eebf6b..5da3cf43e7 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/handle.py +++ b/opentelemetry-api/src/opentelemetry/metrics/handle.py @@ -15,7 +15,7 @@ import typing -class CounterHandle(): +class CounterHandle: def add(self, value: typing.Union[float, int]) -> None: """Adds the given value to the current value. @@ -23,11 +23,11 @@ def add(self, value: typing.Union[float, int]) -> None: """ -class GaugeHandle(): +class GaugeHandle: def set(self, value: typing.Union[float, int]) -> None: """Sets the current value to the given value. Can be negative.""" -class MeasureHandle(): +class MeasureHandle: def record(self, value: typing.Union[float, int]) -> None: """Records the given value to this measure.""" From 47d520911af3dede6e0702b39ec2cac5fa4a40b2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 16 Sep 2019 14:53:22 -0700 Subject: [PATCH 42/88] Update recordbatch --- .../src/opentelemetry/metrics/__init__.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 5008d21ba8..f4514ac73e 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod from enum import Enum -from typing import List, Tuple, Union +from typing import Dict, List, Tuple, Union from opentelemetry.metrics.handle import ( CounterHandle, @@ -48,17 +48,21 @@ class Meter: """ def record_batch( - self, record_tuples: List[Tuple["MeasureHandle", Union[float, int]]] + self, + label_tuples: Dict[str, str], + record_tuples: List[Tuple["Metric", Union[float, int]]] ) -> None: - """Atomically records a batch of `MeasureHandle` and value pairs. + """Atomically records a batch of `Metric` and value pairs. - Recording batches will be restricted to measure metrics. Allows the - functionality of acting upon multiple metric handles with a single - API call. + Allows the functionality of acting upon multiple metrics with + a single API call. Implementations should find handles that match + the key-value pairs in the label tuples. Args: - record_tuples: A list of pairs of `MeasureHandle` s and the - corresponding value to record for that handle. + label_tuples: A collection of key value pairs that will be matched + against to record for the metric-handle that has those labels. + record_tuples: A list of pairs of `Metric` s and the + corresponding value to record for that metric. """ From 2553b890b5de0e2aa049cf5817cbb85236b083e8 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 16 Sep 2019 15:10:15 -0700 Subject: [PATCH 43/88] black --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index f4514ac73e..7e9d2559a6 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -50,7 +50,7 @@ class Meter: def record_batch( self, label_tuples: Dict[str, str], - record_tuples: List[Tuple["Metric", Union[float, int]]] + record_tuples: List[Tuple["Metric", Union[float, int]]], ) -> None: """Atomically records a batch of `Metric` and value pairs. From ebcc567d6c6aa8249f56eb89ed85aa7319b3b7bd Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 18 Sep 2019 17:35:10 -0700 Subject: [PATCH 44/88] Fix typo --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index b8f4fda7c0..7ea68db8e6 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -173,8 +173,8 @@ class Metric(ABC): """ @abstractmethod - def get_or_create_time_series(self, label_values: List[str]) -> "object": - """Gets a timeseries, used for repeated-use of metrics instruments. + def get_handle(self, label_values: List[str]) -> "object": + """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric with a pre-defined set of label values. All metric kinds (counter, From 41ee12c7fd6884d59fcdb56773f8738527087d5f Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 19 Sep 2019 10:44:54 -0700 Subject: [PATCH 45/88] remove ValueType --- .../src/opentelemetry/metrics/__init__.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 7ea68db8e6..335877721b 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,7 +28,7 @@ """ from abc import ABC, abstractmethod from enum import Enum -from typing import Dict, List, Tuple, Union +from typing import Dict, List, Tuple, Type, Union from opentelemetry.metrics.handle import ( CounterHandle, @@ -66,16 +66,11 @@ def record_batch( """ -class ValueType(Enum): - FLOAT = 0 - INT = 1 - - def create_counter( name: str, description: str, unit: str, - value_type: "ValueType", + value_type: Union[Type[float], Type[int]], is_bidirectional: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, @@ -107,7 +102,7 @@ def create_gauge( name: str, description: str, unit: str, - value_type: "ValueType", + value_type: Union[Type[float], Type[int]], is_unidirectional: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, @@ -138,7 +133,7 @@ def create_measure( name: str, description: str, unit: str, - value_type: "ValueType", + value_type: Union[Type[float], Type[int]], is_non_negative: bool = False, label_keys: List[str] = None, span_context: SpanContext = None, From 62862a281b87249d9c1b99238597d9708d246d5f Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 19 Sep 2019 11:42:30 -0700 Subject: [PATCH 46/88] fix lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 335877721b..668c61cbc3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,7 +27,6 @@ """ from abc import ABC, abstractmethod -from enum import Enum from typing import Dict, List, Tuple, Type, Union from opentelemetry.metrics.handle import ( From b99dd3f1dcb7d8812466423f0527a80540d78a3a Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 19 Sep 2019 16:47:24 -0700 Subject: [PATCH 47/88] sdk --- docs/opentelemetry.metrics.handle.rst | 5 - docs/opentelemetry.metrics.rst | 7 - .../src/opentelemetry/metrics/__init__.py | 197 ++++++++++-------- .../src/opentelemetry/metrics/handle.py | 33 --- .../src/opentelemetry/sdk/__init__.py | 4 +- .../src/opentelemetry/sdk/common/__init__.py | 128 ++++++++++++ .../src/opentelemetry/sdk/metrics/__init__.py | 95 +++++++++ .../src/opentelemetry/sdk/trace/__init__.py | 113 +--------- 8 files changed, 335 insertions(+), 247 deletions(-) delete mode 100644 docs/opentelemetry.metrics.handle.rst delete mode 100644 opentelemetry-api/src/opentelemetry/metrics/handle.py create mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py create mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py diff --git a/docs/opentelemetry.metrics.handle.rst b/docs/opentelemetry.metrics.handle.rst deleted file mode 100644 index 826a0e4e5a..0000000000 --- a/docs/opentelemetry.metrics.handle.rst +++ /dev/null @@ -1,5 +0,0 @@ -opentelemetry.metrics.handle module -========================================== - -.. automodule:: opentelemetry.metrics.handle - diff --git a/docs/opentelemetry.metrics.rst b/docs/opentelemetry.metrics.rst index 289f1842ba..358d5491a6 100644 --- a/docs/opentelemetry.metrics.rst +++ b/docs/opentelemetry.metrics.rst @@ -1,13 +1,6 @@ opentelemetry.metrics package ============================= -Submodules ----------- - -.. toctree:: - - opentelemetry.metrics.handle - Module contents --------------- diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 668c61cbc3..5b108552e6 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -65,98 +65,101 @@ def record_batch( """ -def create_counter( - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - 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_counter( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + 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_gauge( - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - 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_gauge( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + 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_measure( - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - 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. - """ + + def create_measure( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + 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): @@ -235,3 +238,21 @@ class IntMeasure(Metric): def get_handle(self, label_values: List[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with an int value.""" + + +class CounterHandle: + def add(self, value: Union[float, int]) -> None: + """Adds the given value to the current value. + + The input value cannot be negative if not bidirectional. + """ + + +class GaugeHandle: + def set(self, value: Union[float, int]) -> None: + """Sets the current value to the given value. Can be negative.""" + + +class MeasureHandle: + def record(self, value: Union[float, int]) -> None: + """Records the given value to this measure.""" diff --git a/opentelemetry-api/src/opentelemetry/metrics/handle.py b/opentelemetry-api/src/opentelemetry/metrics/handle.py deleted file mode 100644 index 5da3cf43e7..0000000000 --- a/opentelemetry-api/src/opentelemetry/metrics/handle.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import typing - - -class CounterHandle: - def add(self, value: typing.Union[float, int]) -> None: - """Adds the given value to the current value. - - The input value cannot be negative if not bidirectional. - """ - - -class GaugeHandle: - def set(self, value: typing.Union[float, int]) -> None: - """Sets the current value to the given value. Can be negative.""" - - -class MeasureHandle: - def record(self, value: typing.Union[float, int]) -> None: - """Records the given value to this measure.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/__init__.py index 81366d9d47..0f3bff4571 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/__init__.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from . import trace, util +from . import metrics, trace, util -__all__ = ["trace", "util"] +__all__ = ["metrics", "trace", "util"] diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py new file mode 100644 index 0000000000..1927221d16 --- /dev/null +++ b/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py @@ -0,0 +1,128 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import threading + +from collections import OrderedDict, deque + +try: + # pylint: disable=ungrouped-imports + from collections.abc import MutableMapping + from collections.abc import Sequence +except ImportError: + # pylint: disable=no-name-in-module,ungrouped-imports + from collections import MutableMapping + from collections import Sequence + + +class BoundedList(Sequence): + """An append only list with a fixed max size.""" + + def __init__(self, maxlen): + self.dropped = 0 + self._dq = deque(maxlen=maxlen) + self._lock = threading.Lock() + + def __repr__(self): + return "{}({}, maxlen={})".format( + type(self).__name__, list(self._dq), self._dq.maxlen + ) + + def __getitem__(self, index): + return self._dq[index] + + def __len__(self): + return len(self._dq) + + def __iter__(self): + with self._lock: + return iter(self._dq.copy()) + + def append(self, item): + with self._lock: + if len(self._dq) == self._dq.maxlen: + self.dropped += 1 + self._dq.append(item) + + def extend(self, seq): + with self._lock: + to_drop = len(seq) + len(self._dq) - self._dq.maxlen + if to_drop > 0: + self.dropped += to_drop + self._dq.extend(seq) + + @classmethod + def from_seq(cls, maxlen, seq): + seq = tuple(seq) + if len(seq) > maxlen: + raise ValueError + bounded_list = cls(maxlen) + # pylint: disable=protected-access + bounded_list._dq = deque(seq, maxlen=maxlen) + return bounded_list + + +class BoundedDict(MutableMapping): + """A dict with a fixed max capacity.""" + + def __init__(self, maxlen): + if not isinstance(maxlen, int): + raise ValueError + if maxlen < 0: + raise ValueError + self.maxlen = maxlen + self.dropped = 0 + self._dict = OrderedDict() + self._lock = threading.Lock() + + def __repr__(self): + return "{}({}, maxlen={})".format( + type(self).__name__, dict(self._dict), self.maxlen + ) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + with self._lock: + if self.maxlen == 0: + self.dropped += 1 + return + + if key in self._dict: + del self._dict[key] + elif len(self._dict) == self.maxlen: + del self._dict[next(iter(self._dict.keys()))] + self.dropped += 1 + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + + def __iter__(self): + with self._lock: + return iter(self._dict.copy()) + + def __len__(self): + return len(self._dict) + + @classmethod + def from_map(cls, maxlen, mapping): + mapping = OrderedDict(mapping) + if len(mapping) > maxlen: + raise ValueError + bounded_dict = cls(maxlen) + # pylint: disable=protected-access + bounded_dict._dict = mapping + return bounded_dict diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py new file mode 100644 index 0000000000..c7c1092fc9 --- /dev/null +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -0,0 +1,95 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Dict, List, Tuple, Type, Union +from opentelemetry import metrics, trace as metrics_api, trace_api + + +class Meter(metrics_api.Meter): + """See `opentelemetry.metrics.Meter`. + + Args: + name: The name of the meter. + """ + def __init__(self, name: str = "") -> None: + self.name = name + + def record_batch( + self, + label_tuples: Dict[str, str], + record_tuples: List[Tuple[metrics_api.Metric, Union[float, int]]], + ) -> None: + pass + + + def create_counter( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + is_bidirectional: bool = False, + label_keys: List[str] = None, + span_context: trace_api.SpanContext = None, + ) -> Union[metrics_api.FloatCounter, metrics_api.IntCounter]: + """See `opentelemetry.metrics.Meter.create_counter`.""" + +class FloatCounter(metrics_api.FloatCounter): + """See `opentelemetry.metrics.FloatCounter`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + is_bidirectional: bool = False, + label_keys: List[str] = None, + span_context: trace_api.SpanContext = None, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.is_bidirectional = is_bidirectional + self.label_keys = label_keys + self.span_context = span_context + self.handles = {} + + def get_handle(self, + label_values: List[str]) -> metrics_api.CounterHandle: + """See `opentelemetry.metrics.FloatCounter.get_handle`.""" + handle = self.handles.get(label_values, CounterHandle(float, self.is_bidirectional)) + self.handles[label_values] = handle + return handle + + +class CounterHandle(metrics_api.CounterHandle): + + def __init__( + self, + value_type: Union[Type[float], Type[int]], + is_bidirectional: bool) -> None: + self.counter_data = 0 + self.type = value_type + self.is_bidirectional = is_bidirectional + + def add(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.CounterHandle.add`.""" + if not self.is_bidirectional and value < 0: + raise ValueError("Unidirectional counter cannot descend.") + if not isinstance(value, self.value_type): + raise ValueError("Invalid value passed for " + self.value_type) + self.counter_data += value + + +meter = Meter() \ No newline at end of file diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index a694476e1f..39a3df21e8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -17,132 +17,21 @@ import random import threading import typing -from collections import OrderedDict, deque from contextlib import contextmanager from opentelemetry import trace as trace_api from opentelemetry.context import Context from opentelemetry.sdk import util +from opentelemetry.sdk.common import BoundedDict, BoundedList from opentelemetry.util import types logger = logging.getLogger(__name__) -try: - # pylint: disable=ungrouped-imports - from collections.abc import MutableMapping - from collections.abc import Sequence -except ImportError: - # pylint: disable=no-name-in-module,ungrouped-imports - from collections import MutableMapping - from collections import Sequence - MAX_NUM_ATTRIBUTES = 32 MAX_NUM_EVENTS = 128 MAX_NUM_LINKS = 32 -class BoundedList(Sequence): - """An append only list with a fixed max size.""" - - def __init__(self, maxlen): - self.dropped = 0 - self._dq = deque(maxlen=maxlen) - self._lock = threading.Lock() - - def __repr__(self): - return "{}({}, maxlen={})".format( - type(self).__name__, list(self._dq), self._dq.maxlen - ) - - def __getitem__(self, index): - return self._dq[index] - - def __len__(self): - return len(self._dq) - - def __iter__(self): - with self._lock: - return iter(self._dq.copy()) - - def append(self, item): - with self._lock: - if len(self._dq) == self._dq.maxlen: - self.dropped += 1 - self._dq.append(item) - - def extend(self, seq): - with self._lock: - to_drop = len(seq) + len(self._dq) - self._dq.maxlen - if to_drop > 0: - self.dropped += to_drop - self._dq.extend(seq) - - @classmethod - def from_seq(cls, maxlen, seq): - seq = tuple(seq) - if len(seq) > maxlen: - raise ValueError - bounded_list = cls(maxlen) - # pylint: disable=protected-access - bounded_list._dq = deque(seq, maxlen=maxlen) - return bounded_list - - -class BoundedDict(MutableMapping): - """A dict with a fixed max capacity.""" - - def __init__(self, maxlen): - if not isinstance(maxlen, int): - raise ValueError - if maxlen < 0: - raise ValueError - self.maxlen = maxlen - self.dropped = 0 - self._dict = OrderedDict() - self._lock = threading.Lock() - - def __repr__(self): - return "{}({}, maxlen={})".format( - type(self).__name__, dict(self._dict), self.maxlen - ) - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - with self._lock: - if self.maxlen == 0: - self.dropped += 1 - return - - if key in self._dict: - del self._dict[key] - elif len(self._dict) == self.maxlen: - del self._dict[next(iter(self._dict.keys()))] - self.dropped += 1 - self._dict[key] = value - - def __delitem__(self, key): - del self._dict[key] - - def __iter__(self): - with self._lock: - return iter(self._dict.copy()) - - def __len__(self): - return len(self._dict) - - @classmethod - def from_map(cls, maxlen, mapping): - mapping = OrderedDict(mapping) - if len(mapping) > maxlen: - raise ValueError - bounded_dict = cls(maxlen) - # pylint: disable=protected-access - bounded_dict._dict = mapping - return bounded_dict - - class SpanProcessor: """Interface which allows hooks for SDK's `Span`s start and end method invocations. From dda296697660cad9f951cdca534e9f9ac665b025 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 19 Sep 2019 17:39:36 -0700 Subject: [PATCH 48/88] metrics --- .../src/opentelemetry/metrics/__init__.py | 18 +++---- .../src/opentelemetry/sdk/metrics/__init__.py | 47 +++++++++++++++++-- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 5b108552e6..6a4ea9c888 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,13 +27,8 @@ """ from abc import ABC, abstractmethod -from typing import Dict, List, Tuple, Type, Union +from typing import List, Tuple, Type, Union -from opentelemetry.metrics.handle import ( - CounterHandle, - GaugeHandle, - MeasureHandle, -) from opentelemetry.trace import SpanContext @@ -48,18 +43,19 @@ class Meter: def record_batch( self, - label_tuples: Dict[str, str], + label_values: List[str], record_tuples: List[Tuple["Metric", Union[float, int]]], ) -> None: """Atomically records a batch of `Metric` and value pairs. Allows the functionality of acting upon multiple metrics with - a single API call. Implementations should find handles that match - the key-value pairs in the label tuples. + a single API call. Implementations should find metric and handles that + match the key-value pairs in the label tuples. Args: - label_tuples: A collection of key value pairs that will be matched - against to record for the metric-handle that has those labels. + label_values: A list of label values that will be matched + against to record for the handles under each metric that has + those labels. record_tuples: A list of pairs of `Metric` s and the corresponding value to record for that metric. """ diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index c7c1092fc9..35413b54f6 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, List, Tuple, Type, Union +from typing import List, Tuple, Type, Union from opentelemetry import metrics, trace as metrics_api, trace_api @@ -27,11 +27,11 @@ def __init__(self, name: str = "") -> None: def record_batch( self, - label_tuples: Dict[str, str], + label_values: List[str], record_tuples: List[Tuple[metrics_api.Metric, Union[float, int]]], ) -> None: - pass - + """See `opentelemetry.metrics.Meter.record_batch`.""" + def create_counter( self, @@ -44,6 +44,17 @@ def create_counter( span_context: trace_api.SpanContext = None, ) -> Union[metrics_api.FloatCounter, metrics_api.IntCounter]: """See `opentelemetry.metrics.Meter.create_counter`.""" + counter_class = FloatCounter + if value_type == int: + counter_class = IntCounter + return counter_class( + name, + description, + unit, + is_bidirectional=is_bidirectional, + label_keys=label_keys, + span_context=span_context) + class FloatCounter(metrics_api.FloatCounter): """See `opentelemetry.metrics.FloatCounter`.""" @@ -73,6 +84,34 @@ def get_handle(self, return handle +class IntCounter(metrics_api.IntCounter): + """See `opentelemetry.metrics.IntCounter`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + is_bidirectional: bool = False, + label_keys: List[str] = None, + span_context: trace_api.SpanContext = None, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.is_bidirectional = is_bidirectional + self.label_keys = label_keys + self.span_context = span_context + self.handles = {} + + def get_handle(self, + label_values: List[str]) -> metrics_api.CounterHandle: + """See `opentelemetry.metrics.IntCounter.get_handle`.""" + handle = self.handles.get(label_values, CounterHandle(int, self.is_bidirectional)) + self.handles[label_values] = handle + return handle + + class CounterHandle(metrics_api.CounterHandle): def __init__( From 38e51309a238ab8cca5dff42280887338b8e6e67 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 20 Sep 2019 12:37:36 -0700 Subject: [PATCH 49/88] example --- .../src/opentelemetry/metrics/__init__.py | 47 ++++++++++++++++++- .../metrics_example.py | 23 ++++++--- .../src/opentelemetry/sdk/metrics/__init__.py | 17 ++++--- .../opentelemetry/sdk/metrics/test_metrics.py | 23 --------- opentelemetry-sdk/tests/metrics/__init__.py | 13 +++++ 5 files changed, 86 insertions(+), 37 deletions(-) rename opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py => opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py (68%) rename opentelemetry-api/src/opentelemetry/metrics/examples/raw.py => opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py (51%) create mode 100644 opentelemetry-sdk/tests/metrics/__init__.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 6a4ea9c888..76d6d87454 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,9 +27,10 @@ """ from abc import ABC, abstractmethod -from typing import List, Tuple, Type, Union +from typing import Callable, List, Optional, Tuple, Type, Union from opentelemetry.trace import SpanContext +from opentelemetry.util import loader # pylint: disable=unused-argument @@ -157,6 +158,50 @@ def create_measure( Returns: A new measure metric for values of the given value_type. """ +# Once https://github.com/python/mypy/issues/7092 is resolved, +# the following type definition should be replaced with +# from opentelemetry.util.loader import ImplementationFactory +ImplementationFactory = Callable[ + [Type[Meter]], Optional[Meter] +] + +_METER = None +_METER_FACTORY = None + +def meter() -> Meter: + """Gets the current global :class:`~.Meter` object. + + If there isn't one set yet, a default will be loaded. + """ + global _METER, _METER_FACTORY # pylint:disable=global-statement + + if _METER is None: + # pylint:disable=protected-access + _METER = loader._load_impl(Meter, _METER_FACTORY) + del _METER_FACTORY + + return _METER + + +def set_preferred_meter_implementation( + factory: ImplementationFactory +) -> None: + """Set the factory to be used to create the meter. + + See :mod:`opentelemetry.util.loader` for details. + + This function may not be called after a meter is already loaded. + + Args: + factory: Callback that should create a new :class:`Meter` instance. + """ + global _METER, _METER_FACTORY # pylint:disable=global-statement + + if _METER: + raise RuntimeError("Meter already loaded.") + + _METER_FACTORY = factory + class Metric(ABC): """Base class for various types of metrics. diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py similarity index 68% rename from opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py rename to opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index 3cbf4d3d20..4048895306 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/pre_aggregated.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -11,22 +11,31 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +""" +This module serves as an example for a simple application using metrics +""" -# pylint: skip-file from opentelemetry import metrics +from opentelemetry.sdk.metrics import Meter + -METER = metrics.Meter() +metrics.set_preferred_meter_implementation(lambda _: Meter()) +METER = metrics.meter() COUNTER = METER.create_counter( "sum numbers", "sum numbers over time", "number", - metrics.ValueType.FLOAT, + int, + False, ["environment"], ) -# Metrics sent to some exporter -METRIC_TESTING = COUNTER.get_handle("Testing") -METRIC_STAGING = COUNTER.get_handle("Staging") +counter_handle = COUNTER.get_handle("Staging") for i in range(100): - METRIC_STAGING.add(i) + counter_handle.add(i) + +print(counter_handle.data) + +# TODO: exporters diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 35413b54f6..3dd31e0a32 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,7 +13,8 @@ # limitations under the License. from typing import List, Tuple, Type, Union -from opentelemetry import metrics, trace as metrics_api, trace_api +from opentelemetry import metrics as metrics_api +from opentelemetry import trace as trace_api class Meter(metrics_api.Meter): @@ -31,7 +32,11 @@ def record_batch( record_tuples: List[Tuple[metrics_api.Metric, Union[float, int]]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" - + for tuple in record_tuples: + handle = tuple[0].get_handle(label_values) + if isinstance(handle, CounterHandle): + handle.add(tuple[1]) + def create_counter( self, @@ -118,8 +123,8 @@ def __init__( self, value_type: Union[Type[float], Type[int]], is_bidirectional: bool) -> None: - self.counter_data = 0 - self.type = value_type + self.data = 0 + self.value_type = value_type self.is_bidirectional = is_bidirectional def add(self, value: Union[float, int]) -> None: @@ -127,8 +132,8 @@ def add(self, value: Union[float, int]) -> None: if not self.is_bidirectional and value < 0: raise ValueError("Unidirectional counter cannot descend.") if not isinstance(value, self.value_type): - raise ValueError("Invalid value passed for " + self.value_type) - self.counter_data += value + raise ValueError("Invalid value passed for " + self.value_type.__name__) + self.data += value meter = Meter() \ No newline at end of file diff --git a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py similarity index 51% rename from opentelemetry-api/src/opentelemetry/metrics/examples/raw.py rename to opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py index 501b2e5b61..d853a7bcf6 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/examples/raw.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py @@ -11,26 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -# pylint: skip-file -from opentelemetry import metrics - -METER = metrics.Meter() -MEASURE = metrics.create_measure( - "idle_cpu_percentage", - "cpu idle over time", - "percentage", - metrics.ValueType.FLOAT, - ["environment"], -) - -# Metrics sent to some exporter -MEASURE_METRIC_TESTING = MEASURE.get_handle("Testing") -MEASURE_METRIC_STAGING = MEASURE.get_handle("Staging") - -# record individual measures -STATISTIC = 100 -MEASURE_METRIC_STAGING.record(STATISTIC) - -# Batch recording -METER.record_batch([tuple(MEASURE_METRIC_STAGING, STATISTIC)]) diff --git a/opentelemetry-sdk/tests/metrics/__init__.py b/opentelemetry-sdk/tests/metrics/__init__.py new file mode 100644 index 0000000000..d853a7bcf6 --- /dev/null +++ b/opentelemetry-sdk/tests/metrics/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. From 006bd89388441f76c59086740b07e4dc0d911a40 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 20 Sep 2019 22:32:03 -0700 Subject: [PATCH 50/88] counter --- .../src/opentelemetry/metrics/__init__.py | 104 +++++++++++------- .../metrics_example.py | 12 +- .../src/opentelemetry/sdk/metrics/__init__.py | 79 +++++++------ .../opentelemetry/sdk/metrics/test_metrics.py | 13 --- .../tests/metrics/test_metrics.py | 56 ++++++++++ 5 files changed, 175 insertions(+), 89 deletions(-) delete mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py create mode 100644 opentelemetry-sdk/tests/metrics/test_metrics.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 76d6d87454..0ac7ba18ff 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -29,7 +29,6 @@ from abc import ABC, abstractmethod from typing import Callable, List, Optional, Tuple, Type, Union -from opentelemetry.trace import SpanContext from opentelemetry.util import loader @@ -68,28 +67,27 @@ def create_counter( description: str, unit: str, value_type: Union[Type[float], Type[int]], - is_bidirectional: bool = False, label_keys: List[str] = None, - span_context: SpanContext = None, + disabled: bool = False, + non_monotonic: bool = False, ) -> 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. + By default, counter values can only go up (monotonic). The API + should reject negative inputs to monotonic counter metrics. + Counter metrics that have a non_monotonic option set to True allows + 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. + disabled: True value tells the SDK not to report by default. + non_monotonic: Set to true to allow negative inputs. Returns: A new counter metric for values of the given value_type. """ @@ -101,27 +99,26 @@ def create_gauge( description: str, unit: str, value_type: Union[Type[float], Type[int]], - is_unidirectional: bool = False, label_keys: List[str] = None, - span_context: SpanContext = None, + disabled: bool = False, + is_monotonic: bool = False, ) -> 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. + By default, gauge values can go both up and down (non-monotic). The API + allows for an optional is_monotonic flag, in which when set to True 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. + disabled: True value tells the SDK not to report by default. + is_monotonic: Set to true to reject negative inputs. Returns: A new gauge metric for values of the given value_type. """ @@ -133,9 +130,9 @@ def create_measure( description: str, unit: str, value_type: Union[Type[float], Type[int]], - is_non_negative: bool = False, label_keys: List[str] = None, - span_context: SpanContext = None, + disabled: bool = False, + non_negative: bool = False, ) -> Union["FloatMeasure", "IntMeasure"]: """Creates a measure metric with type value_type. @@ -148,12 +145,11 @@ def create_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. + disabled: True value tells the SDK not to report by default. + non_negative: Set to true to reject negative inputs. Returns: A new measure metric for values of the given value_type. """ @@ -211,7 +207,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: List[str]) -> "object": + def get_handle(self, label_values: Tuple[str]) -> "object": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -222,17 +218,17 @@ def get_handle(self, label_values: List[str]) -> "object": a value was not provided are permitted. Args: - label_values: A list of label values that will be associated + label_values: A tuple of label values that will be associated with the return handle. """ - def remove_handle(self, label_values: List[str]) -> None: + def remove_handle(self, label_values: Tuple[str]) -> None: """Removes the handle from the Metric, if present. The handle with matching label values will be removed. args: - label_values: The list of label values to match against. + label_values: The tuple of label values to match against. """ def clear(self) -> None: @@ -242,58 +238,88 @@ def clear(self) -> None: class FloatCounter(Metric): """A counter type metric that holds float values.""" - def get_handle(self, label_values: List[str]) -> "CounterHandle": + def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": """Gets a `CounterHandle` with a float value.""" class IntCounter(Metric): """A counter type metric that holds int values.""" - def get_handle(self, label_values: List[str]) -> "CounterHandle": + def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": """Gets a `CounterHandle` with an int value.""" class FloatGauge(Metric): """A gauge type metric that holds float values.""" - def get_handle(self, label_values: List[str]) -> "GaugeHandle": + def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": """Gets a `GaugeHandle` with a float value.""" class IntGauge(Metric): """A gauge type metric that holds int values.""" - def get_handle(self, label_values: List[str]) -> "GaugeHandle": + def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": """Gets a `GaugeHandle` with an int value.""" class FloatMeasure(Metric): """A measure type metric that holds float values.""" - def get_handle(self, label_values: List[str]) -> "MeasureHandle": + def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" class IntMeasure(Metric): """A measure type metric that holds int values.""" - def get_handle(self, label_values: List[str]) -> "MeasureHandle": + def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with an int value.""" -class CounterHandle: - def add(self, value: Union[float, int]) -> None: +class BaseHandle(): + """An interface for metric handles.""" + + @abstractmethod + def update(self, value: Union[float, int]) -> None: + """A generic update method to alter the value of the handle. + + Useful for record_batch, where the type of the handle does not + matter. Implementation should call the appropriate method that + alters the underlying data for that handle type. + """ + + +class CounterHandle(BaseHandle): + def update(self, value: Union[float, int]) -> None: + """Alters the value of the counter handle. + + Implementations should call _add(). + """ + + def _add(self, value: Union[float, int]) -> None: """Adds the given value to the current value. The input value cannot be negative if not bidirectional. """ -class GaugeHandle: - def set(self, value: Union[float, int]) -> None: +class GaugeHandle(BaseHandle): + def update(self, value: Union[float, int]) -> None: + """Alters the value of the gauge handle. + + Implementations should call _set(). + """ + def _set(self, value: Union[float, int]) -> None: """Sets the current value to the given value. Can be negative.""" -class MeasureHandle: - def record(self, value: Union[float, int]) -> None: +class MeasureHandle(BaseHandle): + def update(self, value: Union[float, int]) -> None: + """Alters the value of the measure handle. + + Implementations should call _record(). + """ + + def _record(self, value: Union[float, int]) -> None: """Records the given value to this measure.""" diff --git a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index 4048895306..c59c31c214 100644 --- a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -21,8 +21,8 @@ metrics.set_preferred_meter_implementation(lambda _: Meter()) -METER = metrics.meter() -COUNTER = METER.create_counter( +meter = metrics.meter() +counter = meter.create_counter( "sum numbers", "sum numbers over time", "number", @@ -31,10 +31,12 @@ ["environment"], ) -counter_handle = COUNTER.get_handle("Staging") +label_values = ("staging") -for i in range(100): - counter_handle.add(i) +counter_handle = counter.get_handle(label_values) + +counter_handle.update(100) +meter.record_batch(label_values, [(counter, 50)]) print(counter_handle.data) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 3dd31e0a32..3e3b8ef4d4 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -12,9 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging + from typing import List, Tuple, Type, Union from opentelemetry import metrics as metrics_api -from opentelemetry import trace as trace_api + +logger = logging.getLogger(__name__) class Meter(metrics_api.Meter): @@ -28,14 +31,13 @@ def __init__(self, name: str = "") -> None: def record_batch( self, - label_values: List[str], + label_values: Tuple[str], record_tuples: List[Tuple[metrics_api.Metric, Union[float, int]]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" - for tuple in record_tuples: - handle = tuple[0].get_handle(label_values) - if isinstance(handle, CounterHandle): - handle.add(tuple[1]) + for pair in record_tuples: + handle = pair[0].get_handle(label_values) + handle.update(pair[1]) def create_counter( @@ -44,21 +46,19 @@ def create_counter( description: str, unit: str, value_type: Union[Type[float], Type[int]], - is_bidirectional: bool = False, label_keys: List[str] = None, - span_context: trace_api.SpanContext = None, + disabled: bool = False, + non_monotonic: bool = False, ) -> Union[metrics_api.FloatCounter, metrics_api.IntCounter]: """See `opentelemetry.metrics.Meter.create_counter`.""" - counter_class = FloatCounter - if value_type == int: - counter_class = IntCounter + counter_class = FloatCounter if value_type == float else IntCounter return counter_class( name, description, unit, - is_bidirectional=is_bidirectional, label_keys=label_keys, - span_context=span_context) + disabled=disabled, + non_monotonic=non_monotonic) class FloatCounter(metrics_api.FloatCounter): @@ -69,22 +69,24 @@ def __init__( name: str, description: str, unit: str, - is_bidirectional: bool = False, label_keys: List[str] = None, - span_context: trace_api.SpanContext = None, + disabled: bool = False, + non_monotonic: bool = False, ) -> None: self.name = name self.description = description self.unit = unit - self.is_bidirectional = is_bidirectional self.label_keys = label_keys - self.span_context = span_context + self.disabled = disabled + self.non_monotonic = non_monotonic self.handles = {} def get_handle(self, - label_values: List[str]) -> metrics_api.CounterHandle: + label_values: Tuple[str]) -> metrics_api.CounterHandle: """See `opentelemetry.metrics.FloatCounter.get_handle`.""" - handle = self.handles.get(label_values, CounterHandle(float, self.is_bidirectional)) + handle = self.handles.get( + label_values, + CounterHandle(float, self.disabled, self.non_monotonic)) self.handles[label_values] = handle return handle @@ -97,22 +99,24 @@ def __init__( name: str, description: str, unit: str, - is_bidirectional: bool = False, label_keys: List[str] = None, - span_context: trace_api.SpanContext = None, + disabled: bool = False, + non_monotonic: bool = False, ) -> None: self.name = name self.description = description self.unit = unit - self.is_bidirectional = is_bidirectional self.label_keys = label_keys - self.span_context = span_context + self.disabled = disabled + self.non_monotonic = non_monotonic self.handles = {} def get_handle(self, label_values: List[str]) -> metrics_api.CounterHandle: """See `opentelemetry.metrics.IntCounter.get_handle`.""" - handle = self.handles.get(label_values, CounterHandle(int, self.is_bidirectional)) + handle = self.handles.get( + label_values, + CounterHandle(int, self.disabled, self.non_monotonic)) self.handles[label_values] = handle return handle @@ -122,17 +126,28 @@ class CounterHandle(metrics_api.CounterHandle): def __init__( self, value_type: Union[Type[float], Type[int]], - is_bidirectional: bool) -> None: + disabled: bool, + non_monotonic: bool) -> None: self.data = 0 self.value_type = value_type - self.is_bidirectional = is_bidirectional - - def add(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.CounterHandle.add`.""" - if not self.is_bidirectional and value < 0: - raise ValueError("Unidirectional counter cannot descend.") + self.disabled = disabled + self.non_monotonic = non_monotonic + + def update(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.CounterHandle.update`.""" + self._add(value) + + def _add(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.CounterHandle._add`.""" + if self.disabled: + logger.warning("Counter metric is disabled.") + return + if not self.non_monotonic and value < 0: + logger.warning("Monotonic counter cannot descend.") + return if not isinstance(value, self.value_type): - raise ValueError("Invalid value passed for " + self.value_type.__name__) + logger.warning("Invalid value passed for %s", self.value_type.__name__) + return self.data += value diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py deleted file mode 100644 index d853a7bcf6..0000000000 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/test_metrics.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py new file mode 100644 index 0000000000..3083e81455 --- /dev/null +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -0,0 +1,56 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from unittest import mock + +from opentelemetry import metrics as metrics_api +from opentelemetry.sdk import metrics + + +class TestMeter(unittest.TestCase): + def test_extends_api(self): + meter = metrics.Meter() + self.assertIsInstance(meter, metrics_api.Meter) + + def test_record_batch(self): + meter = metrics.Meter() + label_keys = ["key1"] + label_values = ("value1") + float_counter = metrics.FloatCounter( + "name", + "desc", + "unit", + label_keys, + ) + record_tuples = [(float_counter, 1.0)] + meter.record_batch(label_values, record_tuples) + self.assertEqual(float_counter.get_handle(label_values).data, 1.0) + + def test_record_batch_exists(self): + meter = metrics.Meter() + label_keys = ["key1"] + label_values = ("value1") + float_counter = metrics.FloatCounter( + "name", + "desc", + "unit", + label_keys + ) + handle = float_counter.get_handle(label_values) + handle.update(1.0) + record_tuples = [(float_counter, 1.0)] + meter.record_batch(label_values, record_tuples) + self.assertEqual(float_counter.get_handle(label_values), handle) + self.assertEqual(handle.data, 2.0) From ae94deaa5169930244f611491fa7d1a45e266fc1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Sun, 22 Sep 2019 11:52:46 -0700 Subject: [PATCH 51/88] Tests --- .../src/opentelemetry/sdk/metrics/__init__.py | 225 +++++++++++++++++- .../tests/metrics/test_metrics.py | 215 +++++++++++++++++ 2 files changed, 438 insertions(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 3e3b8ef4d4..66e37848da 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -59,6 +59,47 @@ def create_counter( label_keys=label_keys, disabled=disabled, non_monotonic=non_monotonic) + + + def create_gauge( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + label_keys: List[str] = None, + disabled: bool = False, + is_monotonic: bool = False, + ) -> Union[metrics_api.FloatGauge, metrics_api.IntGauge]: + """See `opentelemetry.metrics.Meter.create_gauge`.""" + gauge_class = FloatGauge if value_type == float else IntGauge + return gauge_class( + name, + description, + unit, + label_keys=label_keys, + disabled=disabled, + is_monotonic=is_monotonic) + + def create_measure( + self, + name: str, + description: str, + unit: str, + value_type: Union[Type[float], Type[int]], + label_keys: List[str] = None, + disabled: bool = False, + non_negative: bool = False, + ) -> Union[metrics_api.FloatMeasure, metrics_api.IntMeasure]: + """See `opentelemetry.metrics.Meter.create_measure`.""" + measure_class = FloatMeasure if value_type == float else IntMeasure + return measure_class( + name, + description, + unit, + label_keys=label_keys, + disabled=disabled, + non_negative=non_negative) class FloatCounter(metrics_api.FloatCounter): @@ -112,11 +153,131 @@ def __init__( self.handles = {} def get_handle(self, - label_values: List[str]) -> metrics_api.CounterHandle: + label_values: List[str]) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.IntCounter.get_handle`.""" handle = self.handles.get( label_values, - CounterHandle(int, self.disabled, self.non_monotonic)) + GaugeHandle(int, self.disabled, self.non_monotonic)) + self.handles[label_values] = handle + return handle + + +class FloatGauge(metrics_api.FloatGauge): + """See `opentelemetry.metrics.FloatGauge`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + label_keys: List[str] = None, + disabled: bool = False, + is_monotonic: bool = False, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.label_keys = label_keys + self.disabled = disabled + self.is_monotonic = is_monotonic + self.handles = {} + + def get_handle(self, + label_values: Tuple[str]) -> metrics_api.GaugeHandle: + """See `opentelemetry.metrics.FloatGauge.get_handle`.""" + handle = self.handles.get( + label_values, + GaugeHandle(float, self.disabled, self.is_monotonic)) + self.handles[label_values] = handle + return handle + + +class IntGauge(metrics_api.IntGauge): + """See `opentelemetry.metrics.IntGauge`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + label_keys: List[str] = None, + disabled: bool = False, + is_monotonic: bool = False, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.label_keys = label_keys + self.disabled = disabled + self.is_monotonic = is_monotonic + self.handles = {} + + def get_handle(self, + label_values: List[str]) -> metrics_api.GaugeHandle: + """See `opentelemetry.metrics.IntGauge.get_handle`.""" + handle = self.handles.get( + label_values, + GaugeHandle(int, self.disabled, self.is_monotonic)) + self.handles[label_values] = handle + return handle + + +class FloatMeasure(metrics_api.FloatMeasure): + """See `opentelemetry.metrics.FloatMeasure`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + label_keys: List[str] = None, + disabled: bool = False, + non_negative: bool = False, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.label_keys = label_keys + self.disabled = disabled + self.non_negative = non_negative + self.handles = {} + + def get_handle(self, + label_values: Tuple[str]) -> metrics_api.MeasureHandle: + """See `opentelemetry.metrics.FloatMeasure.get_handle`.""" + handle = self.handles.get( + label_values, + MeasureHandle(float, self.disabled, self.non_negative)) + self.handles[label_values] = handle + return handle + + +class IntMeasure(metrics_api.IntMeasure): + """See `opentelemetry.metrics.IntMeasure`.""" + + def __init__( + self, + name: str, + description: str, + unit: str, + label_keys: List[str] = None, + disabled: bool = False, + non_negative: bool = False, + ) -> None: + self.name = name + self.description = description + self.unit = unit + self.label_keys = label_keys + self.disabled = disabled + self.non_negative = non_negative + self.handles = {} + + def get_handle(self, + label_values: List[str]) -> metrics_api.MeasureHandle: + """See `opentelemetry.metrics.IntMeasure.get_handle`.""" + handle = self.handles.get( + label_values, + MeasureHandle(int, self.disabled, self.non_negative)) self.handles[label_values] = handle return handle @@ -151,4 +312,64 @@ def _add(self, value: Union[float, int]) -> None: self.data += value +class GaugeHandle(metrics_api.GaugeHandle): + + def __init__( + self, + value_type: Union[Type[float], Type[int]], + disabled: bool, + is_monotonic: bool) -> None: + self.data = 0 + self.value_type = value_type + self.disabled = disabled + self.is_monotonic = is_monotonic + + def update(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.GaugeHandle.update`.""" + self._set(value) + + def _set(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.GaugeHandle._set`.""" + if self.disabled: + logger.warning("Gauge metric is disabled.") + return + if self.is_monotonic and value < 0: + logger.warning("Monotonic gauge cannot descend.") + return + if not isinstance(value, self.value_type): + logger.warning("Invalid value passed for %s", self.value_type.__name__) + return + self.data = value + + +class MeasureHandle(metrics_api.MeasureHandle): + + def __init__( + self, + value_type: Union[Type[float], Type[int]], + disabled: bool, + non_negative: bool) -> None: + self.data = 0 + self.value_type = value_type + self.disabled = disabled + self.non_negative = non_negative + + def update(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.MeasureHandle.update`.""" + self._record(value) + + def _record(self, value: Union[float, int]) -> None: + """See `opentelemetry.metrics.MeasureHandle._record`.""" + if self.disabled: + logger.warning("Measure metric is disabled.") + return + if self.non_negative and value < 0: + logger.warning("Non-negative measure cannot descend.") + return + if not isinstance(value, self.value_type): + logger.warning("Invalid value passed for %s", self.value_type.__name__) + return + # TODO: record + + meter = Meter() \ No newline at end of file diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 3083e81455..99ef1bc240 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -54,3 +54,218 @@ def test_record_batch_exists(self): meter.record_batch(label_values, record_tuples) self.assertEqual(float_counter.get_handle(label_values), handle) self.assertEqual(handle.data, 2.0) + + def test_create_counter(self): + meter = metrics.Meter() + counter = meter.create_counter( + "name", + "desc", + "unit", + int, + [] + ) + self.assertTrue(isinstance(counter, metrics.IntCounter)) + self.assertEqual(counter.name, "name") + + def test_create_gauge(self): + meter = metrics.Meter() + gauge = meter.create_gauge( + "name", + "desc", + "unit", + float, + [] + ) + self.assertTrue(isinstance(gauge, metrics.FloatGauge)) + self.assertEqual(gauge.name, "name") + + def test_create_measure(self): + meter = metrics.Meter() + measure = meter.create_measure( + "name", + "desc", + "unit", + float, + [] + ) + self.assertTrue(isinstance(measure, metrics.FloatMeasure)) + self.assertEqual(measure.name, "name") + + +class TestMetric(unittest.TestCase): + def test_get_handle(self): + metric_types = [metrics.FloatCounter, + metrics.IntCounter, + metrics.FloatGauge, + metrics.IntGauge, + metrics.FloatMeasure, + metrics.IntMeasure] + for _type in metric_types: + metric = _type( + "name", + "desc", + "unit", + ["key"] + ) + label_values = ("value") + handle = metric.get_handle(label_values) + self.assertEqual(metric.handles.get(label_values), handle) + + +class TestCounterHandle(unittest.TestCase): + def test_update(self): + handle = metrics.CounterHandle( + float, + False, + False + ) + handle.update(2.0) + self.assertEqual(handle.data, 2.0) + + def test_add(self): + handle = metrics.CounterHandle( + int, + False, + False + ) + handle._add(3) + self.assertEqual(handle.data, 3) + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_add_disabled(self, logger_mock): + handle = metrics.CounterHandle( + int, + True, + False + ) + handle._add(3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_add_monotonic(self, logger_mock): + handle = metrics.CounterHandle( + int, + False, + False + ) + handle._add(-3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_add_incorrect_type(self, logger_mock): + handle = metrics.CounterHandle( + int, + False, + False + ) + handle._add(3.0) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + +class TestGaugeHandle(unittest.TestCase): + def test_update(self): + handle = metrics.GaugeHandle( + float, + False, + False + ) + handle.update(2.0) + self.assertEqual(handle.data, 2.0) + + def test_set(self): + handle = metrics.GaugeHandle( + int, + False, + False + ) + handle._set(3) + self.assertEqual(handle.data, 3) + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_set_disabled(self, logger_mock): + handle = metrics.GaugeHandle( + int, + True, + False + ) + handle._set(3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_set_monotonic(self, logger_mock): + handle = metrics.GaugeHandle( + int, + False, + True + ) + handle._set(-3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_set_incorrect_type(self, logger_mock): + handle = metrics.GaugeHandle( + int, + False, + False + ) + handle._set(3.0) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + +class TestMeasureHandle(unittest.TestCase): + def test_update(self): + handle = metrics.MeasureHandle( + float, + False, + False + ) + handle.update(2.0) + self.assertEqual(handle.data, 0) + + def test_record(self): + handle = metrics.MeasureHandle( + int, + False, + False + ) + handle._record(3) + self.assertEqual(handle.data, 0) + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_record_disabled(self, logger_mock): + handle = metrics.MeasureHandle( + int, + True, + False + ) + handle._record(3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_record_monotonic(self, logger_mock): + handle = metrics.MeasureHandle( + int, + False, + True + ) + handle._record(-3) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() + + @mock.patch('opentelemetry.sdk.metrics.logger') + def test_record_incorrect_type(self, logger_mock): + handle = metrics.MeasureHandle( + int, + False, + False + ) + handle._record(3.0) + self.assertEqual(handle.data, 0) + logger_mock.warning.assert_called() From a16556e1d97595aee23562e17979f17d3c786c73 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 13:28:48 -0700 Subject: [PATCH 52/88] Address comments --- .../src/opentelemetry/metrics/__init__.py | 105 +++++----- .../src/opentelemetry/sdk/common/__init__.py | 128 ------------- .../src/opentelemetry/sdk/metrics/__init__.py | 179 +++++++++--------- .../src/opentelemetry/sdk/trace/__init__.py | 2 +- .../src/opentelemetry/sdk/util.py | 115 +++++++++++ .../tests/metrics/test_metrics.py | 50 +++-- 6 files changed, 276 insertions(+), 303 deletions(-) delete mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 0ac7ba18ff..68c5bef701 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,7 +27,7 @@ """ from abc import ABC, abstractmethod -from typing import Callable, List, Optional, Tuple, Type, Union +from typing import Callable, Optional, Tuple, Type, Union from opentelemetry.util import loader @@ -43,8 +43,8 @@ class Meter: def record_batch( self, - label_values: List[str], - record_tuples: List[Tuple["Metric", Union[float, int]]], + label_values: Tuple[str], + record_tuples: Tuple[Tuple["Metric", Union[float, int]]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -53,10 +53,9 @@ def record_batch( match the key-value pairs in the label tuples. Args: - label_values: A list of label values that will be matched - against to record for the handles under each metric that has - those labels. - record_tuples: A list of pairs of `Metric` s and the + label_values: The values that will be matched against to record for + the handles under each metric that has those labels. + record_tuples: A tuple of pairs of `Metric` s and the corresponding value to record for that metric. """ @@ -67,29 +66,29 @@ def create_counter( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - non_monotonic: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = True, ) -> Union["FloatCounter", "IntCounter"]: """Creates a counter metric with type value_type. - By default, counter values can only go up (monotonic). The API - should reject negative inputs to monotonic counter metrics. - Counter metrics that have a non_monotonic option set to True allows - negative inputs. + Counter metric expresses the computation of a sum. By default, counter + values can only go up (monotonic). Negative inputs will be discarded + for monotonic counter metrics. Counter metrics that have a monotonic + option set to False allows negative inputs. Args: name: The name of the counter. - description: Human readable description of the metric. + description: Human-readable description of the metric. unit: Unit of the metric values. value_type: The type of values being recorded by the metric. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order must be used + label_keys: The keys for the labels with dynamic values. + Order of the tuple is important as the same order must be used on recording when suppling values for these labels. - disabled: True value tells the SDK not to report by default. - non_monotonic: Set to true to allow negative inputs. + enabled: Whether to report the metric by default. + monotonic: Whether to only allow non-negative values. - Returns: A new counter metric for values of the given value_type. + Returns: A new counter metric for values of the given ``value_type``. """ @@ -99,28 +98,32 @@ def create_gauge( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - is_monotonic: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False, ) -> Union["FloatGauge", "IntGauge"]: """Creates a gauge metric with type value_type. - By default, gauge values can go both up and down (non-monotic). The API - allows for an optional is_monotonic flag, in which when set to True will - reject descending update values. + Gauge metrics express a pre-calculated value that is either `Set()` + by explicit instrumentation or observed through a callback. This kind + of metric should be used when the metric cannot be expressed as a sum + or because the measurement interval is arbitrary. + + By default, gauge values can go both up and down (non-monotonic). + Negative inputs will be discarded for monotonic gauge metrics. Args: name: The name of the gauge. - description: Human readable description of the metric. + description: Human-readable description of the metric. unit: Unit of the metric values. value_type: The type of values being recorded by the metric. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order must be used + label_keys: The keys for the labels with dynamic values. + Order of the tuple is important as the same order must be used on recording when suppling values for these labels. - disabled: True value tells the SDK not to report by default. - is_monotonic: Set to true to reject negative inputs. + enabled: Whether to report the metric by default. + monotonic: Whether to only allow non-negative values. - Returns: A new gauge metric for values of the given value_type. + Returns: A new gauge metric for values of the given ``value_type``. """ @@ -130,28 +133,28 @@ def create_measure( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - non_negative: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False, ) -> 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. + Measure metrics represent raw statistics that are recorded. By + default, measure metrics can accept both positive and negatives. + Negative inputs will be discarded when monotonic is True. Args: name: The name of the measure. - description: Human readable description of the metric. + description: Human-readable description of the metric. unit: Unit of the metric values. value_type: The type of values being recorded by the metric. - label_keys: list of keys for the labels with dynamic values. - Order of the list is important as the same order must be used + label_keys: The keys for the labels with dynamic values. + Order of the tuple is important as the same order must be used on recording when suppling values for these labels. - disabled: True value tells the SDK not to report by default. - non_negative: Set to true to reject negative inputs. + enabled: Whether to report the metric by default. + monotonic: Whether to only allow non-negative values. - Returns: A new measure metric for values of the given value_type. + Returns: A new measure metric for values of the given ``value_type``. """ # Once https://github.com/python/mypy/issues/7092 is resolved, @@ -207,7 +210,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: Tuple[str]) -> "object": + def get_handle(self, label_values: Tuple[str]) -> "BaseHandle": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -218,8 +221,7 @@ def get_handle(self, label_values: Tuple[str]) -> "object": a value was not provided are permitted. Args: - label_values: A tuple of label values that will be associated - with the return handle. + label_values: Values to associate with the returned handle. """ def remove_handle(self, label_values: Tuple[str]) -> None: @@ -228,7 +230,7 @@ def remove_handle(self, label_values: Tuple[str]) -> None: The handle with matching label values will be removed. args: - label_values: The tuple of label values to match against. + label_values: The label values to match against. """ def clear(self) -> None: @@ -277,7 +279,7 @@ def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with an int value.""" -class BaseHandle(): +class BaseHandle: """An interface for metric handles.""" @abstractmethod @@ -298,10 +300,7 @@ def update(self, value: Union[float, int]) -> None: """ def _add(self, value: Union[float, int]) -> None: - """Adds the given value to the current value. - - The input value cannot be negative if not bidirectional. - """ + """Adds the given value to the current value.""" class GaugeHandle(BaseHandle): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py deleted file mode 100644 index 1927221d16..0000000000 --- a/opentelemetry-sdk/src/opentelemetry/sdk/common/__init__.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2019, OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import threading - -from collections import OrderedDict, deque - -try: - # pylint: disable=ungrouped-imports - from collections.abc import MutableMapping - from collections.abc import Sequence -except ImportError: - # pylint: disable=no-name-in-module,ungrouped-imports - from collections import MutableMapping - from collections import Sequence - - -class BoundedList(Sequence): - """An append only list with a fixed max size.""" - - def __init__(self, maxlen): - self.dropped = 0 - self._dq = deque(maxlen=maxlen) - self._lock = threading.Lock() - - def __repr__(self): - return "{}({}, maxlen={})".format( - type(self).__name__, list(self._dq), self._dq.maxlen - ) - - def __getitem__(self, index): - return self._dq[index] - - def __len__(self): - return len(self._dq) - - def __iter__(self): - with self._lock: - return iter(self._dq.copy()) - - def append(self, item): - with self._lock: - if len(self._dq) == self._dq.maxlen: - self.dropped += 1 - self._dq.append(item) - - def extend(self, seq): - with self._lock: - to_drop = len(seq) + len(self._dq) - self._dq.maxlen - if to_drop > 0: - self.dropped += to_drop - self._dq.extend(seq) - - @classmethod - def from_seq(cls, maxlen, seq): - seq = tuple(seq) - if len(seq) > maxlen: - raise ValueError - bounded_list = cls(maxlen) - # pylint: disable=protected-access - bounded_list._dq = deque(seq, maxlen=maxlen) - return bounded_list - - -class BoundedDict(MutableMapping): - """A dict with a fixed max capacity.""" - - def __init__(self, maxlen): - if not isinstance(maxlen, int): - raise ValueError - if maxlen < 0: - raise ValueError - self.maxlen = maxlen - self.dropped = 0 - self._dict = OrderedDict() - self._lock = threading.Lock() - - def __repr__(self): - return "{}({}, maxlen={})".format( - type(self).__name__, dict(self._dict), self.maxlen - ) - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - with self._lock: - if self.maxlen == 0: - self.dropped += 1 - return - - if key in self._dict: - del self._dict[key] - elif len(self._dict) == self.maxlen: - del self._dict[next(iter(self._dict.keys()))] - self.dropped += 1 - self._dict[key] = value - - def __delitem__(self, key): - del self._dict[key] - - def __iter__(self): - with self._lock: - return iter(self._dict.copy()) - - def __len__(self): - return len(self._dict) - - @classmethod - def from_map(cls, maxlen, mapping): - mapping = OrderedDict(mapping) - if len(mapping) > maxlen: - raise ValueError - bounded_dict = cls(maxlen) - # pylint: disable=protected-access - bounded_dict._dict = mapping - return bounded_dict diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 66e37848da..a070584392 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -14,30 +14,23 @@ import logging -from typing import List, Tuple, Type, Union +from typing import Tuple, Type, Union from opentelemetry import metrics as metrics_api logger = logging.getLogger(__name__) class Meter(metrics_api.Meter): - """See `opentelemetry.metrics.Meter`. - - Args: - name: The name of the meter. - """ - def __init__(self, name: str = "") -> None: - self.name = name + """See `opentelemetry.metrics.Meter`.""" def record_batch( self, label_values: Tuple[str], - record_tuples: List[Tuple[metrics_api.Metric, Union[float, int]]], + record_tuples: Tuple[Tuple[metrics_api.Metric, Union[float, int]]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" - for pair in record_tuples: - handle = pair[0].get_handle(label_values) - handle.update(pair[1]) + for metric, value in record_tuples: + handle = metric.get_handle(label_values).update(value) def create_counter( @@ -46,9 +39,9 @@ def create_counter( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - non_monotonic: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = True, ) -> Union[metrics_api.FloatCounter, metrics_api.IntCounter]: """See `opentelemetry.metrics.Meter.create_counter`.""" counter_class = FloatCounter if value_type == float else IntCounter @@ -57,8 +50,8 @@ def create_counter( description, unit, label_keys=label_keys, - disabled=disabled, - non_monotonic=non_monotonic) + enabled=enabled, + monotonic=monotonic) def create_gauge( @@ -67,9 +60,9 @@ def create_gauge( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - is_monotonic: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False, ) -> Union[metrics_api.FloatGauge, metrics_api.IntGauge]: """See `opentelemetry.metrics.Meter.create_gauge`.""" gauge_class = FloatGauge if value_type == float else IntGauge @@ -78,8 +71,8 @@ def create_gauge( description, unit, label_keys=label_keys, - disabled=disabled, - is_monotonic=is_monotonic) + enabled=enabled, + monotonic=monotonic) def create_measure( self, @@ -87,9 +80,9 @@ def create_measure( description: str, unit: str, value_type: Union[Type[float], Type[int]], - label_keys: List[str] = None, - disabled: bool = False, - non_negative: bool = False, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False, ) -> Union[metrics_api.FloatMeasure, metrics_api.IntMeasure]: """See `opentelemetry.metrics.Meter.create_measure`.""" measure_class = FloatMeasure if value_type == float else IntMeasure @@ -98,8 +91,8 @@ def create_measure( description, unit, label_keys=label_keys, - disabled=disabled, - non_negative=non_negative) + enabled=enabled, + monotonic=monotonic) class FloatCounter(metrics_api.FloatCounter): @@ -110,16 +103,16 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - non_monotonic: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.non_monotonic = non_monotonic + self.enabled = enabled + self.monotonic = monotonic self.handles = {} def get_handle(self, @@ -127,7 +120,7 @@ def get_handle(self, """See `opentelemetry.metrics.FloatCounter.get_handle`.""" handle = self.handles.get( label_values, - CounterHandle(float, self.disabled, self.non_monotonic)) + CounterHandle(float, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -140,24 +133,24 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - non_monotonic: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.non_monotonic = non_monotonic + self.enabled = enabled + self.monotonic = monotonic self.handles = {} def get_handle(self, - label_values: List[str]) -> metrics_api.GaugeHandle: + label_values: Tuple[str]) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.IntCounter.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(int, self.disabled, self.non_monotonic)) + GaugeHandle(int, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -170,16 +163,16 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - is_monotonic: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.is_monotonic = is_monotonic + self.enabled = enabled + self.monotonic = monotonic self.handles = {} def get_handle(self, @@ -187,7 +180,7 @@ def get_handle(self, """See `opentelemetry.metrics.FloatGauge.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(float, self.disabled, self.is_monotonic)) + GaugeHandle(float, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -200,24 +193,24 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - is_monotonic: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = False, + monotonic: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.is_monotonic = is_monotonic + self.enabled = enabled + self.monotonic = monotonic self.handles = {} def get_handle(self, - label_values: List[str]) -> metrics_api.GaugeHandle: + label_values: Tuple[str]) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.IntGauge.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(int, self.disabled, self.is_monotonic)) + GaugeHandle(int, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -230,16 +223,16 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - non_negative: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = False, + monotonic: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.non_negative = non_negative + self.enabled = enabled + self.monotonic = monotonic self.handles = {} def get_handle(self, @@ -247,7 +240,7 @@ def get_handle(self, """See `opentelemetry.metrics.FloatMeasure.get_handle`.""" handle = self.handles.get( label_values, - MeasureHandle(float, self.disabled, self.non_negative)) + MeasureHandle(float, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -260,24 +253,24 @@ def __init__( name: str, description: str, unit: str, - label_keys: List[str] = None, - disabled: bool = False, - non_negative: bool = False, - ) -> None: + label_keys: Tuple[str] = None, + enabled: bool = False, + allow_negative: bool = False + ): self.name = name self.description = description self.unit = unit self.label_keys = label_keys - self.disabled = disabled - self.non_negative = non_negative + self.enabled = enabled + self.allow_negative = allow_negative self.handles = {} def get_handle(self, - label_values: List[str]) -> metrics_api.MeasureHandle: + label_values: Tuple[str]) -> metrics_api.MeasureHandle: """See `opentelemetry.metrics.IntMeasure.get_handle`.""" handle = self.handles.get( label_values, - MeasureHandle(int, self.disabled, self.non_negative)) + MeasureHandle(int, self.enabled, self.allow_negative)) self.handles[label_values] = handle return handle @@ -287,12 +280,13 @@ class CounterHandle(metrics_api.CounterHandle): def __init__( self, value_type: Union[Type[float], Type[int]], - disabled: bool, - non_monotonic: bool) -> None: + enabled: bool, + monotonic: bool + ): self.data = 0 self.value_type = value_type - self.disabled = disabled - self.non_monotonic = non_monotonic + self.enabled = enabled + self.monotonic = monotonic def update(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.CounterHandle.update`.""" @@ -300,10 +294,9 @@ def update(self, value: Union[float, int]) -> None: def _add(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" - if self.disabled: - logger.warning("Counter metric is disabled.") + if not self.enabled: return - if not self.non_monotonic and value < 0: + if not self.monotonic and value < 0: logger.warning("Monotonic counter cannot descend.") return if not isinstance(value, self.value_type): @@ -317,12 +310,13 @@ class GaugeHandle(metrics_api.GaugeHandle): def __init__( self, value_type: Union[Type[float], Type[int]], - disabled: bool, - is_monotonic: bool) -> None: + enabled: bool, + monotonic: bool + ): self.data = 0 self.value_type = value_type - self.disabled = disabled - self.is_monotonic = is_monotonic + self.enabled = enabled + self.monotonic = monotonic def update(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.GaugeHandle.update`.""" @@ -330,10 +324,9 @@ def update(self, value: Union[float, int]) -> None: def _set(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" - if self.disabled: - logger.warning("Gauge metric is disabled.") + if not self.enabled: return - if self.is_monotonic and value < 0: + if self.monotonic and value < 0: logger.warning("Monotonic gauge cannot descend.") return if not isinstance(value, self.value_type): @@ -347,12 +340,13 @@ class MeasureHandle(metrics_api.MeasureHandle): def __init__( self, value_type: Union[Type[float], Type[int]], - disabled: bool, - non_negative: bool) -> None: + enabled: bool, + monotonic: bool + ): self.data = 0 self.value_type = value_type - self.disabled = disabled - self.non_negative = non_negative + self.enabled = enabled + self.monotonic = monotonic def update(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.MeasureHandle.update`.""" @@ -360,10 +354,9 @@ def update(self, value: Union[float, int]) -> None: def _record(self, value: Union[float, int]) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" - if self.disabled: - logger.warning("Measure metric is disabled.") + if not self.enabled: return - if self.non_negative and value < 0: + if self.monotonic and value < 0: logger.warning("Non-negative measure cannot descend.") return if not isinstance(value, self.value_type): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 1ca99544d4..3950834301 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -22,7 +22,7 @@ from opentelemetry import trace as trace_api from opentelemetry.context import Context from opentelemetry.sdk import util -from opentelemetry.sdk.common import BoundedDict, BoundedList +from opentelemetry.sdk.util import BoundedDict, BoundedList from opentelemetry.util import types logger = logging.getLogger(__name__) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index d855e85179..d8191ab68e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -13,8 +13,20 @@ # limitations under the License. import datetime +import threading import time +from collections import OrderedDict, deque + +try: + # pylint: disable=ungrouped-imports + from collections.abc import MutableMapping + from collections.abc import Sequence +except ImportError: + # pylint: disable=no-name-in-module,ungrouped-imports + from collections import MutableMapping + from collections import Sequence + try: time_ns = time.time_ns # Python versions < 3.7 @@ -28,3 +40,106 @@ def ns_to_iso_str(nanoseconds): """Get an ISO 8601 string from time_ns value.""" ts = datetime.datetime.fromtimestamp(nanoseconds / 1e9) return ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + +class BoundedList(Sequence): + """An append only list with a fixed max size.""" + + def __init__(self, maxlen): + self.dropped = 0 + self._dq = deque(maxlen=maxlen) + self._lock = threading.Lock() + + def __repr__(self): + return "{}({}, maxlen={})".format( + type(self).__name__, list(self._dq), self._dq.maxlen + ) + + def __getitem__(self, index): + return self._dq[index] + + def __len__(self): + return len(self._dq) + + def __iter__(self): + with self._lock: + return iter(self._dq.copy()) + + def append(self, item): + with self._lock: + if len(self._dq) == self._dq.maxlen: + self.dropped += 1 + self._dq.append(item) + + def extend(self, seq): + with self._lock: + to_drop = len(seq) + len(self._dq) - self._dq.maxlen + if to_drop > 0: + self.dropped += to_drop + self._dq.extend(seq) + + @classmethod + def from_seq(cls, maxlen, seq): + seq = tuple(seq) + if len(seq) > maxlen: + raise ValueError + bounded_list = cls(maxlen) + # pylint: disable=protected-access + bounded_list._dq = deque(seq, maxlen=maxlen) + return bounded_list + + +class BoundedDict(MutableMapping): + """A dict with a fixed max capacity.""" + + def __init__(self, maxlen): + if not isinstance(maxlen, int): + raise ValueError + if maxlen < 0: + raise ValueError + self.maxlen = maxlen + self.dropped = 0 + self._dict = OrderedDict() + self._lock = threading.Lock() + + def __repr__(self): + return "{}({}, maxlen={})".format( + type(self).__name__, dict(self._dict), self.maxlen + ) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + with self._lock: + if self.maxlen == 0: + self.dropped += 1 + return + + if key in self._dict: + del self._dict[key] + elif len(self._dict) == self.maxlen: + del self._dict[next(iter(self._dict.keys()))] + self.dropped += 1 + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + + def __iter__(self): + with self._lock: + return iter(self._dict.copy()) + + def __len__(self): + return len(self._dict) + + @classmethod + def from_map(cls, maxlen, mapping): + mapping = OrderedDict(mapping) + if len(mapping) > maxlen: + raise ValueError + bounded_dict = cls(maxlen) + # pylint: disable=protected-access + bounded_dict._dict = mapping + return bounded_dict + diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 99ef1bc240..404271b111 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -26,7 +26,7 @@ def test_extends_api(self): def test_record_batch(self): meter = metrics.Meter() - label_keys = ["key1"] + label_keys = ("key1") label_values = ("value1") float_counter = metrics.FloatCounter( "name", @@ -40,7 +40,7 @@ def test_record_batch(self): def test_record_batch_exists(self): meter = metrics.Meter() - label_keys = ["key1"] + label_keys = ("key1") label_values = ("value1") float_counter = metrics.FloatCounter( "name", @@ -62,7 +62,7 @@ def test_create_counter(self): "desc", "unit", int, - [] + () ) self.assertTrue(isinstance(counter, metrics.IntCounter)) self.assertEqual(counter.name, "name") @@ -74,7 +74,7 @@ def test_create_gauge(self): "desc", "unit", float, - [] + () ) self.assertTrue(isinstance(gauge, metrics.FloatGauge)) self.assertEqual(gauge.name, "name") @@ -86,7 +86,7 @@ def test_create_measure(self): "desc", "unit", float, - [] + () ) self.assertTrue(isinstance(measure, metrics.FloatMeasure)) self.assertEqual(measure.name, "name") @@ -105,7 +105,7 @@ def test_get_handle(self): "name", "desc", "unit", - ["key"] + ("key") ) label_values = ("value") handle = metric.get_handle(label_values) @@ -116,7 +116,7 @@ class TestCounterHandle(unittest.TestCase): def test_update(self): handle = metrics.CounterHandle( float, - False, + True, False ) handle.update(2.0) @@ -125,28 +125,26 @@ def test_update(self): def test_add(self): handle = metrics.CounterHandle( int, - False, + True, False ) handle._add(3) self.assertEqual(handle.data, 3) - @mock.patch('opentelemetry.sdk.metrics.logger') - def test_add_disabled(self, logger_mock): + def test_add_disabled(self): handle = metrics.CounterHandle( int, - True, + False, False ) handle._add(3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() @mock.patch('opentelemetry.sdk.metrics.logger') def test_add_monotonic(self, logger_mock): handle = metrics.CounterHandle( int, - False, + True, False ) handle._add(-3) @@ -157,7 +155,7 @@ def test_add_monotonic(self, logger_mock): def test_add_incorrect_type(self, logger_mock): handle = metrics.CounterHandle( int, - False, + True, False ) handle._add(3.0) @@ -169,7 +167,7 @@ class TestGaugeHandle(unittest.TestCase): def test_update(self): handle = metrics.GaugeHandle( float, - False, + True, False ) handle.update(2.0) @@ -178,28 +176,26 @@ def test_update(self): def test_set(self): handle = metrics.GaugeHandle( int, - False, + True, False ) handle._set(3) self.assertEqual(handle.data, 3) - @mock.patch('opentelemetry.sdk.metrics.logger') - def test_set_disabled(self, logger_mock): + def test_set_disabled(self): handle = metrics.GaugeHandle( int, - True, + False, False ) handle._set(3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() @mock.patch('opentelemetry.sdk.metrics.logger') def test_set_monotonic(self, logger_mock): handle = metrics.GaugeHandle( int, - False, + True, True ) handle._set(-3) @@ -210,7 +206,7 @@ def test_set_monotonic(self, logger_mock): def test_set_incorrect_type(self, logger_mock): handle = metrics.GaugeHandle( int, - False, + True, False ) handle._set(3.0) @@ -237,22 +233,20 @@ def test_record(self): handle._record(3) self.assertEqual(handle.data, 0) - @mock.patch('opentelemetry.sdk.metrics.logger') - def test_record_disabled(self, logger_mock): + def test_record_disabled(self): handle = metrics.MeasureHandle( int, - True, + False, False ) handle._record(3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() @mock.patch('opentelemetry.sdk.metrics.logger') def test_record_monotonic(self, logger_mock): handle = metrics.MeasureHandle( int, - False, + True, True ) handle._record(-3) @@ -263,7 +257,7 @@ def test_record_monotonic(self, logger_mock): def test_record_incorrect_type(self, logger_mock): handle = metrics.MeasureHandle( int, - False, + True, False ) handle._record(3.0) From 3b0d25106c9a4ae62cb9ba69df4abed236a2e338 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 16:36:45 -0700 Subject: [PATCH 53/88] ADd tests --- .../src/opentelemetry/metrics/__init__.py | 191 ++++----------- opentelemetry-api/tests/metrics/__init__.py | 13 + .../tests/metrics/test_metrics.py | 63 +++++ .../src/opentelemetry/sdk/metrics/__init__.py | 230 ++++++------------ .../tests/metrics/test_metrics.py | 141 ++++++++--- 5 files changed, 297 insertions(+), 341 deletions(-) create mode 100644 opentelemetry-api/tests/metrics/__init__.py create mode 100644 opentelemetry-api/tests/metrics/test_metrics.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 68c5bef701..a77f20ce90 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -26,11 +26,16 @@ """ +import enum + from abc import ABC, abstractmethod -from typing import Callable, Optional, Tuple, Type, Union +from typing import Callable, Generic, Optional, Tuple, Type, TypeVar, Union from opentelemetry.util import loader +MetricKind = Union[Type["Counter"], Type["Gauge"], Type["Measure"]] +ValueType = TypeVar('ValueType', int, float) + # pylint: disable=unused-argument class Meter: @@ -44,7 +49,7 @@ class Meter: def record_batch( self, label_values: Tuple[str], - record_tuples: Tuple[Tuple["Metric", Union[float, int]]], + record_tuples: Tuple[Tuple["Metric", "ValueType"]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -60,91 +65,21 @@ def record_batch( """ - def create_counter( - self, - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = True, - ) -> Union["FloatCounter", "IntCounter"]: - """Creates a counter metric with type value_type. - - Counter metric expresses the computation of a sum. By default, counter - values can only go up (monotonic). Negative inputs will be discarded - for monotonic counter metrics. Counter metrics that have a monotonic - option set to False allows 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. - label_keys: The keys for the labels with dynamic values. - Order of the tuple is important as the same order must be used - on recording when suppling values for these labels. - enabled: Whether to report the metric by default. - monotonic: Whether to only allow non-negative values. - - Returns: A new counter metric for values of the given ``value_type``. - """ - - - def create_gauge( + def create_metric( self, name: str, description: str, unit: str, - value_type: Union[Type[float], Type[int]], + value_type: "ValueType", + metric_kind: "MetricKind", label_keys: Tuple[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> Union["FloatGauge", "IntGauge"]: - """Creates a gauge metric with type value_type. - - Gauge metrics express a pre-calculated value that is either `Set()` - by explicit instrumentation or observed through a callback. This kind - of metric should be used when the metric cannot be expressed as a sum - or because the measurement interval is arbitrary. - - By default, gauge values can go both up and down (non-monotonic). - Negative inputs will be discarded for monotonic gauge metrics. + ) -> "MetricKind": + """Creates a `metric_kind` metric with type `value_type`. 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. - label_keys: The keys for the labels with dynamic values. - Order of the tuple is important as the same order must be used - on recording when suppling values for these labels. - enabled: Whether to report the metric by default. - monotonic: Whether to only allow non-negative values. - - Returns: A new gauge metric for values of the given ``value_type``. - """ - - - def create_measure( - self, - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = False, - ) -> Union["FloatMeasure", "IntMeasure"]: - """Creates a measure metric with type value_type. - - Measure metrics represent raw statistics that are recorded. By - default, measure metrics can accept both positive and negatives. - Negative inputs will be discarded when monotonic is True. - - Args: - name: The name of the measure. + 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. @@ -154,7 +89,7 @@ def create_measure( enabled: Whether to report the metric by default. monotonic: Whether to only allow non-negative values. - Returns: A new measure metric for values of the given ``value_type``. + Returns: A new ``metric_kind`` metric with values of ``value_type``. """ # Once https://github.com/python/mypy/issues/7092 is resolved, @@ -206,11 +141,11 @@ 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. + handle that the metric holds. """ @abstractmethod - def get_handle(self, label_values: Tuple[str]) -> "BaseHandle": + def get_handle(self, label_values: Tuple[str]) -> "MetricHandle": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -237,88 +172,52 @@ def clear(self) -> None: """Removes all handles from the `Metric`.""" -class FloatCounter(Metric): - """A counter type metric that holds float values.""" +class Counter(Metric): + """A counter type metric that expresses the computation of a sum.""" def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": - """Gets a `CounterHandle` with a float value.""" - - -class IntCounter(Metric): - """A counter type metric that holds int values.""" - - def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": - """Gets a `CounterHandle` with an int value.""" - + """Gets a `CounterHandle`.""" -class FloatGauge(Metric): - """A gauge type metric that holds float values.""" - def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": - """Gets a `GaugeHandle` with a float value.""" - - -class IntGauge(Metric): - """A gauge type metric that holds int values.""" +class Gauge(Metric): + """A gauge type metric that expresses a pre-calculated value. + + Gauge metrics have a value that is either `Set()` by explicit + instrumentation or observed through a callback. This kind of metric + should be used when the metric cannot be expressed as a sum or because + the measurement interval is arbitrary. + """ def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": - """Gets a `GaugeHandle` with an int value.""" + """Gets a `GaugeHandle`.""" -class FloatMeasure(Metric): - """A measure type metric that holds float values.""" +class Measure(Metric): + """A measure type metric that represent raw stats that are recorded. + + Measure metrics represent raw statistics that are recorded. By + default, measure metrics can accept both positive and negatives. + Negative inputs will be discarded when monotonic is True. + """ def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" -class IntMeasure(Metric): - """A measure type metric that holds int values.""" - - def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": - """Gets a `MeasureHandle` with an int value.""" - - -class BaseHandle: +class MetricHandle: """An interface for metric handles.""" - @abstractmethod - def update(self, value: Union[float, int]) -> None: - """A generic update method to alter the value of the handle. - - Useful for record_batch, where the type of the handle does not - matter. Implementation should call the appropriate method that - alters the underlying data for that handle type. - """ - - -class CounterHandle(BaseHandle): - def update(self, value: Union[float, int]) -> None: - """Alters the value of the counter handle. - - Implementations should call _add(). - """ - - def _add(self, value: Union[float, int]) -> None: - """Adds the given value to the current value.""" +class CounterHandle(MetricHandle): + def add(self, value: "ValueType") -> None: + """Increases the value of the handle by `value`""" -class GaugeHandle(BaseHandle): - def update(self, value: Union[float, int]) -> None: - """Alters the value of the gauge handle. - - Implementations should call _set(). - """ - def _set(self, value: Union[float, int]) -> None: - """Sets the current value to the given value. Can be negative.""" +class GaugeHandle(MetricHandle): + def set(self, value: "ValueType") -> None: + """Sets the current value of the handle to `value`.""" -class MeasureHandle(BaseHandle): - def update(self, value: Union[float, int]) -> None: - """Alters the value of the measure handle. - - Implementations should call _record(). - """ - def _record(self, value: Union[float, int]) -> None: - """Records the given value to this measure.""" +class MeasureHandle(MetricHandle): + def record(self, value: "ValueType") -> None: + """Records the given `value` to this handle.""" diff --git a/opentelemetry-api/tests/metrics/__init__.py b/opentelemetry-api/tests/metrics/__init__.py new file mode 100644 index 0000000000..d853a7bcf6 --- /dev/null +++ b/opentelemetry-api/tests/metrics/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py new file mode 100644 index 0000000000..f71dab9882 --- /dev/null +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -0,0 +1,63 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from opentelemetry import metrics + + +class TestMeter(unittest.TestCase): + def setUp(self): + self.meter = metrics.Meter() + + def test_record_batch(self): + self.meter.record_batch((), ()) + + def test_create_metric(self): + self.meter.create_metric("", "", "", float, metrics.Counter) + + +class TestMetrics(unittest.TestCase): + + def test_counter(self): + counter = metrics.Counter() + counter.get_handle(()) + + def test_gauge(self): + gauge = metrics.Gauge() + gauge.get_handle(()) + + def test_measure(self): + measure = metrics.Measure() + measure.get_handle(()) + + def test_remove_handle(self): + counter = metrics.Counter() + counter.remove_handle(()) + + def test_clear(self): + counter = metrics.Counter() + counter.clear() + + def test_counter_handle(self): + handle = metrics.CounterHandle() + handle.add(1) + + def test_gauge_handle(self): + handle = metrics.GaugeHandle() + handle.set(1) + + def test_measure_handle(self): + handle = metrics.MeasureHandle() + handle.record(1) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index a070584392..847d11f68f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -26,180 +26,123 @@ class Meter(metrics_api.Meter): def record_batch( self, label_values: Tuple[str], - record_tuples: Tuple[Tuple[metrics_api.Metric, Union[float, int]]], + record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueType]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" for metric, value in record_tuples: - handle = metric.get_handle(label_values).update(value) + handle = metric.get_handle(label_values)._update(value) - - def create_counter( - self, - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = True, - ) -> Union[metrics_api.FloatCounter, metrics_api.IntCounter]: - """See `opentelemetry.metrics.Meter.create_counter`.""" - counter_class = FloatCounter if value_type == float else IntCounter - return counter_class( - name, - description, - unit, - label_keys=label_keys, - enabled=enabled, - monotonic=monotonic) - - - def create_gauge( - self, - name: str, - description: str, - unit: str, - value_type: Union[Type[float], Type[int]], - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = False, - ) -> Union[metrics_api.FloatGauge, metrics_api.IntGauge]: - """See `opentelemetry.metrics.Meter.create_gauge`.""" - gauge_class = FloatGauge if value_type == float else IntGauge - return gauge_class( - name, - description, - unit, - label_keys=label_keys, - enabled=enabled, - monotonic=monotonic) - - def create_measure( + def create_metric( self, name: str, description: str, unit: str, - value_type: Union[Type[float], Type[int]], + value_type: metrics_api.ValueType, + metric_kind: metrics_api.MetricKind, label_keys: Tuple[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> Union[metrics_api.FloatMeasure, metrics_api.IntMeasure]: - """See `opentelemetry.metrics.Meter.create_measure`.""" - measure_class = FloatMeasure if value_type == float else IntMeasure - return measure_class( + ) -> "Metric": + """See `opentelemetry.metrics.Meter.create_metric`.""" + return metric_kind( name, description, unit, + value_type, label_keys=label_keys, enabled=enabled, monotonic=monotonic) -class FloatCounter(metrics_api.FloatCounter): - """See `opentelemetry.metrics.FloatCounter`.""" +class Metric(metrics_api.Metric): def __init__( self, name: str, description: str, unit: str, + value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = True, monotonic: bool = False ): self.name = name self.description = description - self.unit = unit + self.unit = unit, + self.value_type = value_type self.label_keys = label_keys self.enabled = enabled self.monotonic = monotonic self.handles = {} - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.CounterHandle: - """See `opentelemetry.metrics.FloatCounter.get_handle`.""" - handle = self.handles.get( - label_values, - CounterHandle(float, self.enabled, self.monotonic)) - self.handles[label_values] = handle - return handle - - -class IntCounter(metrics_api.IntCounter): - """See `opentelemetry.metrics.IntCounter`.""" - - def __init__( - self, - name: str, - description: str, - unit: str, - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = False - ): - self.name = name - self.description = description - self.unit = unit - self.label_keys = label_keys - self.enabled = enabled - self.monotonic = monotonic - self.handles = {} + def remove_handle(self, label_values: Tuple[str]) -> None: + """See `opentelemetry.metrics.Metric.remove_handle`.""" + self.handles.pop(label_values, None) - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.GaugeHandle: - """See `opentelemetry.metrics.IntCounter.get_handle`.""" - handle = self.handles.get( - label_values, - GaugeHandle(int, self.enabled, self.monotonic)) - self.handles[label_values] = handle - return handle + def clear(self) -> None: + """See `opentelemetry.metrics.Metric.clear`.""" + self.handles.clear() -class FloatGauge(metrics_api.FloatGauge): - """See `opentelemetry.metrics.FloatGauge`.""" +class Counter(Metric): + """See `opentelemetry.metrics.Counter`. + + By default, counter values can only go up (monotonic). Negative inputs + will be discarded for monotonic counter metrics. Counter metrics that + have a monotonic option set to False allows negative inputs. + """ def __init__( self, name: str, description: str, unit: str, + value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = True, - monotonic: bool = False + monotonic: bool = True ): + # super(Counter, self).__init__(self, name, description, unit, value_type) self.name = name self.description = description - self.unit = unit + self.unit = unit, + self.value_type = value_type self.label_keys = label_keys self.enabled = enabled self.monotonic = monotonic self.handles = {} def get_handle(self, - label_values: Tuple[str]) -> metrics_api.GaugeHandle: - """See `opentelemetry.metrics.FloatGauge.get_handle`.""" + label_values: Tuple[str]) -> metrics_api.CounterHandle: + """See `opentelemetry.metrics.FloatCounter.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(float, self.enabled, self.monotonic)) + CounterHandle(self.value_type, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle -class IntGauge(metrics_api.IntGauge): - """See `opentelemetry.metrics.IntGauge`.""" +class Gauge(Metric): + """See `opentelemetry.metrics.Gauge`. + + By default, gauge values can go both up and down (non-monotonic). + Negative inputs will be discarded for monotonic gauge metrics. + """ def __init__( self, name: str, description: str, unit: str, + value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, - enabled: bool = False, + enabled: bool = True, monotonic: bool = False ): self.name = name self.description = description - self.unit = unit + self.unit = unit, + self.value_type = value_type self.label_keys = label_keys self.enabled = enabled self.monotonic = monotonic @@ -207,29 +150,35 @@ def __init__( def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: - """See `opentelemetry.metrics.IntGauge.get_handle`.""" + """See `opentelemetry.metrics.Gauge.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(int, self.enabled, self.monotonic)) + GaugeHandle(self.value_type, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle -class FloatMeasure(metrics_api.FloatMeasure): - """See `opentelemetry.metrics.FloatMeasure`.""" +class Measure(Metric): + """See `opentelemetry.metrics.Measure`. + + By default, measure metrics can accept both positive and negatives. + Negative inputs will be discarded when monotonic is True. + """ def __init__( self, name: str, description: str, unit: str, + value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = False, monotonic: bool = False ): self.name = name self.description = description - self.unit = unit + self.unit = unit, + self.value_type = value_type self.label_keys = label_keys self.enabled = enabled self.monotonic = monotonic @@ -237,40 +186,10 @@ def __init__( def get_handle(self, label_values: Tuple[str]) -> metrics_api.MeasureHandle: - """See `opentelemetry.metrics.FloatMeasure.get_handle`.""" - handle = self.handles.get( - label_values, - MeasureHandle(float, self.enabled, self.monotonic)) - self.handles[label_values] = handle - return handle - - -class IntMeasure(metrics_api.IntMeasure): - """See `opentelemetry.metrics.IntMeasure`.""" - - def __init__( - self, - name: str, - description: str, - unit: str, - label_keys: Tuple[str] = None, - enabled: bool = False, - allow_negative: bool = False - ): - self.name = name - self.description = description - self.unit = unit - self.label_keys = label_keys - self.enabled = enabled - self.allow_negative = allow_negative - self.handles = {} - - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.MeasureHandle: - """See `opentelemetry.metrics.IntMeasure.get_handle`.""" + """See `opentelemetry.metrics.Measure.get_handle`.""" handle = self.handles.get( label_values, - MeasureHandle(int, self.enabled, self.allow_negative)) + MeasureHandle(self.value_type, self.enabled, self.monotonic)) self.handles[label_values] = handle return handle @@ -279,7 +198,7 @@ class CounterHandle(metrics_api.CounterHandle): def __init__( self, - value_type: Union[Type[float], Type[int]], + value_type: metrics_api.ValueType, enabled: bool, monotonic: bool ): @@ -288,12 +207,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def update(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.CounterHandle.update`.""" - self._add(value) - - def _add(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.CounterHandle._add`.""" + def _update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if not self.monotonic and value < 0: @@ -304,6 +218,10 @@ def _add(self, value: Union[float, int]) -> None: return self.data += value + def add(self, value: metrics_api.ValueType) -> None: + """See `opentelemetry.metrics.CounterHandle._add`.""" + self._update(value) + class GaugeHandle(metrics_api.GaugeHandle): @@ -318,12 +236,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def update(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.GaugeHandle.update`.""" - self._set(value) - - def _set(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.GaugeHandle._set`.""" + def _update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if self.monotonic and value < 0: @@ -334,6 +247,10 @@ def _set(self, value: Union[float, int]) -> None: return self.data = value + def set(self, value: metrics_api.ValueType) -> None: + """See `opentelemetry.metrics.GaugeHandle._set`.""" + self._update(value) + class MeasureHandle(metrics_api.MeasureHandle): @@ -348,12 +265,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def update(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.MeasureHandle.update`.""" - self._record(value) - - def _record(self, value: Union[float, int]) -> None: - """See `opentelemetry.metrics.MeasureHandle._record`.""" + def _update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if self.monotonic and value < 0: @@ -364,5 +276,9 @@ def _record(self, value: Union[float, int]) -> None: return # TODO: record + def record(self, value: metrics_api.ValueType) -> None: + """See `opentelemetry.metrics.MeasureHandle._record`.""" + self._update(value) + meter = Meter() \ No newline at end of file diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 404271b111..d2e6ebeb2a 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -28,89 +28,154 @@ def test_record_batch(self): meter = metrics.Meter() label_keys = ("key1") label_values = ("value1") - float_counter = metrics.FloatCounter( + counter = metrics.Counter( "name", "desc", "unit", + float, label_keys, ) - record_tuples = [(float_counter, 1.0)] + record_tuples = [(counter, 1.0)] meter.record_batch(label_values, record_tuples) - self.assertEqual(float_counter.get_handle(label_values).data, 1.0) + self.assertEqual(counter.get_handle(label_values).data, 1.0) + + def test_record_batch_multiple(self): + meter = metrics.Meter() + label_keys = ("key1", "key2", "key3") + label_values = ("value1", "value2", "value3") + counter = metrics.Counter( + "name", + "desc", + "unit", + float, + label_keys, + ) + gauge = metrics.Gauge( + "name", + "desc", + "unit", + int, + label_keys + ) + measure = metrics.Measure( + "name", + "desc", + "unit", + float, + label_keys + ) + record_tuples = [(counter, 1.0), (gauge, 5), (measure, 3.0)] + meter.record_batch(label_values, record_tuples) + self.assertEqual(counter.get_handle(label_values).data, 1.0) + self.assertEqual(gauge.get_handle(label_values).data, 5) + self.assertEqual(measure.get_handle(label_values).data, 0) def test_record_batch_exists(self): meter = metrics.Meter() label_keys = ("key1") label_values = ("value1") - float_counter = metrics.FloatCounter( + counter = metrics.Counter( "name", "desc", "unit", + float, label_keys ) - handle = float_counter.get_handle(label_values) - handle.update(1.0) - record_tuples = [(float_counter, 1.0)] + handle = counter.get_handle(label_values) + handle._update(1.0) + record_tuples = [(counter, 1.0)] meter.record_batch(label_values, record_tuples) - self.assertEqual(float_counter.get_handle(label_values), handle) + self.assertEqual(counter.get_handle(label_values), handle) self.assertEqual(handle.data, 2.0) - def test_create_counter(self): + def test_create_metric(self): meter = metrics.Meter() - counter = meter.create_counter( + counter = meter.create_metric( "name", "desc", "unit", int, + metrics.Counter, () ) - self.assertTrue(isinstance(counter, metrics.IntCounter)) + self.assertTrue(isinstance(counter, metrics.Counter)) + self.assertEqual(counter.value_type, int) self.assertEqual(counter.name, "name") def test_create_gauge(self): meter = metrics.Meter() - gauge = meter.create_gauge( + gauge = meter.create_metric( "name", "desc", "unit", float, + metrics.Gauge, () ) - self.assertTrue(isinstance(gauge, metrics.FloatGauge)) + self.assertTrue(isinstance(gauge, metrics.Gauge)) + self.assertEqual(gauge.value_type, float) self.assertEqual(gauge.name, "name") def test_create_measure(self): meter = metrics.Meter() - measure = meter.create_measure( + measure = meter.create_metric( "name", "desc", "unit", float, + metrics.Measure, () ) - self.assertTrue(isinstance(measure, metrics.FloatMeasure)) + self.assertTrue(isinstance(measure, metrics.Measure)) + self.assertEqual(measure.value_type, float) self.assertEqual(measure.name, "name") class TestMetric(unittest.TestCase): def test_get_handle(self): - metric_types = [metrics.FloatCounter, - metrics.IntCounter, - metrics.FloatGauge, - metrics.IntGauge, - metrics.FloatMeasure, - metrics.IntMeasure] + metric_types = [metrics.Counter, + metrics.Gauge, + metrics.Measure] for _type in metric_types: metric = _type( "name", "desc", "unit", - ("key") - ) + int, + ("key")) label_values = ("value") handle = metric.get_handle(label_values) self.assertEqual(metric.handles.get(label_values), handle) + def test_remove_handle(self): + metric = metrics.Counter( + "name", + "desc", + "unit", + int, + ("key")) + label_values1 = ("value") + label_values2 = ("value2") + metric.get_handle(label_values1) + metric.get_handle(label_values2) + metric.remove_handle(label_values1) + self.assertIsNone(metric.handles.get(label_values1)) + self.assertIsNotNone(metric.handles.get(label_values2)) + + def test_clear(self): + metric = metrics.Counter( + "name", + "desc", + "unit", + int, + ("key")) + label_values1 = ("value") + label_values2 = ("value2") + metric.get_handle(label_values1) + metric.get_handle(label_values2) + metric.clear() + self.assertIsNone(metric.handles.get(label_values1)) + self.assertIsNone(metric.handles.get(label_values2)) class TestCounterHandle(unittest.TestCase): def test_update(self): @@ -119,7 +184,7 @@ def test_update(self): True, False ) - handle.update(2.0) + handle._update(2.0) self.assertEqual(handle.data, 2.0) def test_add(self): @@ -128,7 +193,7 @@ def test_add(self): True, False ) - handle._add(3) + handle.add(3) self.assertEqual(handle.data, 3) def test_add_disabled(self): @@ -137,7 +202,7 @@ def test_add_disabled(self): False, False ) - handle._add(3) + handle.add(3) self.assertEqual(handle.data, 0) @mock.patch('opentelemetry.sdk.metrics.logger') @@ -147,7 +212,7 @@ def test_add_monotonic(self, logger_mock): True, False ) - handle._add(-3) + handle.add(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -158,7 +223,7 @@ def test_add_incorrect_type(self, logger_mock): True, False ) - handle._add(3.0) + handle.add(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -170,7 +235,7 @@ def test_update(self): True, False ) - handle.update(2.0) + handle._update(2.0) self.assertEqual(handle.data, 2.0) def test_set(self): @@ -179,7 +244,7 @@ def test_set(self): True, False ) - handle._set(3) + handle.set(3) self.assertEqual(handle.data, 3) def test_set_disabled(self): @@ -188,7 +253,7 @@ def test_set_disabled(self): False, False ) - handle._set(3) + handle.set(3) self.assertEqual(handle.data, 0) @mock.patch('opentelemetry.sdk.metrics.logger') @@ -198,7 +263,7 @@ def test_set_monotonic(self, logger_mock): True, True ) - handle._set(-3) + handle.set(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -209,7 +274,7 @@ def test_set_incorrect_type(self, logger_mock): True, False ) - handle._set(3.0) + handle.set(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -221,7 +286,7 @@ def test_update(self): False, False ) - handle.update(2.0) + handle._update(2.0) self.assertEqual(handle.data, 0) def test_record(self): @@ -230,7 +295,7 @@ def test_record(self): False, False ) - handle._record(3) + handle.record(3) self.assertEqual(handle.data, 0) def test_record_disabled(self): @@ -239,7 +304,7 @@ def test_record_disabled(self): False, False ) - handle._record(3) + handle.record(3) self.assertEqual(handle.data, 0) @mock.patch('opentelemetry.sdk.metrics.logger') @@ -249,7 +314,7 @@ def test_record_monotonic(self, logger_mock): True, True ) - handle._record(-3) + handle.record(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -260,6 +325,6 @@ def test_record_incorrect_type(self, logger_mock): True, False ) - handle._record(3.0) + handle.record(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() From 8f2a21621e9c59ceaf50ece58828292aa8d9606e Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 17:50:14 -0700 Subject: [PATCH 54/88] Fix typing and examples --- .../src/opentelemetry/metrics/__init__.py | 7 +- .../metrics_example.py | 8 +- .../src/opentelemetry/sdk/metrics/__init__.py | 123 +++++++++--------- .../tests/metrics/test_metrics.py | 6 +- 4 files changed, 77 insertions(+), 67 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index a77f20ce90..3d73b33629 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -33,10 +33,15 @@ from opentelemetry.util import loader -MetricKind = Union[Type["Counter"], Type["Gauge"], Type["Measure"]] ValueType = TypeVar('ValueType', int, float) +class MetricKind(enum.Enum): + COUNTER = 0 + GAUGE = 1 + MEASURE = 2 + + # pylint: disable=unused-argument class Meter: """An interface to allow the recording of metrics. diff --git a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index c59c31c214..c6a2704ab5 100644 --- a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -22,20 +22,20 @@ metrics.set_preferred_meter_implementation(lambda _: Meter()) meter = metrics.meter() -counter = meter.create_counter( +counter = meter.create_metric( "sum numbers", "sum numbers over time", "number", int, - False, - ["environment"], + metrics.MetricKind.COUNTER, + ("environment") ) label_values = ("staging") counter_handle = counter.get_handle(label_values) -counter_handle.update(100) +counter_handle.add(100) meter.record_batch(label_values, [(counter, 50)]) print(counter_handle.data) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 847d11f68f..99a4b9b61d 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -20,40 +20,6 @@ logger = logging.getLogger(__name__) -class Meter(metrics_api.Meter): - """See `opentelemetry.metrics.Meter`.""" - - def record_batch( - self, - label_values: Tuple[str], - record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueType]], - ) -> None: - """See `opentelemetry.metrics.Meter.record_batch`.""" - for metric, value in record_tuples: - handle = metric.get_handle(label_values)._update(value) - - def create_metric( - self, - name: str, - description: str, - unit: str, - value_type: metrics_api.ValueType, - metric_kind: metrics_api.MetricKind, - label_keys: Tuple[str] = None, - enabled: bool = True, - monotonic: bool = False, - ) -> "Metric": - """See `opentelemetry.metrics.Meter.create_metric`.""" - return metric_kind( - name, - description, - unit, - value_type, - label_keys=label_keys, - enabled=enabled, - monotonic=monotonic) - - class Metric(metrics_api.Metric): def __init__( @@ -102,15 +68,15 @@ def __init__( enabled: bool = True, monotonic: bool = True ): - # super(Counter, self).__init__(self, name, description, unit, value_type) - self.name = name - self.description = description - self.unit = unit, - self.value_type = value_type - self.label_keys = label_keys - self.enabled = enabled - self.monotonic = monotonic - self.handles = {} + super(Counter, self).__init__( + name, + description, + unit, + value_type, + label_keys, + enabled, + monotonic) + def get_handle(self, label_values: Tuple[str]) -> metrics_api.CounterHandle: @@ -139,14 +105,14 @@ def __init__( enabled: bool = True, monotonic: bool = False ): - self.name = name - self.description = description - self.unit = unit, - self.value_type = value_type - self.label_keys = label_keys - self.enabled = enabled - self.monotonic = monotonic - self.handles = {} + super(Gauge, self).__init__( + name, + description, + unit, + value_type, + label_keys, + enabled, + monotonic) def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: @@ -175,14 +141,14 @@ def __init__( enabled: bool = False, monotonic: bool = False ): - self.name = name - self.description = description - self.unit = unit, - self.value_type = value_type - self.label_keys = label_keys - self.enabled = enabled - self.monotonic = monotonic - self.handles = {} + super(Measure, self).__init__( + name, + description, + unit, + value_type, + label_keys, + enabled, + monotonic) def get_handle(self, label_values: Tuple[str]) -> metrics_api.MeasureHandle: @@ -281,4 +247,43 @@ def record(self, value: metrics_api.ValueType) -> None: self._update(value) +METRIC_KIND_MAP = { + metrics_api.MetricKind.COUNTER: Counter, + metrics_api.MetricKind.GAUGE: Gauge, + metrics_api.MetricKind.MEASURE: Measure +} +class Meter(metrics_api.Meter): + """See `opentelemetry.metrics.Meter`.""" + + def record_batch( + self, + label_values: Tuple[str], + record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueType]], + ) -> None: + """See `opentelemetry.metrics.Meter.record_batch`.""" + for metric, value in record_tuples: + handle = metric.get_handle(label_values)._update(value) + + def create_metric( + self, + name: str, + description: str, + unit: str, + value_type: metrics_api.ValueType, + metric_kind: metrics_api.MetricKind, + label_keys: Tuple[str] = None, + enabled: bool = True, + monotonic: bool = False + ) -> "Metric": + """See `opentelemetry.metrics.Meter.create_metric`.""" + return METRIC_KIND_MAP[metric_kind]( + name, + description, + unit, + value_type, + label_keys=label_keys, + enabled=enabled, + monotonic=monotonic) + + meter = Meter() \ No newline at end of file diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index d2e6ebeb2a..9720a146d8 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -95,7 +95,7 @@ def test_create_metric(self): "desc", "unit", int, - metrics.Counter, + metrics_api.MetricKind.COUNTER, () ) self.assertTrue(isinstance(counter, metrics.Counter)) @@ -109,7 +109,7 @@ def test_create_gauge(self): "desc", "unit", float, - metrics.Gauge, + metrics_api.MetricKind.GAUGE, () ) self.assertTrue(isinstance(gauge, metrics.Gauge)) @@ -123,7 +123,7 @@ def test_create_measure(self): "desc", "unit", float, - metrics.Measure, + metrics_api.MetricKind.MEASURE, () ) self.assertTrue(isinstance(measure, metrics.Measure)) From 4f4e38501adf3576253f6949a5e560758b2a8314 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 18:02:55 -0700 Subject: [PATCH 55/88] black --- .../src/opentelemetry/metrics/__init__.py | 13 +- .../tests/metrics/test_metrics.py | 3 +- .../metrics_example.py | 4 +- .../src/opentelemetry/sdk/metrics/__init__.py | 96 ++++---- .../src/opentelemetry/sdk/util.py | 1 - .../tests/metrics/test_metrics.py | 207 ++++-------------- 6 files changed, 94 insertions(+), 230 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 3d73b33629..f7e2de4ef7 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -33,7 +33,7 @@ from opentelemetry.util import loader -ValueType = TypeVar('ValueType', int, float) +ValueType = TypeVar("ValueType", int, float) class MetricKind(enum.Enum): @@ -69,7 +69,6 @@ def record_batch( corresponding value to record for that metric. """ - def create_metric( self, name: str, @@ -97,16 +96,16 @@ def create_metric( Returns: A new ``metric_kind`` metric with values of ``value_type``. """ + # Once https://github.com/python/mypy/issues/7092 is resolved, # the following type definition should be replaced with # from opentelemetry.util.loader import ImplementationFactory -ImplementationFactory = Callable[ - [Type[Meter]], Optional[Meter] -] +ImplementationFactory = Callable[[Type[Meter]], Optional[Meter]] _METER = None _METER_FACTORY = None + def meter() -> Meter: """Gets the current global :class:`~.Meter` object. @@ -122,9 +121,7 @@ def meter() -> Meter: return _METER -def set_preferred_meter_implementation( - factory: ImplementationFactory -) -> None: +def set_preferred_meter_implementation(factory: ImplementationFactory) -> None: """Set the factory to be used to create the meter. See :mod:`opentelemetry.util.loader` for details. diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index f71dab9882..689bd8fb33 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -20,7 +20,7 @@ class TestMeter(unittest.TestCase): def setUp(self): self.meter = metrics.Meter() - + def test_record_batch(self): self.meter.record_batch((), ()) @@ -29,7 +29,6 @@ def test_create_metric(self): class TestMetrics(unittest.TestCase): - def test_counter(self): counter = metrics.Counter() counter.get_handle(()) diff --git a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index c6a2704ab5..55e2e4f0fe 100644 --- a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -28,10 +28,10 @@ "number", int, metrics.MetricKind.COUNTER, - ("environment") + ("environment"), ) -label_values = ("staging") +label_values = "staging" counter_handle = counter.get_handle(label_values) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 99a4b9b61d..04797bd488 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -21,7 +21,6 @@ class Metric(metrics_api.Metric): - def __init__( self, name: str, @@ -30,11 +29,11 @@ def __init__( value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = True, - monotonic: bool = False + monotonic: bool = False, ): self.name = name self.description = description - self.unit = unit, + self.unit = (unit,) self.value_type = value_type self.label_keys = label_keys self.enabled = enabled @@ -66,24 +65,20 @@ def __init__( value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = True, - monotonic: bool = True + monotonic: bool = True, ): super(Counter, self).__init__( - name, - description, - unit, - value_type, - label_keys, - enabled, - monotonic) - + name, description, unit, value_type, label_keys, enabled, monotonic + ) - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.CounterHandle: + def get_handle( + self, label_values: Tuple[str] + ) -> metrics_api.CounterHandle: """See `opentelemetry.metrics.FloatCounter.get_handle`.""" handle = self.handles.get( label_values, - CounterHandle(self.value_type, self.enabled, self.monotonic)) + CounterHandle(self.value_type, self.enabled, self.monotonic), + ) self.handles[label_values] = handle return handle @@ -103,23 +98,18 @@ def __init__( value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = True, - monotonic: bool = False + monotonic: bool = False, ): super(Gauge, self).__init__( - name, - description, - unit, - value_type, - label_keys, - enabled, - monotonic) + name, description, unit, value_type, label_keys, enabled, monotonic + ) - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.GaugeHandle: + def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.Gauge.get_handle`.""" handle = self.handles.get( label_values, - GaugeHandle(self.value_type, self.enabled, self.monotonic)) + GaugeHandle(self.value_type, self.enabled, self.monotonic), + ) self.handles[label_values] = handle return handle @@ -139,34 +129,27 @@ def __init__( value_type: metrics_api.ValueType, label_keys: Tuple[str] = None, enabled: bool = False, - monotonic: bool = False + monotonic: bool = False, ): super(Measure, self).__init__( - name, - description, - unit, - value_type, - label_keys, - enabled, - monotonic) + name, description, unit, value_type, label_keys, enabled, monotonic + ) - def get_handle(self, - label_values: Tuple[str]) -> metrics_api.MeasureHandle: + def get_handle( + self, label_values: Tuple[str] + ) -> metrics_api.MeasureHandle: """See `opentelemetry.metrics.Measure.get_handle`.""" handle = self.handles.get( label_values, - MeasureHandle(self.value_type, self.enabled, self.monotonic)) + MeasureHandle(self.value_type, self.enabled, self.monotonic), + ) self.handles[label_values] = handle return handle class CounterHandle(metrics_api.CounterHandle): - def __init__( - self, - value_type: metrics_api.ValueType, - enabled: bool, - monotonic: bool + self, value_type: metrics_api.ValueType, enabled: bool, monotonic: bool ): self.data = 0 self.value_type = value_type @@ -180,7 +163,9 @@ def _update(self, value: metrics_api.ValueType) -> None: logger.warning("Monotonic counter cannot descend.") return if not isinstance(value, self.value_type): - logger.warning("Invalid value passed for %s", self.value_type.__name__) + logger.warning( + "Invalid value passed for %s", self.value_type.__name__ + ) return self.data += value @@ -190,12 +175,11 @@ def add(self, value: metrics_api.ValueType) -> None: class GaugeHandle(metrics_api.GaugeHandle): - def __init__( self, value_type: Union[Type[float], Type[int]], enabled: bool, - monotonic: bool + monotonic: bool, ): self.data = 0 self.value_type = value_type @@ -209,7 +193,9 @@ def _update(self, value: metrics_api.ValueType) -> None: logger.warning("Monotonic gauge cannot descend.") return if not isinstance(value, self.value_type): - logger.warning("Invalid value passed for %s", self.value_type.__name__) + logger.warning( + "Invalid value passed for %s", self.value_type.__name__ + ) return self.data = value @@ -219,12 +205,11 @@ def set(self, value: metrics_api.ValueType) -> None: class MeasureHandle(metrics_api.MeasureHandle): - def __init__( self, value_type: Union[Type[float], Type[int]], enabled: bool, - monotonic: bool + monotonic: bool, ): self.data = 0 self.value_type = value_type @@ -238,7 +223,9 @@ def _update(self, value: metrics_api.ValueType) -> None: logger.warning("Non-negative measure cannot descend.") return if not isinstance(value, self.value_type): - logger.warning("Invalid value passed for %s", self.value_type.__name__) + logger.warning( + "Invalid value passed for %s", self.value_type.__name__ + ) return # TODO: record @@ -250,8 +237,10 @@ def record(self, value: metrics_api.ValueType) -> None: METRIC_KIND_MAP = { metrics_api.MetricKind.COUNTER: Counter, metrics_api.MetricKind.GAUGE: Gauge, - metrics_api.MetricKind.MEASURE: Measure + metrics_api.MetricKind.MEASURE: Measure, } + + class Meter(metrics_api.Meter): """See `opentelemetry.metrics.Meter`.""" @@ -273,7 +262,7 @@ def create_metric( metric_kind: metrics_api.MetricKind, label_keys: Tuple[str] = None, enabled: bool = True, - monotonic: bool = False + monotonic: bool = False, ) -> "Metric": """See `opentelemetry.metrics.Meter.create_metric`.""" return METRIC_KIND_MAP[metric_kind]( @@ -283,7 +272,8 @@ def create_metric( value_type, label_keys=label_keys, enabled=enabled, - monotonic=monotonic) + monotonic=monotonic, + ) -meter = Meter() \ No newline at end of file +meter = Meter() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index d8191ab68e..7ac7b706b8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -142,4 +142,3 @@ def from_map(cls, maxlen, mapping): # pylint: disable=protected-access bounded_dict._dict = mapping return bounded_dict - diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 9720a146d8..91af9913af 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -26,15 +26,9 @@ def test_extends_api(self): def test_record_batch(self): meter = metrics.Meter() - label_keys = ("key1") - label_values = ("value1") - counter = metrics.Counter( - "name", - "desc", - "unit", - float, - label_keys, - ) + label_keys = "key1" + label_values = "value1" + counter = metrics.Counter("name", "desc", "unit", float, label_keys) record_tuples = [(counter, 1.0)] meter.record_batch(label_values, record_tuples) self.assertEqual(counter.get_handle(label_values).data, 1.0) @@ -43,27 +37,9 @@ def test_record_batch_multiple(self): meter = metrics.Meter() label_keys = ("key1", "key2", "key3") label_values = ("value1", "value2", "value3") - counter = metrics.Counter( - "name", - "desc", - "unit", - float, - label_keys, - ) - gauge = metrics.Gauge( - "name", - "desc", - "unit", - int, - label_keys - ) - measure = metrics.Measure( - "name", - "desc", - "unit", - float, - label_keys - ) + counter = metrics.Counter("name", "desc", "unit", float, label_keys) + gauge = metrics.Gauge("name", "desc", "unit", int, label_keys) + measure = metrics.Measure("name", "desc", "unit", float, label_keys) record_tuples = [(counter, 1.0), (gauge, 5), (measure, 3.0)] meter.record_batch(label_values, record_tuples) self.assertEqual(counter.get_handle(label_values).data, 1.0) @@ -72,15 +48,9 @@ def test_record_batch_multiple(self): def test_record_batch_exists(self): meter = metrics.Meter() - label_keys = ("key1") - label_values = ("value1") - counter = metrics.Counter( - "name", - "desc", - "unit", - float, - label_keys - ) + label_keys = "key1" + label_values = "value1" + counter = metrics.Counter("name", "desc", "unit", float, label_keys) handle = counter.get_handle(label_values) handle._update(1.0) record_tuples = [(counter, 1.0)] @@ -91,12 +61,7 @@ def test_record_batch_exists(self): def test_create_metric(self): meter = metrics.Meter() counter = meter.create_metric( - "name", - "desc", - "unit", - int, - metrics_api.MetricKind.COUNTER, - () + "name", "desc", "unit", int, metrics_api.MetricKind.COUNTER, () ) self.assertTrue(isinstance(counter, metrics.Counter)) self.assertEqual(counter.value_type, int) @@ -105,12 +70,7 @@ def test_create_metric(self): def test_create_gauge(self): meter = metrics.Meter() gauge = meter.create_metric( - "name", - "desc", - "unit", - float, - metrics_api.MetricKind.GAUGE, - () + "name", "desc", "unit", float, metrics_api.MetricKind.GAUGE, () ) self.assertTrue(isinstance(gauge, metrics.Gauge)) self.assertEqual(gauge.value_type, float) @@ -119,12 +79,7 @@ def test_create_gauge(self): def test_create_measure(self): meter = metrics.Meter() measure = meter.create_metric( - "name", - "desc", - "unit", - float, - metrics_api.MetricKind.MEASURE, - () + "name", "desc", "unit", float, metrics_api.MetricKind.MEASURE, () ) self.assertTrue(isinstance(measure, metrics.Measure)) self.assertEqual(measure.value_type, float) @@ -133,29 +88,17 @@ def test_create_measure(self): class TestMetric(unittest.TestCase): def test_get_handle(self): - metric_types = [metrics.Counter, - metrics.Gauge, - metrics.Measure] + metric_types = [metrics.Counter, metrics.Gauge, metrics.Measure] for _type in metric_types: - metric = _type( - "name", - "desc", - "unit", - int, - ("key")) - label_values = ("value") + metric = _type("name", "desc", "unit", int, ("key")) + label_values = "value" handle = metric.get_handle(label_values) self.assertEqual(metric.handles.get(label_values), handle) def test_remove_handle(self): - metric = metrics.Counter( - "name", - "desc", - "unit", - int, - ("key")) - label_values1 = ("value") - label_values2 = ("value2") + metric = metrics.Counter("name", "desc", "unit", int, ("key")) + label_values1 = "value" + label_values2 = "value2" metric.get_handle(label_values1) metric.get_handle(label_values2) metric.remove_handle(label_values1) @@ -163,66 +106,42 @@ def test_remove_handle(self): self.assertIsNotNone(metric.handles.get(label_values2)) def test_clear(self): - metric = metrics.Counter( - "name", - "desc", - "unit", - int, - ("key")) - label_values1 = ("value") - label_values2 = ("value2") + metric = metrics.Counter("name", "desc", "unit", int, ("key")) + label_values1 = "value" + label_values2 = "value2" metric.get_handle(label_values1) metric.get_handle(label_values2) metric.clear() self.assertIsNone(metric.handles.get(label_values1)) self.assertIsNone(metric.handles.get(label_values2)) + class TestCounterHandle(unittest.TestCase): def test_update(self): - handle = metrics.CounterHandle( - float, - True, - False - ) + handle = metrics.CounterHandle(float, True, False) handle._update(2.0) self.assertEqual(handle.data, 2.0) def test_add(self): - handle = metrics.CounterHandle( - int, - True, - False - ) + handle = metrics.CounterHandle(int, True, False) handle.add(3) self.assertEqual(handle.data, 3) def test_add_disabled(self): - handle = metrics.CounterHandle( - int, - False, - False - ) + handle = metrics.CounterHandle(int, False, False) handle.add(3) self.assertEqual(handle.data, 0) - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_add_monotonic(self, logger_mock): - handle = metrics.CounterHandle( - int, - True, - False - ) + handle = metrics.CounterHandle(int, True, False) handle.add(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_add_incorrect_type(self, logger_mock): - handle = metrics.CounterHandle( - int, - True, - False - ) + handle = metrics.CounterHandle(int, True, False) handle.add(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -230,50 +149,30 @@ def test_add_incorrect_type(self, logger_mock): class TestGaugeHandle(unittest.TestCase): def test_update(self): - handle = metrics.GaugeHandle( - float, - True, - False - ) + handle = metrics.GaugeHandle(float, True, False) handle._update(2.0) self.assertEqual(handle.data, 2.0) def test_set(self): - handle = metrics.GaugeHandle( - int, - True, - False - ) + handle = metrics.GaugeHandle(int, True, False) handle.set(3) self.assertEqual(handle.data, 3) def test_set_disabled(self): - handle = metrics.GaugeHandle( - int, - False, - False - ) + handle = metrics.GaugeHandle(int, False, False) handle.set(3) self.assertEqual(handle.data, 0) - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_set_monotonic(self, logger_mock): - handle = metrics.GaugeHandle( - int, - True, - True - ) + handle = metrics.GaugeHandle(int, True, True) handle.set(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_set_incorrect_type(self, logger_mock): - handle = metrics.GaugeHandle( - int, - True, - False - ) + handle = metrics.GaugeHandle(int, True, False) handle.set(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() @@ -281,50 +180,30 @@ def test_set_incorrect_type(self, logger_mock): class TestMeasureHandle(unittest.TestCase): def test_update(self): - handle = metrics.MeasureHandle( - float, - False, - False - ) + handle = metrics.MeasureHandle(float, False, False) handle._update(2.0) self.assertEqual(handle.data, 0) def test_record(self): - handle = metrics.MeasureHandle( - int, - False, - False - ) + handle = metrics.MeasureHandle(int, False, False) handle.record(3) self.assertEqual(handle.data, 0) def test_record_disabled(self): - handle = metrics.MeasureHandle( - int, - False, - False - ) + handle = metrics.MeasureHandle(int, False, False) handle.record(3) self.assertEqual(handle.data, 0) - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_record_monotonic(self, logger_mock): - handle = metrics.MeasureHandle( - int, - True, - True - ) + handle = metrics.MeasureHandle(int, True, True) handle.record(-3) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() - @mock.patch('opentelemetry.sdk.metrics.logger') + @mock.patch("opentelemetry.sdk.metrics.logger") def test_record_incorrect_type(self, logger_mock): - handle = metrics.MeasureHandle( - int, - True, - False - ) + handle = metrics.MeasureHandle(int, True, False) handle.record(3.0) self.assertEqual(handle.data, 0) logger_mock.warning.assert_called() From c0f1d10151f7d2ba0011e3bd7417312292d50dd2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 20:19:21 -0700 Subject: [PATCH 56/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 4 ++-- .../tests/metrics/test_metrics.py | 1 + .../src/opentelemetry/sdk/metrics/__init__.py | 23 +++++++++++++------ .../tests/metrics/test_metrics.py | 8 +++---- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index f7e2de4ef7..b8323003b4 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -183,7 +183,7 @@ def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": class Gauge(Metric): """A gauge type metric that expresses a pre-calculated value. - + Gauge metrics have a value that is either `Set()` by explicit instrumentation or observed through a callback. This kind of metric should be used when the metric cannot be expressed as a sum or because @@ -196,7 +196,7 @@ def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": class Measure(Metric): """A measure type metric that represent raw stats that are recorded. - + Measure metrics represent raw statistics that are recorded. By default, measure metrics can accept both positive and negatives. Negative inputs will be discarded when monotonic is True. diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 689bd8fb33..51b8c8da33 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -17,6 +17,7 @@ from opentelemetry import metrics +# pylint: disable=no-self-use class TestMeter(unittest.TestCase): def setUp(self): self.meter = metrics.Meter() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 04797bd488..bbb5745a26 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -21,6 +21,7 @@ class Metric(metrics_api.Metric): + """See `opentelemetry.metrics.Metric`.""" def __init__( self, name: str, @@ -40,6 +41,14 @@ def __init__( self.monotonic = monotonic self.handles = {} + @abstractmethod + def get_handle( + self, + label_values: Tuple[str] + ) -> metrics_api.MetricHandle: + """See `opentelemetry.metrics.Metric.get_handle`.""" + pass + def remove_handle(self, label_values: Tuple[str]) -> None: """See `opentelemetry.metrics.Metric.remove_handle`.""" self.handles.pop(label_values, None) @@ -51,7 +60,7 @@ def clear(self) -> None: class Counter(Metric): """See `opentelemetry.metrics.Counter`. - + By default, counter values can only go up (monotonic). Negative inputs will be discarded for monotonic counter metrics. Counter metrics that have a monotonic option set to False allows negative inputs. @@ -85,7 +94,7 @@ def get_handle( class Gauge(Metric): """See `opentelemetry.metrics.Gauge`. - + By default, gauge values can go both up and down (non-monotonic). Negative inputs will be discarded for monotonic gauge metrics. """ @@ -116,7 +125,7 @@ def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: class Measure(Metric): """See `opentelemetry.metrics.Measure`. - + By default, measure metrics can accept both positive and negatives. Negative inputs will be discarded when monotonic is True. """ @@ -156,7 +165,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def _update(self, value: metrics_api.ValueType) -> None: + def update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if not self.monotonic and value < 0: @@ -186,7 +195,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def _update(self, value: metrics_api.ValueType) -> None: + def update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if self.monotonic and value < 0: @@ -216,7 +225,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def _update(self, value: metrics_api.ValueType) -> None: + def update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if self.monotonic and value < 0: @@ -251,7 +260,7 @@ def record_batch( ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" for metric, value in record_tuples: - handle = metric.get_handle(label_values)._update(value) + metric.get_handle(label_values).update(value) def create_metric( self, diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 91af9913af..8e9dc2bc7f 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -52,7 +52,7 @@ def test_record_batch_exists(self): label_values = "value1" counter = metrics.Counter("name", "desc", "unit", float, label_keys) handle = counter.get_handle(label_values) - handle._update(1.0) + handle.update(1.0) record_tuples = [(counter, 1.0)] meter.record_batch(label_values, record_tuples) self.assertEqual(counter.get_handle(label_values), handle) @@ -119,7 +119,7 @@ def test_clear(self): class TestCounterHandle(unittest.TestCase): def test_update(self): handle = metrics.CounterHandle(float, True, False) - handle._update(2.0) + handle.update(2.0) self.assertEqual(handle.data, 2.0) def test_add(self): @@ -150,7 +150,7 @@ def test_add_incorrect_type(self, logger_mock): class TestGaugeHandle(unittest.TestCase): def test_update(self): handle = metrics.GaugeHandle(float, True, False) - handle._update(2.0) + handle.update(2.0) self.assertEqual(handle.data, 2.0) def test_set(self): @@ -181,7 +181,7 @@ def test_set_incorrect_type(self, logger_mock): class TestMeasureHandle(unittest.TestCase): def test_update(self): handle = metrics.MeasureHandle(float, False, False) - handle._update(2.0) + handle.update(2.0) self.assertEqual(handle.data, 0) def test_record(self): From bbdc9df632e508f62f4c65bbcf45749fcab7f959 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 20:29:32 -0700 Subject: [PATCH 57/88] remove override --- opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index bbb5745a26..d0881431ce 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -41,7 +41,6 @@ def __init__( self.monotonic = monotonic self.handles = {} - @abstractmethod def get_handle( self, label_values: Tuple[str] From bc617c06435ca05d444bbb2cb7a9866760734263 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 20:36:06 -0700 Subject: [PATCH 58/88] Fix tests --- opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index d0881431ce..c53cfb5814 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -179,7 +179,7 @@ def update(self, value: metrics_api.ValueType) -> None: def add(self, value: metrics_api.ValueType) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" - self._update(value) + self.update(value) class GaugeHandle(metrics_api.GaugeHandle): @@ -209,7 +209,7 @@ def update(self, value: metrics_api.ValueType) -> None: def set(self, value: metrics_api.ValueType) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" - self._update(value) + self.update(value) class MeasureHandle(metrics_api.MeasureHandle): @@ -239,7 +239,7 @@ def update(self, value: metrics_api.ValueType) -> None: def record(self, value: metrics_api.ValueType) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" - self._update(value) + self.update(value) METRIC_KIND_MAP = { From 081556b5a803d7fdc5b4d4e5ef35d80bb00daa03 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 20:45:43 -0700 Subject: [PATCH 59/88] mypy --- opentelemetry-api/tests/metrics/test_metrics.py | 8 ++++---- .../src/opentelemetry/sdk/metrics/__init__.py | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 51b8c8da33..30dea51615 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -32,19 +32,19 @@ def test_create_metric(self): class TestMetrics(unittest.TestCase): def test_counter(self): counter = metrics.Counter() - counter.get_handle(()) + counter.get_handle(("test")) def test_gauge(self): gauge = metrics.Gauge() - gauge.get_handle(()) + gauge.get_handle(("test")) def test_measure(self): measure = metrics.Measure() - measure.get_handle(()) + measure.get_handle(("test")) def test_remove_handle(self): counter = metrics.Counter() - counter.remove_handle(()) + counter.remove_handle(("test")) def test_clear(self): counter = metrics.Counter() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index c53cfb5814..9f4878446e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -22,6 +22,7 @@ class Metric(metrics_api.Metric): """See `opentelemetry.metrics.Metric`.""" + def __init__( self, name: str, @@ -41,10 +42,7 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle( - self, - label_values: Tuple[str] - ) -> metrics_api.MetricHandle: + def get_handle(self, label_values: Tuple[str]) -> metrics_api.MetricHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" pass From 77650be4aa996718276c0a86195ddf340f61ae17 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 21:55:12 -0700 Subject: [PATCH 60/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 31 ++++++++++--------- .../src/opentelemetry/sdk/metrics/__init__.py | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index b8323003b4..4569773f06 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -29,11 +29,11 @@ import enum from abc import ABC, abstractmethod -from typing import Callable, Generic, Optional, Tuple, Type, TypeVar, Union +from typing import Callable, Optional, Tuple, Type, TypeVar from opentelemetry.util import loader -ValueType = TypeVar("ValueType", int, float) +_ValueType = TypeVar("_ValueType", int, float) class MetricKind(enum.Enum): @@ -54,7 +54,7 @@ class Meter: def record_batch( self, label_values: Tuple[str], - record_tuples: Tuple[Tuple["Metric", "ValueType"]], + record_tuples: Tuple[Tuple["Metric", _ValueType]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -64,7 +64,7 @@ def record_batch( Args: label_values: The values that will be matched against to record for - the handles under each metric that has those labels. + the handles under each metric that has those labels. record_tuples: A tuple of pairs of `Metric` s and the corresponding value to record for that metric. """ @@ -74,19 +74,20 @@ def create_metric( name: str, description: str, unit: str, - value_type: "ValueType", - metric_kind: "MetricKind", + value_type: Type[_ValueType], + metric_kind: MetricKind, label_keys: Tuple[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> "MetricKind": - """Creates a `metric_kind` metric with type `value_type`. + ) -> "Metric": + """Creates a ``metric_kind`` metric with type ``value_type``. 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. + metric_kind: The kind of metric being created. label_keys: The keys for the labels with dynamic values. Order of the tuple is important as the same order must be used on recording when suppling values for these labels. @@ -184,7 +185,7 @@ def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": class Gauge(Metric): """A gauge type metric that expresses a pre-calculated value. - Gauge metrics have a value that is either `Set()` by explicit + Gauge metrics have a value that is either ``Set`` by explicit instrumentation or observed through a callback. This kind of metric should be used when the metric cannot be expressed as a sum or because the measurement interval is arbitrary. @@ -211,15 +212,15 @@ class MetricHandle: class CounterHandle(MetricHandle): - def add(self, value: "ValueType") -> None: - """Increases the value of the handle by `value`""" + def add(self, value: _ValueType) -> None: + """Increases the value of the handle by ``value``""" class GaugeHandle(MetricHandle): - def set(self, value: "ValueType") -> None: - """Sets the current value of the handle to `value`.""" + def set(self, value: _ValueType) -> None: + """Sets the current value of the handle to ``value``.""" class MeasureHandle(MetricHandle): - def record(self, value: "ValueType") -> None: - """Records the given `value` to this handle.""" + def record(self, value: _ValueType) -> None: + """Records the given ``value`` to this handle.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 9f4878446e..8514c2ec46 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -44,7 +44,7 @@ def __init__( def get_handle(self, label_values: Tuple[str]) -> metrics_api.MetricHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" - pass + pass # pylint: disable=unnecessary-pass def remove_handle(self, label_values: Tuple[str]) -> None: """See `opentelemetry.metrics.Metric.remove_handle`.""" From d1b8c325a789b09cb4b75dd9c06ca16fc6396549 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 22:02:50 -0700 Subject: [PATCH 61/88] fix type --- .../src/opentelemetry/metrics/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 4569773f06..ba32118452 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -33,7 +33,7 @@ from opentelemetry.util import loader -_ValueType = TypeVar("_ValueType", int, float) +ValueType = TypeVar("_ValueType", int, float) class MetricKind(enum.Enum): @@ -54,7 +54,7 @@ class Meter: def record_batch( self, label_values: Tuple[str], - record_tuples: Tuple[Tuple["Metric", _ValueType]], + record_tuples: Tuple[Tuple["Metric", ValueType]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -74,7 +74,7 @@ def create_metric( name: str, description: str, unit: str, - value_type: Type[_ValueType], + value_type: Type[ValueType], metric_kind: MetricKind, label_keys: Tuple[str] = None, enabled: bool = True, @@ -212,15 +212,15 @@ class MetricHandle: class CounterHandle(MetricHandle): - def add(self, value: _ValueType) -> None: + def add(self, value: ValueType) -> None: """Increases the value of the handle by ``value``""" class GaugeHandle(MetricHandle): - def set(self, value: _ValueType) -> None: + def set(self, value: ValueType) -> None: """Sets the current value of the handle to ``value``.""" class MeasureHandle(MetricHandle): - def record(self, value: _ValueType) -> None: + def record(self, value: ValueType) -> None: """Records the given ``value`` to this handle.""" From 1a8a03753c54ce16ba16ed6c458c3967fdc1fb27 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 22:50:38 -0700 Subject: [PATCH 62/88] fix typing --- .../src/opentelemetry/metrics/__init__.py | 6 ++--- .../src/opentelemetry/sdk/metrics/__init__.py | 24 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index ba32118452..9e3c832423 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -29,11 +29,11 @@ import enum from abc import ABC, abstractmethod -from typing import Callable, Optional, Tuple, Type, TypeVar +from typing import Callable, Optional, Tuple, Type, Union from opentelemetry.util import loader -ValueType = TypeVar("_ValueType", int, float) +ValueType = Union[int, float] class MetricKind(enum.Enum): @@ -74,7 +74,7 @@ def create_metric( name: str, description: str, unit: str, - value_type: Type[ValueType], + value_type: ValueType, metric_kind: MetricKind, label_keys: Tuple[str] = None, enabled: bool = True, diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 8514c2ec46..9adee82e13 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -74,7 +74,13 @@ def __init__( monotonic: bool = True, ): super(Counter, self).__init__( - name, description, unit, value_type, label_keys, enabled, monotonic + name, + description, + unit, + value_type, + label_keys=label_keys, + enabled=enabled, + monotonic=monotonic ) def get_handle( @@ -107,7 +113,13 @@ def __init__( monotonic: bool = False, ): super(Gauge, self).__init__( - name, description, unit, value_type, label_keys, enabled, monotonic + name, + description, + unit, + value_type, + label_keys=label_keys, + enabled=enabled, + monotonic=monotonic ) def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: @@ -138,7 +150,13 @@ def __init__( monotonic: bool = False, ): super(Measure, self).__init__( - name, description, unit, value_type, label_keys, enabled, monotonic + name, + description, + unit, + value_type, + label_keys=label_keys, + enabled=enabled, + monotonic=monotonic ) def get_handle( From 5c58f93c39d6feeaac6e7a62fefa34904f32f2ee Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 23:19:30 -0700 Subject: [PATCH 63/88] fix tests --- .../tests/metrics/test_metrics.py | 8 ++--- .../src/opentelemetry/sdk/metrics/__init__.py | 10 +++---- .../tests/metrics/test_metrics.py | 30 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 30dea51615..7eb99aadde 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -32,19 +32,19 @@ def test_create_metric(self): class TestMetrics(unittest.TestCase): def test_counter(self): counter = metrics.Counter() - counter.get_handle(("test")) + counter.get_handle(("test",)) def test_gauge(self): gauge = metrics.Gauge() - gauge.get_handle(("test")) + gauge.get_handle(("test",)) def test_measure(self): measure = metrics.Measure() - measure.get_handle(("test")) + measure.get_handle(("test",)) def test_remove_handle(self): counter = metrics.Counter() - counter.remove_handle(("test")) + counter.remove_handle(("test",)) def test_clear(self): counter = metrics.Counter() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 9adee82e13..9c28c3b339 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -80,7 +80,7 @@ def __init__( value_type, label_keys=label_keys, enabled=enabled, - monotonic=monotonic + monotonic=monotonic, ) def get_handle( @@ -119,7 +119,7 @@ def __init__( value_type, label_keys=label_keys, enabled=enabled, - monotonic=monotonic + monotonic=monotonic, ) def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: @@ -156,7 +156,7 @@ def __init__( value_type, label_keys=label_keys, enabled=enabled, - monotonic=monotonic + monotonic=monotonic, ) def get_handle( @@ -188,7 +188,7 @@ def update(self, value: metrics_api.ValueType) -> None: return if not isinstance(value, self.value_type): logger.warning( - "Invalid value passed for %s", self.value_type.__name__ + "Invalid value passed for %s.", self.value_type.__name__ ) return self.data += value @@ -244,7 +244,7 @@ def update(self, value: metrics_api.ValueType) -> None: if not self.enabled: return if self.monotonic and value < 0: - logger.warning("Non-negative measure cannot descend.") + logger.warning("Monotonic measure cannot descend.") return if not isinstance(value, self.value_type): logger.warning( diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 8e9dc2bc7f..d29b1f2ad6 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -26,8 +26,8 @@ def test_extends_api(self): def test_record_batch(self): meter = metrics.Meter() - label_keys = "key1" - label_values = "value1" + label_keys = ("key1",) + label_values = ("value1",) counter = metrics.Counter("name", "desc", "unit", float, label_keys) record_tuples = [(counter, 1.0)] meter.record_batch(label_values, record_tuples) @@ -48,8 +48,8 @@ def test_record_batch_multiple(self): def test_record_batch_exists(self): meter = metrics.Meter() - label_keys = "key1" - label_values = "value1" + label_keys = ("key1",) + label_values = ("value1",) counter = metrics.Counter("name", "desc", "unit", float, label_keys) handle = counter.get_handle(label_values) handle.update(1.0) @@ -91,14 +91,14 @@ def test_get_handle(self): metric_types = [metrics.Counter, metrics.Gauge, metrics.Measure] for _type in metric_types: metric = _type("name", "desc", "unit", int, ("key")) - label_values = "value" + label_values = ("value",) handle = metric.get_handle(label_values) self.assertEqual(metric.handles.get(label_values), handle) def test_remove_handle(self): metric = metrics.Counter("name", "desc", "unit", int, ("key")) - label_values1 = "value" - label_values2 = "value2" + label_values1 = ("value",) + label_values2 = ("value2",) metric.get_handle(label_values1) metric.get_handle(label_values2) metric.remove_handle(label_values1) @@ -107,8 +107,8 @@ def test_remove_handle(self): def test_clear(self): metric = metrics.Counter("name", "desc", "unit", int, ("key")) - label_values1 = "value" - label_values2 = "value2" + label_values1 = ("value",) + label_values2 = ("value2",) metric.get_handle(label_values1) metric.get_handle(label_values2) metric.clear() @@ -137,14 +137,14 @@ def test_add_monotonic(self, logger_mock): handle = metrics.CounterHandle(int, True, False) handle.add(-3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) @mock.patch("opentelemetry.sdk.metrics.logger") def test_add_incorrect_type(self, logger_mock): handle = metrics.CounterHandle(int, True, False) handle.add(3.0) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) class TestGaugeHandle(unittest.TestCase): @@ -168,14 +168,14 @@ def test_set_monotonic(self, logger_mock): handle = metrics.GaugeHandle(int, True, True) handle.set(-3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) @mock.patch("opentelemetry.sdk.metrics.logger") def test_set_incorrect_type(self, logger_mock): handle = metrics.GaugeHandle(int, True, False) handle.set(3.0) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) class TestMeasureHandle(unittest.TestCase): @@ -199,11 +199,11 @@ def test_record_monotonic(self, logger_mock): handle = metrics.MeasureHandle(int, True, True) handle.record(-3) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) @mock.patch("opentelemetry.sdk.metrics.logger") def test_record_incorrect_type(self, logger_mock): handle = metrics.MeasureHandle(int, True, False) handle.record(3.0) self.assertEqual(handle.data, 0) - logger_mock.warning.assert_called() + self.assertTrue(logger_mock.warning.called) From 1886a7ef9d8e7df3c3a96d699eaf058171c51fbe Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 23:26:41 -0700 Subject: [PATCH 64/88] isort --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 9e3c832423..57aa090d69 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,7 +27,6 @@ """ import enum - from abc import ABC, abstractmethod from typing import Callable, Optional, Tuple, Type, Union From 90fbbbbcdf7230e364cfc8f6fa3d441c71551d21 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 23:32:32 -0700 Subject: [PATCH 65/88] isort --- opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 9c28c3b339..ad704f5d39 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,8 +13,8 @@ # limitations under the License. import logging - from typing import Tuple, Type, Union + from opentelemetry import metrics as metrics_api logger = logging.getLogger(__name__) From f602d708fdc1bdc7bd4dda59b75a0b25be70ac12 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 23:40:20 -0700 Subject: [PATCH 66/88] isort --- opentelemetry-sdk/src/opentelemetry/sdk/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util.py b/opentelemetry-sdk/src/opentelemetry/sdk/util.py index 7ac7b706b8..b4e9b83d83 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util.py @@ -15,7 +15,6 @@ import datetime import threading import time - from collections import OrderedDict, deque try: From 3d6654ba790d41b3d38506a3a4cbbcd755d16b48 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 23 Sep 2019 23:47:20 -0700 Subject: [PATCH 67/88] isort --- .../src/opentelemetry_example_app/metrics_example.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index 55e2e4f0fe..6f75e22a3c 100644 --- a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -19,7 +19,6 @@ from opentelemetry import metrics from opentelemetry.sdk.metrics import Meter - metrics.set_preferred_meter_implementation(lambda _: Meter()) meter = metrics.meter() counter = meter.create_metric( From 6cd6ede6c6712b48dda4608c99cc2019c304b4b7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 25 Sep 2019 13:53:32 -0700 Subject: [PATCH 68/88] noop --- .../src/opentelemetry/metrics/__init__.py | 21 +++++++++++++++++-- .../tests/metrics/test_metrics.py | 12 +++++++---- .../metrics_example.py | 6 +++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 57aa090d69..b138974e41 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -95,7 +95,7 @@ def create_metric( Returns: A new ``metric_kind`` metric with values of ``value_type``. """ - + return DefaultMetric() # Once https://github.com/python/mypy/issues/7092 is resolved, # the following type definition should be replaced with @@ -174,11 +174,19 @@ def clear(self) -> None: """Removes all handles from the `Metric`.""" +class DefaultMetric(Metric): + """The default Metric used when no Metric implementation is available.""" + + def get_handle(self, label_values: Tuple[str]) -> "MetricHandle": + return DefaultMetricHandle() + + class Counter(Metric): """A counter type metric that expresses the computation of a sum.""" def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": """Gets a `CounterHandle`.""" + return CounterHandle() class Gauge(Metric): @@ -192,6 +200,7 @@ class Gauge(Metric): def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": """Gets a `GaugeHandle`.""" + return GaugeHandle() class Measure(Metric): @@ -204,12 +213,20 @@ class Measure(Metric): def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" + return MeasureHandle() -class MetricHandle: +class MetricHandle(ABC): """An interface for metric handles.""" +class DefaultMetricHandle: + """The default MetricHandle. + + Used when no MetricHandle implementation is available. + """ + + class CounterHandle(MetricHandle): def add(self, value: ValueType) -> None: """Increases the value of the handle by ``value``""" diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 7eb99aadde..11116943de 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -26,21 +26,25 @@ def test_record_batch(self): self.meter.record_batch((), ()) def test_create_metric(self): - self.meter.create_metric("", "", "", float, metrics.Counter) + metric = self.meter.create_metric("", "", "", float, metrics.Counter) + self.assertIsInstance(metric, metrics.DefaultMetric) class TestMetrics(unittest.TestCase): def test_counter(self): counter = metrics.Counter() - counter.get_handle(("test",)) + handle = counter.get_handle(("test",)) + self.assertIsInstance(handle, metrics.CounterHandle) def test_gauge(self): gauge = metrics.Gauge() - gauge.get_handle(("test",)) + handle = gauge.get_handle(("test",)) + self.assertIsInstance(handle, metrics.GaugeHandle) def test_measure(self): measure = metrics.Measure() - measure.get_handle(("test",)) + handle = measure.get_handle(("test",)) + self.assertIsInstance(handle, metrics.MeasureHandle) def test_remove_handle(self): counter = metrics.Counter() diff --git a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index 6f75e22a3c..1c9db3def7 100644 --- a/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -22,9 +22,9 @@ metrics.set_preferred_meter_implementation(lambda _: Meter()) meter = metrics.meter() counter = meter.create_metric( - "sum numbers", - "sum numbers over time", - "number", + "available memory", + "available memory", + "bytes", int, metrics.MetricKind.COUNTER, ("environment"), From d8e9a645e7ddd329e229e7a9cf841502e69c552b Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 25 Sep 2019 14:04:43 -0700 Subject: [PATCH 69/88] lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index b138974e41..75ef693713 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -97,6 +97,7 @@ def create_metric( """ return DefaultMetric() + # Once https://github.com/python/mypy/issues/7092 is resolved, # the following type definition should be replaced with # from opentelemetry.util.loader import ImplementationFactory @@ -177,7 +178,7 @@ def clear(self) -> None: class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" - def get_handle(self, label_values: Tuple[str]) -> "MetricHandle": + def get_handle(self, label_values: Tuple[str]) -> "DefaultMetricHandle": return DefaultMetricHandle() From 311b1c455ab6c3eb6498d19c6dfdf43b46d5ac53 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 25 Sep 2019 14:33:43 -0700 Subject: [PATCH 70/88] lint --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 75ef693713..4f67890908 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -95,6 +95,7 @@ def create_metric( Returns: A new ``metric_kind`` metric with values of ``value_type``. """ + # pylint: disable=no-self-use return DefaultMetric() @@ -221,9 +222,9 @@ class MetricHandle(ABC): """An interface for metric handles.""" -class DefaultMetricHandle: +class DefaultMetricHandle(MetricHandle): """The default MetricHandle. - + Used when no MetricHandle implementation is available. """ From 8ea1bd753a81f03442621d6ce3760c5514047f59 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 07:46:01 -0700 Subject: [PATCH 71/88] fix tuple typing --- .../src/opentelemetry/metrics/__init__.py | 14 ++++++------ .../src/opentelemetry/sdk/metrics/__init__.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 57aa090d69..ba60e996e1 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -52,7 +52,7 @@ class Meter: def record_batch( self, - label_values: Tuple[str], + label_values: Tuple[str, ...], record_tuples: Tuple[Tuple["Metric", ValueType]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -75,7 +75,7 @@ def create_metric( unit: str, value_type: ValueType, metric_kind: MetricKind, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": @@ -147,7 +147,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: Tuple[str]) -> "MetricHandle": + def get_handle(self, label_values: Tuple[str, ...]) -> "MetricHandle": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -161,7 +161,7 @@ def get_handle(self, label_values: Tuple[str]) -> "MetricHandle": label_values: Values to associate with the returned handle. """ - def remove_handle(self, label_values: Tuple[str]) -> None: + def remove_handle(self, label_values: Tuple[str, ...]) -> None: """Removes the handle from the Metric, if present. The handle with matching label values will be removed. @@ -177,7 +177,7 @@ def clear(self) -> None: class Counter(Metric): """A counter type metric that expresses the computation of a sum.""" - def get_handle(self, label_values: Tuple[str]) -> "CounterHandle": + def get_handle(self, label_values: Tuple[str, ...]) -> "CounterHandle": """Gets a `CounterHandle`.""" @@ -190,7 +190,7 @@ class Gauge(Metric): the measurement interval is arbitrary. """ - def get_handle(self, label_values: Tuple[str]) -> "GaugeHandle": + def get_handle(self, label_values: Tuple[str, ...]) -> "GaugeHandle": """Gets a `GaugeHandle`.""" @@ -202,7 +202,7 @@ class Measure(Metric): Negative inputs will be discarded when monotonic is True. """ - def get_handle(self, label_values: Tuple[str]) -> "MeasureHandle": + def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index ad704f5d39..5f8e104082 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -29,7 +29,7 @@ def __init__( description: str, unit: str, value_type: metrics_api.ValueType, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): @@ -42,11 +42,11 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Tuple[str]) -> metrics_api.MetricHandle: + def get_handle(self, label_values: Tuple[str, ...]) -> metrics_api.MetricHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" pass # pylint: disable=unnecessary-pass - def remove_handle(self, label_values: Tuple[str]) -> None: + def remove_handle(self, label_values: Tuple[str, ...]) -> None: """See `opentelemetry.metrics.Metric.remove_handle`.""" self.handles.pop(label_values, None) @@ -69,7 +69,7 @@ def __init__( description: str, unit: str, value_type: metrics_api.ValueType, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = True, ): @@ -84,7 +84,7 @@ def __init__( ) def get_handle( - self, label_values: Tuple[str] + self, label_values: Tuple[str, ...] ) -> metrics_api.CounterHandle: """See `opentelemetry.metrics.FloatCounter.get_handle`.""" handle = self.handles.get( @@ -108,7 +108,7 @@ def __init__( description: str, unit: str, value_type: metrics_api.ValueType, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): @@ -122,7 +122,7 @@ def __init__( monotonic=monotonic, ) - def get_handle(self, label_values: Tuple[str]) -> metrics_api.GaugeHandle: + def get_handle(self, label_values: Tuple[str, ...]) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.Gauge.get_handle`.""" handle = self.handles.get( label_values, @@ -145,7 +145,7 @@ def __init__( description: str, unit: str, value_type: metrics_api.ValueType, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = False, monotonic: bool = False, ): @@ -160,7 +160,7 @@ def __init__( ) def get_handle( - self, label_values: Tuple[str] + self, label_values: Tuple[str, ...] ) -> metrics_api.MeasureHandle: """See `opentelemetry.metrics.Measure.get_handle`.""" handle = self.handles.get( @@ -270,7 +270,7 @@ class Meter(metrics_api.Meter): def record_batch( self, - label_values: Tuple[str], + label_values: Tuple[str, ...], record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueType]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" @@ -284,7 +284,7 @@ def create_metric( unit: str, value_type: metrics_api.ValueType, metric_kind: metrics_api.MetricKind, - label_keys: Tuple[str] = None, + label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": From 1090a6c81ffa4fcf58d7d004a00a8e572e5eac9b Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 07:48:51 -0700 Subject: [PATCH 72/88] fix type --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 5c9ec16478..f2f0613ed2 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -179,7 +179,7 @@ def clear(self) -> None: class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" - def get_handle(self, label_values: Tuple[str]) -> "DefaultMetricHandle": + def get_handle(self, label_values: Tuple[str, ...]) -> "DefaultMetricHandle": return DefaultMetricHandle() From 18e6a113a46524b0c5264675d55943f63cd0e08f Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 07:54:33 -0700 Subject: [PATCH 73/88] black --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 4 +++- .../src/opentelemetry/sdk/metrics/__init__.py | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index f2f0613ed2..e81cd5f483 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -179,7 +179,9 @@ def clear(self) -> None: class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" - def get_handle(self, label_values: Tuple[str, ...]) -> "DefaultMetricHandle": + def get_handle( + self, label_values: Tuple[str, ...] + ) -> "DefaultMetricHandle": return DefaultMetricHandle() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 5f8e104082..8ba35d63ee 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -42,7 +42,9 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Tuple[str, ...]) -> metrics_api.MetricHandle: + def get_handle( + self, label_values: Tuple[str, ...] + ) -> metrics_api.MetricHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" pass # pylint: disable=unnecessary-pass @@ -122,7 +124,9 @@ def __init__( monotonic=monotonic, ) - def get_handle(self, label_values: Tuple[str, ...]) -> metrics_api.GaugeHandle: + def get_handle( + self, label_values: Tuple[str, ...] + ) -> metrics_api.GaugeHandle: """See `opentelemetry.metrics.Gauge.get_handle`.""" handle = self.handles.get( label_values, From 4b03cb7e8dd3c6707b6933bd7d862380dcd16d7b Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 13:57:57 -0700 Subject: [PATCH 74/88] address comments --- .../src/opentelemetry/metrics/__init__.py | 212 ++++++++-------- .../tests/metrics/test_metrics.py | 8 - .../src/opentelemetry/sdk/metrics/__init__.py | 229 ++++++------------ .../tests/metrics/test_metrics.py | 28 +-- 4 files changed, 180 insertions(+), 297 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index e81cd5f483..20278d7edb 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -28,17 +28,102 @@ """ import enum from abc import ABC, abstractmethod -from typing import Callable, Optional, Tuple, Type, Union +from typing import Callable, Optional, Tuple, Type, TypeVar from opentelemetry.util import loader -ValueType = Union[int, float] +_ValueT = TypeVar("_ValueT", int, float) -class MetricKind(enum.Enum): - COUNTER = 0 - GAUGE = 1 - MEASURE = 2 +class DefaultMetricHandle: + """The default MetricHandle. + + Used when no MetricHandle implementation is available. + """ + + +class CounterHandle: + def add(self, value: Type[_ValueT]) -> None: + """Increases the value of the handle by ``value``""" + + +class GaugeHandle: + def set(self, value: Type[_ValueT]) -> None: + """Sets the current value of the handle to ``value``.""" + + +class MeasureHandle: + def record(self, value: Type[_ValueT]) -> None: + """Records the given ``value`` to this handle.""" + + +class Metric(ABC): + """Base class for various types of metrics. + + Metric class that inherit from this class are specialized with the type of + handle that the metric holds. + """ + + @abstractmethod + def get_handle(self, label_values: Tuple[str, ...]) -> "object": + """Gets a handle, used for repeated-use of metrics instruments. + + Handles are useful to reduce the cost of repeatedly recording a metric + with a pre-defined set of label values. All metric kinds (counter, + gauge, measure) support declaring a set of required label keys. The + values corresponding to these keys should be specified in every handle. + "Unspecified" label values, in cases where a handle is requested but + a value was not provided are permitted. + + Args: + label_values: Values to associate with the returned handle. + """ + + +class DefaultMetric(Metric): + """The default Metric used when no Metric implementation is available.""" + + def get_handle( + self, label_values: Tuple[str, ...] + ) -> "DefaultMetricHandle": + return DefaultMetricHandle() + + +class Counter(Metric): + """A counter type metric that expresses the computation of a sum.""" + + def get_handle(self, label_values: Tuple[str, ...]) -> "CounterHandle": + """Gets a `CounterHandle`.""" + return CounterHandle() + + +class Gauge(Metric): + """A gauge type metric that expresses a pre-calculated value. + + Gauge metrics have a value that is either ``Set`` by explicit + instrumentation or observed through a callback. This kind of metric + should be used when the metric cannot be expressed as a sum or because + the measurement interval is arbitrary. + """ + + def get_handle(self, label_values: Tuple[str, ...]) -> "GaugeHandle": + """Gets a `GaugeHandle`.""" + return GaugeHandle() + + +class Measure(Metric): + """A measure type metric that represent raw stats that are recorded. + + Measure metrics represent raw statistics that are recorded. By + default, measure metrics can accept both positive and negatives. + Negative inputs will be discarded when monotonic is True. + """ + + def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": + """Gets a `MeasureHandle` with a float value.""" + return MeasureHandle() + +_MetricT = TypeVar("_MetricT", Counter, Gauge, Measure) # pylint: disable=unused-argument @@ -53,7 +138,7 @@ class Meter: def record_batch( self, label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple["Metric", ValueType]], + record_tuples: Tuple[Tuple["Metric", Type[_ValueT]]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -73,8 +158,8 @@ def create_metric( name: str, description: str, unit: str, - value_type: ValueType, - metric_kind: MetricKind, + value_type: Type[_ValueT], + metric_type: Type[_MetricT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, @@ -86,14 +171,14 @@ def create_metric( description: Human-readable description of the metric. unit: Unit of the metric values. value_type: The type of values being recorded by the metric. - metric_kind: The kind of metric being created. + metric_type: The type of metric being created. label_keys: The keys for the labels with dynamic values. Order of the tuple is important as the same order must be used on recording when suppling values for these labels. enabled: Whether to report the metric by default. monotonic: Whether to only allow non-negative values. - Returns: A new ``metric_kind`` metric with values of ``value_type``. + Returns: A new ``metric_type`` metric with values of ``value_type``. """ # pylint: disable=no-self-use return DefaultMetric() @@ -139,108 +224,3 @@ def set_preferred_meter_implementation(factory: ImplementationFactory) -> None: raise RuntimeError("Meter already loaded.") _METER_FACTORY = factory - - -class Metric(ABC): - """Base class for various types of metrics. - - Metric class that inherit from this class are specialized with the type of - handle that the metric holds. - """ - - @abstractmethod - def get_handle(self, label_values: Tuple[str, ...]) -> "MetricHandle": - """Gets a handle, used for repeated-use of metrics instruments. - - Handles are useful to reduce the cost of repeatedly recording a metric - with a pre-defined set of label values. All metric kinds (counter, - gauge, measure) support declaring a set of required label keys. The - values corresponding to these keys should be specified in every handle. - "Unspecified" label values, in cases where a handle is requested but - a value was not provided are permitted. - - Args: - label_values: Values to associate with the returned handle. - """ - - def remove_handle(self, label_values: Tuple[str, ...]) -> None: - """Removes the handle from the Metric, if present. - - The handle with matching label values will be removed. - - args: - label_values: The label values to match against. - """ - - def clear(self) -> None: - """Removes all handles from the `Metric`.""" - - -class DefaultMetric(Metric): - """The default Metric used when no Metric implementation is available.""" - - def get_handle( - self, label_values: Tuple[str, ...] - ) -> "DefaultMetricHandle": - return DefaultMetricHandle() - - -class Counter(Metric): - """A counter type metric that expresses the computation of a sum.""" - - def get_handle(self, label_values: Tuple[str, ...]) -> "CounterHandle": - """Gets a `CounterHandle`.""" - return CounterHandle() - - -class Gauge(Metric): - """A gauge type metric that expresses a pre-calculated value. - - Gauge metrics have a value that is either ``Set`` by explicit - instrumentation or observed through a callback. This kind of metric - should be used when the metric cannot be expressed as a sum or because - the measurement interval is arbitrary. - """ - - def get_handle(self, label_values: Tuple[str, ...]) -> "GaugeHandle": - """Gets a `GaugeHandle`.""" - return GaugeHandle() - - -class Measure(Metric): - """A measure type metric that represent raw stats that are recorded. - - Measure metrics represent raw statistics that are recorded. By - default, measure metrics can accept both positive and negatives. - Negative inputs will be discarded when monotonic is True. - """ - - def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": - """Gets a `MeasureHandle` with a float value.""" - return MeasureHandle() - - -class MetricHandle(ABC): - """An interface for metric handles.""" - - -class DefaultMetricHandle(MetricHandle): - """The default MetricHandle. - - Used when no MetricHandle implementation is available. - """ - - -class CounterHandle(MetricHandle): - def add(self, value: ValueType) -> None: - """Increases the value of the handle by ``value``""" - - -class GaugeHandle(MetricHandle): - def set(self, value: ValueType) -> None: - """Sets the current value of the handle to ``value``.""" - - -class MeasureHandle(MetricHandle): - def record(self, value: ValueType) -> None: - """Records the given ``value`` to this handle.""" diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 11116943de..c8c180db57 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -46,14 +46,6 @@ def test_measure(self): handle = measure.get_handle(("test",)) self.assertIsInstance(handle, metrics.MeasureHandle) - def test_remove_handle(self): - counter = metrics.Counter() - counter.remove_handle(("test",)) - - def test_clear(self): - counter = metrics.Counter() - counter.clear() - def test_counter_handle(self): handle = metrics.CounterHandle() handle.add(1) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 8ba35d63ee..4031420070 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -20,22 +20,78 @@ logger = logging.getLogger(__name__) +class BaseHandle: + def __init__( + self, value_type: metrics_api._ValueT, enabled: bool, monotonic: bool + ): + self.data = 0 + self.value_type = value_type + self.enabled = enabled + self.monotonic = monotonic + + def _validate_update(self, value: metrics_api._ValueT): + if not self.enabled: + return False + if self.monotonic and value < 0: + logger.warning("Monotonic metric cannot descend.") + return False + if not isinstance(value, self.value_type): + logger.warning( + "Invalid value passed for %s.", self.value_type.__name__ + ) + return False + return True + + +class CounterHandle(metrics_api.CounterHandle, BaseHandle): + def update(self, value: metrics_api._ValueT) -> None: + if self._validate_update(value): + self.data += value + + def add(self, value: metrics_api._ValueT) -> None: + """See `opentelemetry.metrics.CounterHandle._add`.""" + self.update(value) + + +class GaugeHandle(metrics_api.GaugeHandle, BaseHandle): + def update(self, value: metrics_api._ValueT) -> None: + if self._validate_update(value): + self.data = value + + def set(self, value: metrics_api._ValueT) -> None: + """See `opentelemetry.metrics.GaugeHandle._set`.""" + self.update(value) + + +class MeasureHandle(metrics_api.MeasureHandle, BaseHandle): + def update(self, value: metrics_api._ValueT) -> None: + if self._validate_update(value): + pass + # TODO: record + + def record(self, value: metrics_api._ValueT) -> None: + """See `opentelemetry.metrics.MeasureHandle._record`.""" + self.update(value) + + class Metric(metrics_api.Metric): """See `opentelemetry.metrics.Metric`.""" + HANDLE_TYPE = BaseHandle + def __init__( self, name: str, description: str, unit: str, - value_type: metrics_api.ValueType, + value_type: metrics_api._ValueT, label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): self.name = name self.description = description - self.unit = (unit,) + self.unit = unit self.value_type = value_type self.label_keys = label_keys self.enabled = enabled @@ -44,17 +100,13 @@ def __init__( def get_handle( self, label_values: Tuple[str, ...] - ) -> metrics_api.MetricHandle: + ) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" - pass # pylint: disable=unnecessary-pass - - def remove_handle(self, label_values: Tuple[str, ...]) -> None: - """See `opentelemetry.metrics.Metric.remove_handle`.""" - self.handles.pop(label_values, None) - - def clear(self) -> None: - """See `opentelemetry.metrics.Metric.clear`.""" - self.handles.clear() + handle = self.handles.get(label_values) + if not handle: + handle = self.__class__.HANDLE_TYPE(self.value_type, self.enabled, self.monotonic) + self.handles[label_values] = handle + return handle class Counter(Metric): @@ -65,17 +117,19 @@ class Counter(Metric): have a monotonic option set to False allows negative inputs. """ + HANDLE_TYPE = CounterHandle + def __init__( self, name: str, description: str, unit: str, - value_type: metrics_api.ValueType, + value_type: metrics_api._ValueT, label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = True, ): - super(Counter, self).__init__( + super().__init__( name, description, unit, @@ -85,17 +139,6 @@ def __init__( monotonic=monotonic, ) - def get_handle( - self, label_values: Tuple[str, ...] - ) -> metrics_api.CounterHandle: - """See `opentelemetry.metrics.FloatCounter.get_handle`.""" - handle = self.handles.get( - label_values, - CounterHandle(self.value_type, self.enabled, self.monotonic), - ) - self.handles[label_values] = handle - return handle - class Gauge(Metric): """See `opentelemetry.metrics.Gauge`. @@ -104,17 +147,19 @@ class Gauge(Metric): Negative inputs will be discarded for monotonic gauge metrics. """ + HANDLE_TYPE = GaugeHandle + def __init__( self, name: str, description: str, unit: str, - value_type: metrics_api.ValueType, + value_type: metrics_api._ValueT, label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): - super(Gauge, self).__init__( + super().__init__( name, description, unit, @@ -124,17 +169,6 @@ def __init__( monotonic=monotonic, ) - def get_handle( - self, label_values: Tuple[str, ...] - ) -> metrics_api.GaugeHandle: - """See `opentelemetry.metrics.Gauge.get_handle`.""" - handle = self.handles.get( - label_values, - GaugeHandle(self.value_type, self.enabled, self.monotonic), - ) - self.handles[label_values] = handle - return handle - class Measure(Metric): """See `opentelemetry.metrics.Measure`. @@ -143,17 +177,19 @@ class Measure(Metric): Negative inputs will be discarded when monotonic is True. """ + HANDLE_TYPE = MeasureHandle + def __init__( self, name: str, description: str, unit: str, - value_type: metrics_api.ValueType, + value_type: metrics_api._ValueT, label_keys: Tuple[str, ...] = None, enabled: bool = False, monotonic: bool = False, ): - super(Measure, self).__init__( + super().__init__( name, description, unit, @@ -163,111 +199,6 @@ def __init__( monotonic=monotonic, ) - def get_handle( - self, label_values: Tuple[str, ...] - ) -> metrics_api.MeasureHandle: - """See `opentelemetry.metrics.Measure.get_handle`.""" - handle = self.handles.get( - label_values, - MeasureHandle(self.value_type, self.enabled, self.monotonic), - ) - self.handles[label_values] = handle - return handle - - -class CounterHandle(metrics_api.CounterHandle): - def __init__( - self, value_type: metrics_api.ValueType, enabled: bool, monotonic: bool - ): - self.data = 0 - self.value_type = value_type - self.enabled = enabled - self.monotonic = monotonic - - def update(self, value: metrics_api.ValueType) -> None: - if not self.enabled: - return - if not self.monotonic and value < 0: - logger.warning("Monotonic counter cannot descend.") - return - if not isinstance(value, self.value_type): - logger.warning( - "Invalid value passed for %s.", self.value_type.__name__ - ) - return - self.data += value - - def add(self, value: metrics_api.ValueType) -> None: - """See `opentelemetry.metrics.CounterHandle._add`.""" - self.update(value) - - -class GaugeHandle(metrics_api.GaugeHandle): - def __init__( - self, - value_type: Union[Type[float], Type[int]], - enabled: bool, - monotonic: bool, - ): - self.data = 0 - self.value_type = value_type - self.enabled = enabled - self.monotonic = monotonic - - def update(self, value: metrics_api.ValueType) -> None: - if not self.enabled: - return - if self.monotonic and value < 0: - logger.warning("Monotonic gauge cannot descend.") - return - if not isinstance(value, self.value_type): - logger.warning( - "Invalid value passed for %s", self.value_type.__name__ - ) - return - self.data = value - - def set(self, value: metrics_api.ValueType) -> None: - """See `opentelemetry.metrics.GaugeHandle._set`.""" - self.update(value) - - -class MeasureHandle(metrics_api.MeasureHandle): - def __init__( - self, - value_type: Union[Type[float], Type[int]], - enabled: bool, - monotonic: bool, - ): - self.data = 0 - self.value_type = value_type - self.enabled = enabled - self.monotonic = monotonic - - def update(self, value: metrics_api.ValueType) -> None: - if not self.enabled: - return - if self.monotonic and value < 0: - logger.warning("Monotonic measure cannot descend.") - return - if not isinstance(value, self.value_type): - logger.warning( - "Invalid value passed for %s", self.value_type.__name__ - ) - return - # TODO: record - - def record(self, value: metrics_api.ValueType) -> None: - """See `opentelemetry.metrics.MeasureHandle._record`.""" - self.update(value) - - -METRIC_KIND_MAP = { - metrics_api.MetricKind.COUNTER: Counter, - metrics_api.MetricKind.GAUGE: Gauge, - metrics_api.MetricKind.MEASURE: Measure, -} - class Meter(metrics_api.Meter): """See `opentelemetry.metrics.Meter`.""" @@ -275,7 +206,7 @@ class Meter(metrics_api.Meter): def record_batch( self, label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueType]], + record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api._ValueT]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" for metric, value in record_tuples: @@ -286,14 +217,14 @@ def create_metric( name: str, description: str, unit: str, - value_type: metrics_api.ValueType, - metric_kind: metrics_api.MetricKind, + value_type: metrics_api._ValueT, + metric_type: metrics_api._MetricT, label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": """See `opentelemetry.metrics.Meter.create_metric`.""" - return METRIC_KIND_MAP[metric_kind]( + return metric_type( name, description, unit, diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index d29b1f2ad6..4cfab9aaa0 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -61,7 +61,7 @@ def test_record_batch_exists(self): def test_create_metric(self): meter = metrics.Meter() counter = meter.create_metric( - "name", "desc", "unit", int, metrics_api.MetricKind.COUNTER, () + "name", "desc", "unit", int, metrics.Counter, () ) self.assertTrue(isinstance(counter, metrics.Counter)) self.assertEqual(counter.value_type, int) @@ -70,7 +70,7 @@ def test_create_metric(self): def test_create_gauge(self): meter = metrics.Meter() gauge = meter.create_metric( - "name", "desc", "unit", float, metrics_api.MetricKind.GAUGE, () + "name", "desc", "unit", float, metrics.Gauge, () ) self.assertTrue(isinstance(gauge, metrics.Gauge)) self.assertEqual(gauge.value_type, float) @@ -79,7 +79,7 @@ def test_create_gauge(self): def test_create_measure(self): meter = metrics.Meter() measure = meter.create_metric( - "name", "desc", "unit", float, metrics_api.MetricKind.MEASURE, () + "name", "desc", "unit", float, metrics.Measure, () ) self.assertTrue(isinstance(measure, metrics.Measure)) self.assertEqual(measure.value_type, float) @@ -95,26 +95,6 @@ def test_get_handle(self): handle = metric.get_handle(label_values) self.assertEqual(metric.handles.get(label_values), handle) - def test_remove_handle(self): - metric = metrics.Counter("name", "desc", "unit", int, ("key")) - label_values1 = ("value",) - label_values2 = ("value2",) - metric.get_handle(label_values1) - metric.get_handle(label_values2) - metric.remove_handle(label_values1) - self.assertIsNone(metric.handles.get(label_values1)) - self.assertIsNotNone(metric.handles.get(label_values2)) - - def test_clear(self): - metric = metrics.Counter("name", "desc", "unit", int, ("key")) - label_values1 = ("value",) - label_values2 = ("value2",) - metric.get_handle(label_values1) - metric.get_handle(label_values2) - metric.clear() - self.assertIsNone(metric.handles.get(label_values1)) - self.assertIsNone(metric.handles.get(label_values2)) - class TestCounterHandle(unittest.TestCase): def test_update(self): @@ -134,7 +114,7 @@ def test_add_disabled(self): @mock.patch("opentelemetry.sdk.metrics.logger") def test_add_monotonic(self, logger_mock): - handle = metrics.CounterHandle(int, True, False) + handle = metrics.CounterHandle(int, True, True) handle.add(-3) self.assertEqual(handle.data, 0) self.assertTrue(logger_mock.warning.called) From cedcd1ef091362490a0735dcf1b14ca63a0a5461 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 14:23:41 -0700 Subject: [PATCH 75/88] fix type --- .../src/opentelemetry/metrics/__init__.py | 9 ++++--- .../tests/metrics/test_metrics.py | 3 ++- .../src/opentelemetry/sdk/metrics/__init__.py | 25 +++++++++++-------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 20278d7edb..891d7a60d3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -43,17 +43,17 @@ class DefaultMetricHandle: class CounterHandle: - def add(self, value: Type[_ValueT]) -> None: + def add(self, value: _ValueT) -> None: """Increases the value of the handle by ``value``""" class GaugeHandle: - def set(self, value: Type[_ValueT]) -> None: + def set(self, value: _ValueT) -> None: """Sets the current value of the handle to ``value``.""" class MeasureHandle: - def record(self, value: Type[_ValueT]) -> None: + def record(self, value: _ValueT) -> None: """Records the given ``value`` to this handle.""" @@ -123,6 +123,7 @@ def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" return MeasureHandle() + _MetricT = TypeVar("_MetricT", Counter, Gauge, Measure) @@ -138,7 +139,7 @@ class Meter: def record_batch( self, label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple["Metric", Type[_ValueT]]], + record_tuples: Tuple[Tuple["Metric", _ValueT]], ) -> None: """Atomically records a batch of `Metric` and value pairs. diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index c8c180db57..7b7caa0dd5 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -23,7 +23,8 @@ def setUp(self): self.meter = metrics.Meter() def test_record_batch(self): - self.meter.record_batch((), ()) + counter = metrics.Counter() + self.meter.record_batch(("values"), ((counter, 1))) def test_create_metric(self): metric = self.meter.create_metric("", "", "", float, metrics.Counter) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 4031420070..a205d37e9b 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -22,7 +22,10 @@ class BaseHandle: def __init__( - self, value_type: metrics_api._ValueT, enabled: bool, monotonic: bool + self, + value_type: Type[metrics_api._ValueT], + enabled: bool, + monotonic: bool, ): self.data = 0 self.value_type = value_type @@ -84,7 +87,7 @@ def __init__( name: str, description: str, unit: str, - value_type: metrics_api._ValueT, + value_type: Type[metrics_api._ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, @@ -98,13 +101,13 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle( - self, label_values: Tuple[str, ...] - ) -> BaseHandle: + def get_handle(self, label_values: Tuple[str, ...]) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: - handle = self.__class__.HANDLE_TYPE(self.value_type, self.enabled, self.monotonic) + handle = self.__class__.HANDLE_TYPE( + self.value_type, self.enabled, self.monotonic + ) self.handles[label_values] = handle return handle @@ -124,7 +127,7 @@ def __init__( name: str, description: str, unit: str, - value_type: metrics_api._ValueT, + value_type: Type[metrics_api._ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = True, @@ -154,7 +157,7 @@ def __init__( name: str, description: str, unit: str, - value_type: metrics_api._ValueT, + value_type: Type[metrics_api._ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, @@ -184,7 +187,7 @@ def __init__( name: str, description: str, unit: str, - value_type: metrics_api._ValueT, + value_type: Type[metrics_api._ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = False, monotonic: bool = False, @@ -217,8 +220,8 @@ def create_metric( name: str, description: str, unit: str, - value_type: metrics_api._ValueT, - metric_type: metrics_api._MetricT, + value_type: Type[metrics_api._ValueT], + metric_type: Type[metrics_api._MetricT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, From 77854c9c839e4c5febf13415e9d069ba135d30f7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 14:41:03 -0700 Subject: [PATCH 76/88] fix lint --- .../src/opentelemetry/metrics/__init__.py | 16 +++++----- .../src/opentelemetry/sdk/metrics/__init__.py | 30 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 891d7a60d3..37b4499743 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -32,7 +32,7 @@ from opentelemetry.util import loader -_ValueT = TypeVar("_ValueT", int, float) +ValueT = TypeVar("ValueT", int, float) class DefaultMetricHandle: @@ -43,17 +43,17 @@ class DefaultMetricHandle: class CounterHandle: - def add(self, value: _ValueT) -> None: + def add(self, value: ValueT) -> None: """Increases the value of the handle by ``value``""" class GaugeHandle: - def set(self, value: _ValueT) -> None: + def set(self, value: ValueT) -> None: """Sets the current value of the handle to ``value``.""" class MeasureHandle: - def record(self, value: _ValueT) -> None: + def record(self, value: ValueT) -> None: """Records the given ``value`` to this handle.""" @@ -124,7 +124,7 @@ def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": return MeasureHandle() -_MetricT = TypeVar("_MetricT", Counter, Gauge, Measure) +MetricT = TypeVar("MetricT", Counter, Gauge, Measure) # pylint: disable=unused-argument @@ -139,7 +139,7 @@ class Meter: def record_batch( self, label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple["Metric", _ValueT]], + record_tuples: Tuple[Tuple["Metric", ValueT]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -159,8 +159,8 @@ def create_metric( name: str, description: str, unit: str, - value_type: Type[_ValueT], - metric_type: Type[_MetricT], + value_type: Type[ValueT], + metric_type: Type[MetricT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index a205d37e9b..8c42ff9e49 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -23,7 +23,7 @@ class BaseHandle: def __init__( self, - value_type: Type[metrics_api._ValueT], + value_type: Type[metrics_api.ValueT], enabled: bool, monotonic: bool, ): @@ -32,7 +32,7 @@ def __init__( self.enabled = enabled self.monotonic = monotonic - def _validate_update(self, value: metrics_api._ValueT): + def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: return False if self.monotonic and value < 0: @@ -47,32 +47,32 @@ def _validate_update(self, value: metrics_api._ValueT): class CounterHandle(metrics_api.CounterHandle, BaseHandle): - def update(self, value: metrics_api._ValueT) -> None: + def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): self.data += value - def add(self, value: metrics_api._ValueT) -> None: + def add(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.CounterHandle._add`.""" self.update(value) class GaugeHandle(metrics_api.GaugeHandle, BaseHandle): - def update(self, value: metrics_api._ValueT) -> None: + def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): self.data = value - def set(self, value: metrics_api._ValueT) -> None: + def set(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.GaugeHandle._set`.""" self.update(value) class MeasureHandle(metrics_api.MeasureHandle, BaseHandle): - def update(self, value: metrics_api._ValueT) -> None: + def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): pass # TODO: record - def record(self, value: metrics_api._ValueT) -> None: + def record(self, value: metrics_api.ValueT) -> None: """See `opentelemetry.metrics.MeasureHandle._record`.""" self.update(value) @@ -87,7 +87,7 @@ def __init__( name: str, description: str, unit: str, - value_type: Type[metrics_api._ValueT], + value_type: Type[metrics_api.ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, @@ -127,7 +127,7 @@ def __init__( name: str, description: str, unit: str, - value_type: Type[metrics_api._ValueT], + value_type: Type[metrics_api.ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = True, @@ -157,7 +157,7 @@ def __init__( name: str, description: str, unit: str, - value_type: Type[metrics_api._ValueT], + value_type: Type[metrics_api.ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, @@ -187,7 +187,7 @@ def __init__( name: str, description: str, unit: str, - value_type: Type[metrics_api._ValueT], + value_type: Type[metrics_api.ValueT], label_keys: Tuple[str, ...] = None, enabled: bool = False, monotonic: bool = False, @@ -209,7 +209,7 @@ class Meter(metrics_api.Meter): def record_batch( self, label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api._ValueT]], + record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueT]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" for metric, value in record_tuples: @@ -220,8 +220,8 @@ def create_metric( name: str, description: str, unit: str, - value_type: Type[metrics_api._ValueT], - metric_type: Type[metrics_api._MetricT], + value_type: Type[metrics_api.ValueT], + metric_type: Type[metrics_api.MetricT], label_keys: Tuple[str, ...] = None, enabled: bool = True, monotonic: bool = False, From 605c9474b2f6feb97dd511d1603d7a123548936b Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 14:46:50 -0700 Subject: [PATCH 77/88] remove imports --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 1 - opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 37b4499743..e30303921f 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -26,7 +26,6 @@ """ -import enum from abc import ABC, abstractmethod from typing import Callable, Optional, Tuple, Type, TypeVar diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 8c42ff9e49..24149369c1 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. import logging -from typing import Tuple, Type, Union +from typing import Tuple, Type from opentelemetry import metrics as metrics_api From 031bd1f1185d7c3185e2dafa374c614d35ecd516 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 14:53:28 -0700 Subject: [PATCH 78/88] default tests --- opentelemetry-api/tests/metrics/test_metrics.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 7b7caa0dd5..34dc5f79ee 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -32,21 +32,29 @@ def test_create_metric(self): class TestMetrics(unittest.TestCase): + def test_default(self): + default = metrics.DefaultMetric() + handle = default.get_handle(("test", "test1")) + self.assertIsInstance(handle, metrics.DefaultMetricHandle) + def test_counter(self): counter = metrics.Counter() - handle = counter.get_handle(("test",)) + handle = counter.get_handle(("test", "test1")) self.assertIsInstance(handle, metrics.CounterHandle) def test_gauge(self): gauge = metrics.Gauge() - handle = gauge.get_handle(("test",)) + handle = gauge.get_handle(("test", "test1")) self.assertIsInstance(handle, metrics.GaugeHandle) def test_measure(self): measure = metrics.Measure() - handle = measure.get_handle(("test",)) + handle = measure.get_handle(("test", "test1")) self.assertIsInstance(handle, metrics.MeasureHandle) + def test_default_handle(self): + handle = metrics.DefaultMetricHandle() + def test_counter_handle(self): handle = metrics.CounterHandle() handle.add(1) From 071b25be2e4ac2fc0e7a728ebb2aaf95a483fc62 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 15:04:47 -0700 Subject: [PATCH 79/88] fix lint --- opentelemetry-api/tests/metrics/test_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/tests/metrics/test_metrics.py b/opentelemetry-api/tests/metrics/test_metrics.py index 34dc5f79ee..14667f62ea 100644 --- a/opentelemetry-api/tests/metrics/test_metrics.py +++ b/opentelemetry-api/tests/metrics/test_metrics.py @@ -53,7 +53,7 @@ def test_measure(self): self.assertIsInstance(handle, metrics.MeasureHandle) def test_default_handle(self): - handle = metrics.DefaultMetricHandle() + metrics.DefaultMetricHandle() def test_counter_handle(self): handle = metrics.CounterHandle() From 00eaea3160b0cafc73b21ffb1bd8b6f57b87f0b5 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 17:10:14 -0700 Subject: [PATCH 80/88] usse sequence --- .../src/opentelemetry/metrics/__init__.py | 24 +++++++++---------- .../src/opentelemetry/sdk/metrics/__init__.py | 18 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index e30303921f..21bf54934c 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -27,7 +27,7 @@ """ from abc import ABC, abstractmethod -from typing import Callable, Optional, Tuple, Type, TypeVar +from typing import Callable, Optional, Sequence, Tuple, Type, TypeVar from opentelemetry.util import loader @@ -64,7 +64,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: Tuple[str, ...]) -> "object": + def get_handle(self, label_values: Sequence[str, ...]) -> "object": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -83,7 +83,7 @@ class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" def get_handle( - self, label_values: Tuple[str, ...] + self, label_values: Sequence[str, ...] ) -> "DefaultMetricHandle": return DefaultMetricHandle() @@ -91,7 +91,7 @@ def get_handle( class Counter(Metric): """A counter type metric that expresses the computation of a sum.""" - def get_handle(self, label_values: Tuple[str, ...]) -> "CounterHandle": + def get_handle(self, label_values: Sequence[str, ...]) -> "CounterHandle": """Gets a `CounterHandle`.""" return CounterHandle() @@ -105,7 +105,7 @@ class Gauge(Metric): the measurement interval is arbitrary. """ - def get_handle(self, label_values: Tuple[str, ...]) -> "GaugeHandle": + def get_handle(self, label_values: Sequence[str, ...]) -> "GaugeHandle": """Gets a `GaugeHandle`.""" return GaugeHandle() @@ -118,7 +118,7 @@ class Measure(Metric): Negative inputs will be discarded when monotonic is True. """ - def get_handle(self, label_values: Tuple[str, ...]) -> "MeasureHandle": + def get_handle(self, label_values: Sequence[str, ...]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" return MeasureHandle() @@ -137,8 +137,8 @@ class Meter: def record_batch( self, - label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple["Metric", ValueT]], + label_values: Sequence[str, ...], + record_tuples: Sequence[Tuple["Metric", ValueT]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -149,7 +149,7 @@ def record_batch( Args: label_values: The values that will be matched against to record for the handles under each metric that has those labels. - record_tuples: A tuple of pairs of `Metric` s and the + record_tuples: A sequence of pairs of `Metric` s and the corresponding value to record for that metric. """ @@ -160,7 +160,7 @@ def create_metric( unit: str, value_type: Type[ValueT], metric_type: Type[MetricT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": @@ -173,8 +173,8 @@ def create_metric( value_type: The type of values being recorded by the metric. metric_type: The type of metric being created. label_keys: The keys for the labels with dynamic values. - Order of the tuple is important as the same order must be used - on recording when suppling values for these labels. + Order of the sequence is important as the same order must be + used on recording when suppling values for these labels. enabled: Whether to report the metric by default. monotonic: Whether to only allow non-negative values. diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 24149369c1..49ff702913 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. import logging -from typing import Tuple, Type +from typing import Sequence, Tuple, Type from opentelemetry import metrics as metrics_api @@ -88,7 +88,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): @@ -101,7 +101,7 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Tuple[str, ...]) -> BaseHandle: + def get_handle(self, label_values: Sequence[str, ...]) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: @@ -128,7 +128,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = True, monotonic: bool = True, ): @@ -158,7 +158,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = True, monotonic: bool = False, ): @@ -188,7 +188,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = False, monotonic: bool = False, ): @@ -208,8 +208,8 @@ class Meter(metrics_api.Meter): def record_batch( self, - label_values: Tuple[str, ...], - record_tuples: Tuple[Tuple[metrics_api.Metric, metrics_api.ValueT]], + label_values: Sequence[str, ...], + record_tuples: Sequence[Tuple[metrics_api.Metric, metrics_api.ValueT]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" for metric, value in record_tuples: @@ -222,7 +222,7 @@ def create_metric( unit: str, value_type: Type[metrics_api.ValueT], metric_type: Type[metrics_api.MetricT], - label_keys: Tuple[str, ...] = None, + label_keys: Sequence[str, ...] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": From 554b7b1594779ae215099864647d47c31502319e Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 17:15:43 -0700 Subject: [PATCH 81/88] remove ellipses --- .../src/opentelemetry/metrics/__init__.py | 8 ++++---- .../src/opentelemetry/sdk/metrics/__init__.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 21bf54934c..de66e487e3 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -64,7 +64,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: Sequence[str, ...]) -> "object": + def get_handle(self, label_values: Sequence[str] -> "object": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -91,7 +91,7 @@ def get_handle( class Counter(Metric): """A counter type metric that expresses the computation of a sum.""" - def get_handle(self, label_values: Sequence[str, ...]) -> "CounterHandle": + def get_handle(self, label_values: Sequence[str]) -> "CounterHandle": """Gets a `CounterHandle`.""" return CounterHandle() @@ -105,7 +105,7 @@ class Gauge(Metric): the measurement interval is arbitrary. """ - def get_handle(self, label_values: Sequence[str, ...]) -> "GaugeHandle": + def get_handle(self, label_values: Sequence[str]) -> "GaugeHandle": """Gets a `GaugeHandle`.""" return GaugeHandle() @@ -118,7 +118,7 @@ class Measure(Metric): Negative inputs will be discarded when monotonic is True. """ - def get_handle(self, label_values: Sequence[str, ...]) -> "MeasureHandle": + def get_handle(self, label_values: Sequence[str]) -> "MeasureHandle": """Gets a `MeasureHandle` with a float value.""" return MeasureHandle() diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index 49ff702913..a6a08bf1a6 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -88,7 +88,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, ): @@ -128,7 +128,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = True, ): @@ -158,7 +158,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, ): @@ -188,7 +188,7 @@ def __init__( description: str, unit: str, value_type: Type[metrics_api.ValueT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = False, monotonic: bool = False, ): @@ -208,7 +208,7 @@ class Meter(metrics_api.Meter): def record_batch( self, - label_values: Sequence[str, ...], + label_values: Sequence[str], record_tuples: Sequence[Tuple[metrics_api.Metric, metrics_api.ValueT]], ) -> None: """See `opentelemetry.metrics.Meter.record_batch`.""" @@ -222,7 +222,7 @@ def create_metric( unit: str, value_type: Type[metrics_api.ValueT], metric_type: Type[metrics_api.MetricT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": From 7668de45d34dfb388a7cbbe67600efdda4b0eb86 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 17:21:04 -0700 Subject: [PATCH 82/88] remove ellipses --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 8 ++++---- .../src/opentelemetry/sdk/metrics/__init__.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index de66e487e3..c991a1c320 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -64,7 +64,7 @@ class Metric(ABC): """ @abstractmethod - def get_handle(self, label_values: Sequence[str] -> "object": + def get_handle(self, label_values: Sequence[str]) -> "object": """Gets a handle, used for repeated-use of metrics instruments. Handles are useful to reduce the cost of repeatedly recording a metric @@ -83,7 +83,7 @@ class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" def get_handle( - self, label_values: Sequence[str, ...] + self, label_values: Sequence[str] ) -> "DefaultMetricHandle": return DefaultMetricHandle() @@ -137,7 +137,7 @@ class Meter: def record_batch( self, - label_values: Sequence[str, ...], + label_values: Sequence[str], record_tuples: Sequence[Tuple["Metric", ValueT]], ) -> None: """Atomically records a batch of `Metric` and value pairs. @@ -160,7 +160,7 @@ def create_metric( unit: str, value_type: Type[ValueT], metric_type: Type[MetricT], - label_keys: Sequence[str, ...] = None, + label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, ) -> "Metric": diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index a6a08bf1a6..a2062735d8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -101,7 +101,7 @@ def __init__( self.monotonic = monotonic self.handles = {} - def get_handle(self, label_values: Sequence[str, ...]) -> BaseHandle: + def get_handle(self, label_values: Sequence[str]) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: From 61bef299d6eeb2a0a4b6472eaa5fb8ee68666508 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 17:25:57 -0700 Subject: [PATCH 83/88] black --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index c991a1c320..bfb7ae95b5 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -82,9 +82,7 @@ def get_handle(self, label_values: Sequence[str]) -> "object": class DefaultMetric(Metric): """The default Metric used when no Metric implementation is available.""" - def get_handle( - self, label_values: Sequence[str] - ) -> "DefaultMetricHandle": + def get_handle(self, label_values: Sequence[str]) -> "DefaultMetricHandle": return DefaultMetricHandle() From 6564a3a8b918806b69a973fa9381c6cbb4ae32a1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 19:33:17 -0700 Subject: [PATCH 84/88] Fix typo --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index bfb7ae95b5..61cc8bdfac 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -165,7 +165,7 @@ def create_metric( """Creates a ``metric_kind`` metric with type ``value_type``. Args: - name: The name of the counter. + name: The name of the metric. description: Human-readable description of the metric. unit: Unit of the metric values. value_type: The type of values being recorded by the metric. From 1083d7eeeb6c4e657a63d712f0facab7cc6a9d89 Mon Sep 17 00:00:00 2001 From: Leighton Date: Thu, 26 Sep 2019 19:48:33 -0700 Subject: [PATCH 85/88] fix example --- .../src/opentelemetry_example_app/metrics_example.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py b/examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py index 1c9db3def7..dd9509feb2 100644 --- a/examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py +++ b/examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py @@ -17,7 +17,7 @@ """ from opentelemetry import metrics -from opentelemetry.sdk.metrics import Meter +from opentelemetry.sdk.metrics import Counter, Meter metrics.set_preferred_meter_implementation(lambda _: Meter()) meter = metrics.meter() @@ -26,17 +26,14 @@ "available memory", "bytes", int, - metrics.MetricKind.COUNTER, - ("environment"), + Counter, + ("environment",), ) -label_values = "staging" - +label_values = ("staging",) counter_handle = counter.get_handle(label_values) - counter_handle.add(100) meter.record_batch(label_values, [(counter, 50)]) - print(counter_handle.data) # TODO: exporters From 248e53e254b0023f423ce20ce26e2fe13adc13ad Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 08:33:17 -0700 Subject: [PATCH 86/88] fix type --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 61cc8bdfac..9ef6e7f80e 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -161,7 +161,7 @@ def create_metric( label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> "Metric": + ) -> MetricT: """Creates a ``metric_kind`` metric with type ``value_type``. Args: diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index a2062735d8..e2ca8fe86f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -225,7 +225,7 @@ def create_metric( label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> "Metric": + ) -> metrics_api.MetricT: """See `opentelemetry.metrics.Meter.create_metric`.""" return metric_type( name, From 56df6ac15946139a336690de40f0862e63ab7dfb Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 08:39:39 -0700 Subject: [PATCH 87/88] fix type --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 9ef6e7f80e..61cc8bdfac 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -161,7 +161,7 @@ def create_metric( label_keys: Sequence[str] = None, enabled: bool = True, monotonic: bool = False, - ) -> MetricT: + ) -> "Metric": """Creates a ``metric_kind`` metric with type ``value_type``. Args: From 2130ce81c6e8dafbffc5df732ebae69ce8313b6a Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 27 Sep 2019 14:48:08 -0700 Subject: [PATCH 88/88] address comments --- .../src/opentelemetry/sdk/metrics/__init__.py | 15 ++++++++++----- opentelemetry-sdk/tests/metrics/test_metrics.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py index e2ca8fe86f..f80a72c770 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py @@ -35,9 +35,6 @@ def __init__( def _validate_update(self, value: metrics_api.ValueT): if not self.enabled: return False - if self.monotonic and value < 0: - logger.warning("Monotonic metric cannot descend.") - return False if not isinstance(value, self.value_type): logger.warning( "Invalid value passed for %s.", self.value_type.__name__ @@ -49,6 +46,9 @@ def _validate_update(self, value: metrics_api.ValueT): class CounterHandle(metrics_api.CounterHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): + if self.monotonic and value < 0: + logger.warning("Monotonic counter cannot descend.") + return self.data += value def add(self, value: metrics_api.ValueT) -> None: @@ -59,6 +59,9 @@ def add(self, value: metrics_api.ValueT) -> None: class GaugeHandle(metrics_api.GaugeHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): + if self.monotonic and value < self.data: + logger.warning("Monotonic gauge cannot descend.") + return self.data = value def set(self, value: metrics_api.ValueT) -> None: @@ -69,7 +72,9 @@ def set(self, value: metrics_api.ValueT) -> None: class MeasureHandle(metrics_api.MeasureHandle, BaseHandle): def update(self, value: metrics_api.ValueT) -> None: if self._validate_update(value): - pass + if self.monotonic and value < 0: + logger.warning("Monotonic measure cannot accept negatives.") + return # TODO: record def record(self, value: metrics_api.ValueT) -> None: @@ -105,7 +110,7 @@ def get_handle(self, label_values: Sequence[str]) -> BaseHandle: """See `opentelemetry.metrics.Metric.get_handle`.""" handle = self.handles.get(label_values) if not handle: - handle = self.__class__.HANDLE_TYPE( + handle = self.HANDLE_TYPE( self.value_type, self.enabled, self.monotonic ) self.handles[label_values] = handle diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py index 4cfab9aaa0..dc4151c4ee 100644 --- a/opentelemetry-sdk/tests/metrics/test_metrics.py +++ b/opentelemetry-sdk/tests/metrics/test_metrics.py @@ -90,7 +90,7 @@ class TestMetric(unittest.TestCase): def test_get_handle(self): metric_types = [metrics.Counter, metrics.Gauge, metrics.Measure] for _type in metric_types: - metric = _type("name", "desc", "unit", int, ("key")) + metric = _type("name", "desc", "unit", int, ("key",)) label_values = ("value",) handle = metric.get_handle(label_values) self.assertEqual(metric.handles.get(label_values), handle)