From a8d5e165c0b1c97d217a20ad6cb4025988eea9b8 Mon Sep 17 00:00:00 2001 From: "allen.k1m" Date: Wed, 3 Jan 2024 23:21:04 +0900 Subject: [PATCH] include feedback --- .../README.rst | 26 +- .../instrumentation/asyncio/__init__.py | 261 ++++-------------- .../instrumentation/asyncio/metrics.py | 62 ----- .../tests/test_asyncio_cancellation.py | 36 +-- .../tests/test_asyncio_ensure_future.py | 42 +-- 5 files changed, 78 insertions(+), 349 deletions(-) delete mode 100644 instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/metrics.py diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/README.rst b/instrumentation/opentelemetry-instrumentation-asyncio/README.rst index 1a94a0c223..c11e343ac3 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/README.rst +++ b/instrumentation/opentelemetry-instrumentation-asyncio/README.rst @@ -9,7 +9,7 @@ OpenTelemetry asyncio Instrumentation AsyncioInstrumentor: Tracing Requests Made by the Asyncio Library -The opentelemetry-instrumentation-asycnio package allows tracing asyncio applications. +The opentelemetry-instrumentation-asyncio package allows tracing asyncio applications. The metric for coroutine, future, is generated even if there is no setting to generate a span. @@ -85,28 +85,8 @@ Run instrumented application asyncio metric types ---------------------- -* `asyncio.futures.duration` (ms) - Duration of the future -* `asyncio.futures.exceptions` (count) - Number of exceptions raised by the future -* `asyncio.futures.cancelled` (count) - Number of futures cancelled -* `asyncio.futures.created` (count) - Number of futures created -* `asyncio.futures.active` (count) - Number of futures active -* `asyncio.futures.finished` (count) - Number of futures finished -* `asyncio.futures.timeouts` (count) - Number of futures timed out - -* `asyncio.coroutine.duration` (ms) - Duration of the coroutine -* `asyncio.coroutine.exceptions` (count) - Number of exceptions raised by the coroutine -* `asyncio.coroutine.created` (count) - Number of coroutines created -* `asyncio.coroutine.active` (count) - Number of coroutines active -* `asyncio.coroutine.finished` (count) - Number of coroutines finished -* `asyncio.coroutine.timeouts` (count) - Number of coroutines timed out -* `asyncio.coroutine.cancelled` (count) - Number of coroutines cancelled - -* `asyncio.to_thread.duration` (ms) - Duration of the to_thread -* `asyncio.to_thread.exceptions` (count) - Number of exceptions raised by the to_thread -* `asyncio.to_thread.created` (count) - Number of to_thread created -* `asyncio.to_thread.active` (count) - Number of to_thread active -* `asyncio.to_thread.finished` (count) - Number of to_thread finished - +* `asyncio.process.duration` (seconds) - Duration of asyncio process +* `asyncio.process.count` (count) - Number of asyncio process API diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py index e34133ffbc..bee967b770 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/__init__.py @@ -69,27 +69,8 @@ def func(): asyncio metric types --------------------- -* asyncio.futures.duration (ms) - Duration of the future -* asyncio.futures.exceptions (count) - Number of exceptions raised by the future -* asyncio.futures.cancelled (count) - Number of futures cancelled -* asyncio.futures.created (count) - Number of futures created -* asyncio.futures.active (count) - Number of futures active -* asyncio.futures.finished (count) - Number of futures finished -* asyncio.futures.timeouts (count) - Number of futures timed out - -* asyncio.coroutine.duration (ms) - Duration of the coroutine -* asyncio.coroutine.exceptions (count) - Number of exceptions raised by the coroutine -* asyncio.coroutine.created (count) - Number of coroutines created -* asyncio.coroutine.active (count) - Number of coroutines active -* asyncio.coroutine.finished (count) - Number of coroutines finished -* asyncio.coroutine.timeouts (count) - Number of coroutines timed out -* asyncio.coroutine.cancelled (count) - Number of coroutines cancelled - -* asyncio.to_thread.duration (ms) - Duration of the to_thread -* asyncio.to_thread.exceptions (count) - Number of exceptions raised by the to_thread -* asyncio.to_thread.created (count) - Number of to_thread created -* asyncio.to_thread.active (count) - Number of to_thread active -* asyncio.to_thread.finished (count) - Number of to_thread finished +* asyncio.process.duration (seconds) - Duration of asyncio process +* asyncio.process.count (count) - Number of asyncio process API @@ -103,30 +84,6 @@ def func(): from wrapt import wrap_function_wrapper as _wrap -# pylint: disable=no-name-in-module -from opentelemetry.instrumentation.asyncio.metrics import ( - ASYNCIO_COROUTINE_ACTIVE, - ASYNCIO_COROUTINE_CANCELLED, - ASYNCIO_COROUTINE_CREATED, - ASYNCIO_COROUTINE_DURATION, - ASYNCIO_COROUTINE_EXCEPTIONS, - ASYNCIO_COROUTINE_FINISHED, - ASYNCIO_COROUTINE_NAME, - ASYNCIO_COROUTINE_TIMEOUTS, - ASYNCIO_EXCEPTIONS_NAME, - ASYNCIO_FUTURES_ACTIVE, - ASYNCIO_FUTURES_CANCELLED, - ASYNCIO_FUTURES_CREATED, - ASYNCIO_FUTURES_DURATION, - ASYNCIO_FUTURES_EXCEPTIONS, - ASYNCIO_FUTURES_FINISHED, - ASYNCIO_FUTURES_TIMEOUTS, - ASYNCIO_TO_THREAD_ACTIVE, - ASYNCIO_TO_THREAD_CREATED, - ASYNCIO_TO_THREAD_DURATION, - ASYNCIO_TO_THREAD_EXCEPTIONS, - ASYNCIO_TO_THREAD_FINISHED, -) from opentelemetry.instrumentation.asyncio.package import _instruments from opentelemetry.instrumentation.asyncio.utils import ( get_coros_to_trace, @@ -161,27 +118,8 @@ class AsyncioInstrumentor(BaseInstrumentor): def __init__(self): super().__init__() - self.to_thread_duration_metric = None - self.to_thread_exception_metric = None - self.to_thread_active_metric = None - self.to_thread_created_metric = None - self.to_thread_finished_metric = None - - self.coro_duration_metric = None - self.coro_exception_metric = None - self.coro_cancelled_metric = None - self.coro_active_metric = None - self.coro_created_metric = None - self.coro_finished_metric = None - self.coro_timeout_metric = None - - self.future_duration_metric = None - self.future_exception_metric = None - self.future_cancelled_metric = None - self.future_active_metric = None - self.future_created_metric = None - self.future_finished_metric = None - self.future_timeout_metric = None + self.process_duration_metric = None + self.process_counts_metric = None self._tracer = None self._meter = None @@ -203,9 +141,16 @@ def _instrument(self, **kwargs): self._future_active_enabled = get_future_trace_enabled() self._to_thread_name_to_trace = get_to_thread_to_trace() - self.create_coro_metric() - self.create_future_metric() - self.create_to_thread_metric() + self.process_duration_metric = self._meter.create_histogram( + name="asyncio.process.duration", + description="Duration of asyncio process", + unit="seconds", + ) + self.process_counts_metric = self._meter.create_up_down_counter( + name="asyncio.process.count", + description="Number of asyncio process", + unit="1", + ) for method in self.methods_with_coroutine: self.instrument_method_with_coroutine(method) @@ -295,8 +240,6 @@ def wrap_taskgroup_create_task(method, instance, args, kwargs): def trace_to_thread(self, func): """Trace a function.""" start = default_timer() - self.to_thread_created_metric.add(1) - self.to_thread_active_metric.add(1) span = ( self._tracer.start_span( f"{ASYNCIO_PREFIX}to_thread_func-" + func.__name__ @@ -304,19 +247,19 @@ def trace_to_thread(self, func): if func.__name__ in self._to_thread_name_to_trace else None ) + attr = {"type": "to_thread", "name": func.__name__} + duration_attr = attr.copy() exception = None try: + attr["state"] = "finished" return func - except Exception as exc: - exception_attr = {ASYNCIO_EXCEPTIONS_NAME: exc.__class__.__name__} - exception = exc - self.to_thread_exception_metric.add(1, exception_attr) + except Exception: + attr["state"] = "exception" raise finally: duration = max(round((default_timer() - start) * 1000), 0) - self.to_thread_duration_metric.record(duration) - self.to_thread_finished_metric.add(1) - self.to_thread_active_metric.add(-1) + self.process_duration_metric.record(duration, duration_attr) + self.process_counts_metric.add(1, attr) if span: if span.is_recording() and exception: span.set_status(Status(StatusCode.ERROR)) @@ -336,42 +279,34 @@ def trace_item(self, coro_or_future): async def trace_coroutine(self, coro): start = default_timer() - coro_attr = { - ASYNCIO_COROUTINE_NAME: coro.__name__, + attr = { + "type": "coroutine", + "name": coro.__name__, } - self.coro_created_metric.add(1, coro_attr) - self.coro_active_metric.add(1, coro_attr) - + duration_attr = attr.copy() span = ( self._tracer.start_span(f"{ASYNCIO_PREFIX}coro-" + coro.__name__) if coro.__name__ in self._coros_name_to_trace else None ) - exception = None try: + attr["state"] = "finished" return await coro # CancelledError is raised when a coroutine is cancelled # before it has a chance to run. We don't want to record # this as an error. except asyncio.CancelledError: - self.coro_cancelled_metric.add(1, coro_attr) - except asyncio.TimeoutError: - self.coro_timeout_metric.add(1, coro_attr) - raise + attr["state"] = "cancelled" except Exception as exc: exception = exc - coro_exception_attr = coro_attr.copy() - coro_exception_attr[ - ASYNCIO_EXCEPTIONS_NAME - ] = exc.__class__.__name__ - self.coro_exception_metric.add(1, coro_exception_attr) + state = determine_state(exception) + attr["state"] = state raise finally: - duration = max(round((default_timer() - start) * 1000), 0) - self.coro_duration_metric.record(duration, coro_attr) - self.coro_finished_metric.add(1, coro_attr) - self.coro_active_metric.add(-1, coro_attr) + duration = max(round(default_timer() - start), 0) + self.process_duration_metric.record(duration, duration_attr) + self.process_counts_metric.add(1, attr) if span: if span.is_recording() and exception: @@ -381,8 +316,6 @@ async def trace_coroutine(self, coro): def trace_future(self, future): start = default_timer() - self.future_created_metric.add(1) - self.future_active_metric.add(1) span = ( self._tracer.start_span(f"{ASYNCIO_PREFIX}future") if self._future_active_enabled @@ -390,21 +323,16 @@ def trace_future(self, future): ) def callback(f): + duration = max(round(default_timer() - start), 0) exception = f.exception() - if isinstance(exception, asyncio.CancelledError): - self.future_cancelled_metric.add(1) - elif isinstance(exception, asyncio.TimeoutError): - self.future_timeout_metric.add(1) - elif exception: - exception_attr = { - ASYNCIO_EXCEPTIONS_NAME: exception.__class__.__name__ - } - self.future_exception_metric.add(1, exception_attr) - - duration = max(round((default_timer() - start) * 1000), 0) - self.future_duration_metric.record(duration) - self.future_finished_metric.add(1) - self.future_active_metric.add(-1) + attr = { + "type": "future", + } + duration_attr = attr.copy() + state = determine_state(exception) + attr["state"] = state + self.process_counts_metric.add(1, attr) + self.process_duration_metric.record(duration, duration_attr) if span: if span.is_recording() and exception: span.set_status(Status(StatusCode.ERROR)) @@ -414,106 +342,15 @@ def callback(f): future.add_done_callback(callback) return future - def create_coro_metric(self): - self.coro_duration_metric = self._meter.create_histogram( - name=ASYNCIO_COROUTINE_DURATION, - description="Duration of asyncio coroutine", - unit="ms", - ) - self.coro_exception_metric = self._meter.create_counter( - name=ASYNCIO_COROUTINE_EXCEPTIONS, - description="Number of exceptions in asyncio coroutine", - unit="1", - ) - self.coro_cancelled_metric = self._meter.create_counter( - name=ASYNCIO_COROUTINE_CANCELLED, - description="Number of asyncio coroutine cancelled", - unit="1", - ) - self.coro_active_metric = self._meter.create_up_down_counter( - name=ASYNCIO_COROUTINE_ACTIVE, - description="Number of asyncio coroutine active", - unit="1", - ) - self.coro_created_metric = self._meter.create_counter( - name=ASYNCIO_COROUTINE_CREATED, - description="Number of asyncio coroutine created", - unit="1", - ) - self.coro_finished_metric = self._meter.create_counter( - name=ASYNCIO_COROUTINE_FINISHED, - description="Number of asyncio coroutine finished", - unit="1", - ) - self.coro_timeout_metric = self._meter.create_counter( - name=ASYNCIO_COROUTINE_TIMEOUTS, - description="Number of asyncio coroutine timeouts", - unit="1", - ) - - def create_future_metric(self): - self.future_duration_metric = self._meter.create_histogram( - name=ASYNCIO_FUTURES_DURATION, - description="Duration of asyncio future", - unit="ms", - ) - self.future_exception_metric = self._meter.create_counter( - name=ASYNCIO_FUTURES_EXCEPTIONS, - description="Number of exceptions in asyncio future", - unit="1", - ) - self.future_cancelled_metric = self._meter.create_counter( - name=ASYNCIO_FUTURES_CANCELLED, - description="Number of asyncio future cancelled", - unit="1", - ) - self.future_created_metric = self._meter.create_counter( - name=ASYNCIO_FUTURES_CREATED, - description="Number of asyncio future created", - unit="1", - ) - self.future_active_metric = self._meter.create_up_down_counter( - name=ASYNCIO_FUTURES_ACTIVE, - description="Number of asyncio future active", - unit="1", - ) - self.future_finished_metric = self._meter.create_counter( - name=ASYNCIO_FUTURES_FINISHED, - description="Number of asyncio future finished", - unit="1", - ) - self.future_timeout_metric = self._meter.create_counter( - name=ASYNCIO_FUTURES_TIMEOUTS, - description="Number of asyncio future timeouts", - unit="1", - ) - def create_to_thread_metric(self): - self.to_thread_duration_metric = self._meter.create_histogram( - name=ASYNCIO_TO_THREAD_DURATION, - description="Duration of asyncio function", - unit="ms", - ) - self.to_thread_exception_metric = self._meter.create_counter( - name=ASYNCIO_TO_THREAD_EXCEPTIONS, - description="Number of exceptions in asyncio function", - unit="1", - ) - self.to_thread_created_metric = self._meter.create_counter( - name=ASYNCIO_TO_THREAD_CREATED, - description="Number of asyncio function created", - unit="1", - ) - self.to_thread_active_metric = self._meter.create_up_down_counter( - name=ASYNCIO_TO_THREAD_ACTIVE, - description="Number of asyncio function active", - unit="1", - ) - self.to_thread_finished_metric = self._meter.create_counter( - name=ASYNCIO_TO_THREAD_FINISHED, - description="Number of asyncio function finished", - unit="1", - ) +def determine_state(exception): + if isinstance(exception, asyncio.CancelledError): + return "cancelled" + if isinstance(exception, asyncio.TimeoutError): + return "timeout" + if exception: + return "exception" + return "finished" def uninstrument_taskgroup_create_task(): diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/metrics.py b/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/metrics.py deleted file mode 100644 index 92f02b8788..0000000000 --- a/instrumentation/opentelemetry-instrumentation-asyncio/src/opentelemetry/instrumentation/asyncio/metrics.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright The 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. - -ASYNCIO_COROUTINE_DURATION = "asyncio.coroutine.duration" -ASYNCIO_COROUTINE_EXCEPTIONS = "asyncio.coroutine.exceptions" -ASYNCIO_COROUTINE_CANCELLED = "asyncio.coroutine.cancelled" -ASYNCIO_COROUTINE_ACTIVE = "asyncio.coroutine.active" -ASYNCIO_COROUTINE_CREATED = "asyncio.coroutine.created" -ASYNCIO_COROUTINE_FINISHED = "asyncio.coroutine.finished" -ASYNCIO_COROUTINE_TIMEOUTS = "asyncio.coroutine.timeouts" - -ASYNCIO_COROUTINE_NAME = "coroutine.name" -ASYNCIO_EXCEPTIONS_NAME = "exceptions.name" - -ASYNCIO_FUTURES_DURATION = "asyncio.futures.duration" -ASYNCIO_FUTURES_EXCEPTIONS = "asyncio.futures.exceptions" -ASYNCIO_FUTURES_CANCELLED = "asyncio.futures.cancelled" -ASYNCIO_FUTURES_CREATED = "asyncio.futures.created" -ASYNCIO_FUTURES_ACTIVE = "asyncio.futures.active" -ASYNCIO_FUTURES_FINISHED = "asyncio.futures.finished" -ASYNCIO_FUTURES_TIMEOUTS = "asyncio.futures.timeouts" - -ASYNCIO_TO_THREAD_DURATION = "asyncio.to_thread.duration" -ASYNCIO_TO_THREAD_EXCEPTIONS = "asyncio.to_thread.exceptions" -ASYNCIO_TO_THREAD_CREATED = "asyncio.to_thread.created" -ASYNCIO_TO_THREAD_ACTIVE = "asyncio.to_thread.active" -ASYNCIO_TO_THREAD_FINISHED = "asyncio.to_thread.finished" - -__all__ = [ - "ASYNCIO_COROUTINE_DURATION", - "ASYNCIO_COROUTINE_EXCEPTIONS", - "ASYNCIO_COROUTINE_CANCELLED", - "ASYNCIO_COROUTINE_ACTIVE", - "ASYNCIO_COROUTINE_CREATED", - "ASYNCIO_COROUTINE_FINISHED", - "ASYNCIO_COROUTINE_TIMEOUTS", - "ASYNCIO_COROUTINE_NAME", - "ASYNCIO_EXCEPTIONS_NAME", - "ASYNCIO_FUTURES_DURATION", - "ASYNCIO_FUTURES_EXCEPTIONS", - "ASYNCIO_FUTURES_CANCELLED", - "ASYNCIO_FUTURES_CREATED", - "ASYNCIO_FUTURES_ACTIVE", - "ASYNCIO_FUTURES_FINISHED", - "ASYNCIO_FUTURES_TIMEOUTS", - "ASYNCIO_TO_THREAD_DURATION", - "ASYNCIO_TO_THREAD_EXCEPTIONS", - "ASYNCIO_TO_THREAD_CREATED", - "ASYNCIO_TO_THREAD_ACTIVE", - "ASYNCIO_TO_THREAD_FINISHED", -] diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_cancellation.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_cancellation.py index b812426685..32fc1caffa 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_cancellation.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_cancellation.py @@ -15,14 +15,7 @@ from unittest.mock import patch # pylint: disable=no-name-in-module -from opentelemetry.instrumentation.asyncio import ( - ASYNCIO_COROUTINE_ACTIVE, - ASYNCIO_COROUTINE_CANCELLED, - ASYNCIO_COROUTINE_CREATED, - ASYNCIO_COROUTINE_DURATION, - ASYNCIO_COROUTINE_FINISHED, - AsyncioInstrumentor, -) +from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor from opentelemetry.instrumentation.asyncio.environment_variables import ( OTEL_PYTHON_ASYNCIO_COROUTINE_NAMES_TO_TRACE, ) @@ -66,13 +59,20 @@ def test_cancel(self): .scope_metrics[0] .metrics ): - if metric.name == ASYNCIO_COROUTINE_CANCELLED: - self.assertEqual(metric.data.data_points[0].value, 1) - elif metric.name == ASYNCIO_COROUTINE_DURATION: - self.assertEqual(metric.data.data_points[0].min != 0, True) - elif metric.name == ASYNCIO_COROUTINE_ACTIVE: - self.assertEqual(metric.data.data_points[0].value, 0) - elif metric.name == ASYNCIO_COROUTINE_CREATED: - self.assertEqual(metric.data.data_points[0].value, 1) - elif metric.name == ASYNCIO_COROUTINE_FINISHED: - self.assertEqual(metric.data.data_points[0].value, 1) + if metric.name == "asyncio.process.duration": + for point in metric.data.data_points: + self.assertEqual(point.attributes["type"], "coroutine") + self.assertIn( + point.attributes["name"], + ["cancellation_coro", "cancellable_coroutine"], + ) + if metric.name == "asyncio.process.count": + for point in metric.data.data_points: + self.assertEqual(point.attributes["type"], "coroutine") + self.assertIn( + point.attributes["name"], + ["cancellation_coro", "cancellable_coroutine"], + ) + self.assertIn( + point.attributes["state"], ["finished", "cancelled"] + ) diff --git a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_ensure_future.py b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_ensure_future.py index a160c23850..daf3bc55f4 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_ensure_future.py +++ b/instrumentation/opentelemetry-instrumentation-asyncio/tests/test_asyncio_ensure_future.py @@ -17,16 +17,7 @@ import pytest # pylint: disable=no-name-in-module -from opentelemetry.instrumentation.asyncio import ( - ASYNCIO_FUTURES_ACTIVE, - ASYNCIO_FUTURES_CANCELLED, - ASYNCIO_FUTURES_CREATED, - ASYNCIO_FUTURES_DURATION, - ASYNCIO_FUTURES_EXCEPTIONS, - ASYNCIO_FUTURES_FINISHED, - ASYNCIO_FUTURES_TIMEOUTS, - AsyncioInstrumentor, -) +from opentelemetry.instrumentation.asyncio import AsyncioInstrumentor from opentelemetry.instrumentation.asyncio.environment_variables import ( OTEL_PYTHON_ASYNCIO_FUTURE_TRACE_ENABLED, ) @@ -35,16 +26,6 @@ from .common_test_func import async_func -_expected_metric_names = [ - ASYNCIO_FUTURES_DURATION, - ASYNCIO_FUTURES_EXCEPTIONS, - ASYNCIO_FUTURES_CANCELLED, - ASYNCIO_FUTURES_CREATED, - ASYNCIO_FUTURES_ACTIVE, - ASYNCIO_FUTURES_FINISHED, - ASYNCIO_FUTURES_TIMEOUTS, -] - class TestAsyncioEnsureFuture(TestBase): @patch.dict( @@ -101,17 +82,10 @@ async def test(): .scope_metrics[0] .metrics ): - if metric.name == ASYNCIO_FUTURES_DURATION: - self.assertEqual(metric.data.data_points[0].count, 1) - elif metric.name == ASYNCIO_FUTURES_ACTIVE: - self.assertEqual(metric.data.data_points[0].value, 0) - elif metric.name == ASYNCIO_FUTURES_CREATED: - self.assertEqual(metric.data.data_points[0].value, 1) - elif metric.name == ASYNCIO_FUTURES_FINISHED: - self.assertEqual(metric.data.data_points[0].value, 1) - elif metric.name == ASYNCIO_FUTURES_EXCEPTIONS: - self.assertEqual(metric.data.data_points[0].value, 0) - elif metric.name == ASYNCIO_FUTURES_CANCELLED: - self.assertEqual(metric.data.data_points[0].value, 0) - elif metric.name == ASYNCIO_FUTURES_TIMEOUTS: - self.assertEqual(metric.data.data_points[0].value, 0) + if metric.name == "asyncio.process.duration": + for point in metric.data.data_points: + self.assertEqual(point.attributes["type"], "future") + if metric.name == "asyncio.process.count": + for point in metric.data.data_points: + self.assertEqual(point.attributes["type"], "future") + self.assertEqual(point.attributes["state"], "finished")