mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-29 21:23:55 +08:00
HTTP semantic convention stability migration for fastapi (#2682)
This commit is contained in:
@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
([#2638](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2638))
|
([#2638](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2638))
|
||||||
- `opentelemetry-instrumentation-asgi` Implement new semantic convention opt-in with stable http semantic conventions
|
- `opentelemetry-instrumentation-asgi` Implement new semantic convention opt-in with stable http semantic conventions
|
||||||
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
||||||
|
- `opentelemetry-instrumentation-fastapi` Implement new semantic convention opt-in with stable http semantic conventions
|
||||||
|
([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682))
|
||||||
- `opentelemetry-instrumentation-httpx` Implement new semantic convention opt-in migration with stable http semantic conventions
|
- `opentelemetry-instrumentation-httpx` Implement new semantic convention opt-in migration with stable http semantic conventions
|
||||||
([#2631](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2631))
|
([#2631](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2631))
|
||||||
- `opentelemetry-instrumentation-system-metrics` Permit to use psutil 6.0+.
|
- `opentelemetry-instrumentation-system-metrics` Permit to use psutil 6.0+.
|
||||||
@ -32,9 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi`, `opentelemetry-instrumentation-starlette` Use `tracer` and `meter` of originating components instead of one from `asgi` middleware
|
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-fastapi`, `opentelemetry-instrumentation-starlette` Use `tracer` and `meter` of originating components instead of one from `asgi` middleware
|
||||||
([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580))
|
([#2580](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2580))
|
||||||
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope
|
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `asgi` middleware
|
||||||
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
||||||
|
- Populate `{method}` as `HTTP` on `_OTHER` methods from scope for `fastapi` middleware
|
||||||
|
([#2682](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2682))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes | experimental
|
| [opentelemetry-instrumentation-django](./opentelemetry-instrumentation-django) | django >= 1.10 | Yes | experimental
|
||||||
| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 6.0 | No | experimental
|
| [opentelemetry-instrumentation-elasticsearch](./opentelemetry-instrumentation-elasticsearch) | elasticsearch >= 6.0 | No | experimental
|
||||||
| [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes | experimental
|
| [opentelemetry-instrumentation-falcon](./opentelemetry-instrumentation-falcon) | falcon >= 1.4.1, < 4.0.0 | Yes | experimental
|
||||||
| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | experimental
|
| [opentelemetry-instrumentation-fastapi](./opentelemetry-instrumentation-fastapi) | fastapi ~= 0.58 | Yes | migration
|
||||||
| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | migration
|
| [opentelemetry-instrumentation-flask](./opentelemetry-instrumentation-flask) | flask >= 1.0 | Yes | migration
|
||||||
| [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio ~= 1.27 | No | experimental
|
| [opentelemetry-instrumentation-grpc](./opentelemetry-instrumentation-grpc) | grpcio ~= 1.27 | No | experimental
|
||||||
| [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No | migration
|
| [opentelemetry-instrumentation-httpx](./opentelemetry-instrumentation-httpx) | httpx >= 0.18.0 | No | migration
|
||||||
|
@ -177,6 +177,12 @@ from typing import Collection
|
|||||||
import fastapi
|
import fastapi
|
||||||
from starlette.routing import Match
|
from starlette.routing import Match
|
||||||
|
|
||||||
|
from opentelemetry.instrumentation._semconv import (
|
||||||
|
_get_schema_url,
|
||||||
|
_HTTPStabilityMode,
|
||||||
|
_OpenTelemetrySemanticConventionStability,
|
||||||
|
_OpenTelemetryStabilitySignalType,
|
||||||
|
)
|
||||||
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
||||||
from opentelemetry.instrumentation.asgi.types import (
|
from opentelemetry.instrumentation.asgi.types import (
|
||||||
ClientRequestHook,
|
ClientRequestHook,
|
||||||
@ -189,7 +195,11 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|||||||
from opentelemetry.metrics import get_meter
|
from opentelemetry.metrics import get_meter
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
from opentelemetry.trace import get_tracer
|
from opentelemetry.trace import get_tracer
|
||||||
from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls
|
from opentelemetry.util.http import (
|
||||||
|
get_excluded_urls,
|
||||||
|
parse_excluded_urls,
|
||||||
|
sanitize_method,
|
||||||
|
)
|
||||||
|
|
||||||
_excluded_urls_from_env = get_excluded_urls("FASTAPI")
|
_excluded_urls_from_env = get_excluded_urls("FASTAPI")
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@ -218,6 +228,11 @@ class FastAPIInstrumentor(BaseInstrumentor):
|
|||||||
app._is_instrumented_by_opentelemetry = False
|
app._is_instrumented_by_opentelemetry = False
|
||||||
|
|
||||||
if not getattr(app, "_is_instrumented_by_opentelemetry", False):
|
if not getattr(app, "_is_instrumented_by_opentelemetry", False):
|
||||||
|
# initialize semantic conventions opt-in if needed
|
||||||
|
_OpenTelemetrySemanticConventionStability._initialize()
|
||||||
|
sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
|
||||||
|
_OpenTelemetryStabilitySignalType.HTTP,
|
||||||
|
)
|
||||||
if excluded_urls is None:
|
if excluded_urls is None:
|
||||||
excluded_urls = _excluded_urls_from_env
|
excluded_urls = _excluded_urls_from_env
|
||||||
else:
|
else:
|
||||||
@ -226,13 +241,13 @@ class FastAPIInstrumentor(BaseInstrumentor):
|
|||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
tracer_provider,
|
tracer_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(sem_conv_opt_in_mode),
|
||||||
)
|
)
|
||||||
meter = get_meter(
|
meter = get_meter(
|
||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
meter_provider,
|
meter_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(sem_conv_opt_in_mode),
|
||||||
)
|
)
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
@ -303,6 +318,7 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
|
|||||||
_client_request_hook: ClientRequestHook = None
|
_client_request_hook: ClientRequestHook = None
|
||||||
_client_response_hook: ClientResponseHook = None
|
_client_response_hook: ClientResponseHook = None
|
||||||
_instrumented_fastapi_apps = set()
|
_instrumented_fastapi_apps = set()
|
||||||
|
_sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -310,13 +326,17 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
|
|||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
_InstrumentedFastAPI._tracer_provider,
|
_InstrumentedFastAPI._tracer_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(
|
||||||
|
_InstrumentedFastAPI._sem_conv_opt_in_mode
|
||||||
|
),
|
||||||
)
|
)
|
||||||
meter = get_meter(
|
meter = get_meter(
|
||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
_InstrumentedFastAPI._meter_provider,
|
_InstrumentedFastAPI._meter_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(
|
||||||
|
_InstrumentedFastAPI._sem_conv_opt_in_mode
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.add_middleware(
|
self.add_middleware(
|
||||||
OpenTelemetryMiddleware,
|
OpenTelemetryMiddleware,
|
||||||
@ -373,8 +393,10 @@ def _get_default_span_details(scope):
|
|||||||
A tuple of span name and attributes
|
A tuple of span name and attributes
|
||||||
"""
|
"""
|
||||||
route = _get_route_details(scope)
|
route = _get_route_details(scope)
|
||||||
method = scope.get("method", "")
|
method = sanitize_method(scope.get("method", "").strip())
|
||||||
attributes = {}
|
attributes = {}
|
||||||
|
if method == "_OTHER":
|
||||||
|
method = "HTTP"
|
||||||
if route:
|
if route:
|
||||||
attributes[SpanAttributes.HTTP_ROUTE] = route
|
attributes[SpanAttributes.HTTP_ROUTE] = route
|
||||||
if method and route: # http
|
if method and route: # http
|
||||||
|
@ -16,3 +16,5 @@
|
|||||||
_instruments = ("fastapi ~= 0.58",)
|
_instruments = ("fastapi ~= 0.58",)
|
||||||
|
|
||||||
_supports_metrics = True
|
_supports_metrics = True
|
||||||
|
|
||||||
|
_semconv_status = "migration"
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
# pylint: disable=too-many-lines
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from timeit import default_timer
|
from timeit import default_timer
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@ -20,39 +23,77 @@ from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
|||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
import opentelemetry.instrumentation.fastapi as otel_fastapi
|
import opentelemetry.instrumentation.fastapi as otel_fastapi
|
||||||
|
from opentelemetry.instrumentation._semconv import (
|
||||||
|
OTEL_SEMCONV_STABILITY_OPT_IN,
|
||||||
|
_OpenTelemetrySemanticConventionStability,
|
||||||
|
_server_active_requests_count_attrs_new,
|
||||||
|
_server_active_requests_count_attrs_old,
|
||||||
|
_server_duration_attrs_new,
|
||||||
|
_server_duration_attrs_old,
|
||||||
|
)
|
||||||
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
||||||
from opentelemetry.sdk.metrics.export import (
|
from opentelemetry.sdk.metrics.export import (
|
||||||
HistogramDataPoint,
|
HistogramDataPoint,
|
||||||
NumberDataPoint,
|
NumberDataPoint,
|
||||||
)
|
)
|
||||||
from opentelemetry.sdk.resources import Resource
|
from opentelemetry.sdk.resources import Resource
|
||||||
|
from opentelemetry.semconv.attributes.http_attributes import (
|
||||||
|
HTTP_REQUEST_METHOD,
|
||||||
|
HTTP_RESPONSE_STATUS_CODE,
|
||||||
|
HTTP_ROUTE,
|
||||||
|
)
|
||||||
|
from opentelemetry.semconv.attributes.network_attributes import (
|
||||||
|
NETWORK_PROTOCOL_VERSION,
|
||||||
|
)
|
||||||
|
from opentelemetry.semconv.attributes.url_attributes import URL_SCHEME
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
from opentelemetry.test.test_base import TestBase
|
from opentelemetry.test.test_base import TestBase
|
||||||
from opentelemetry.util.http import (
|
from opentelemetry.util.http import get_excluded_urls
|
||||||
_active_requests_count_attrs,
|
|
||||||
_duration_attrs,
|
|
||||||
get_excluded_urls,
|
|
||||||
)
|
|
||||||
|
|
||||||
_expected_metric_names = [
|
_expected_metric_names_old = [
|
||||||
"http.server.active_requests",
|
"http.server.active_requests",
|
||||||
"http.server.duration",
|
"http.server.duration",
|
||||||
"http.server.response.size",
|
"http.server.response.size",
|
||||||
"http.server.request.size",
|
"http.server.request.size",
|
||||||
]
|
]
|
||||||
_recommended_attrs = {
|
_expected_metric_names_new = [
|
||||||
"http.server.active_requests": _active_requests_count_attrs,
|
"http.server.active_requests",
|
||||||
"http.server.duration": {*_duration_attrs, SpanAttributes.HTTP_TARGET},
|
"http.server.request.duration",
|
||||||
|
"http.server.response.body.size",
|
||||||
|
"http.server.request.body.size",
|
||||||
|
]
|
||||||
|
_expected_metric_names_both = _expected_metric_names_old
|
||||||
|
_expected_metric_names_both.extend(_expected_metric_names_new)
|
||||||
|
|
||||||
|
_recommended_attrs_old = {
|
||||||
|
"http.server.active_requests": _server_active_requests_count_attrs_old,
|
||||||
|
"http.server.duration": {
|
||||||
|
*_server_duration_attrs_old,
|
||||||
|
SpanAttributes.HTTP_TARGET,
|
||||||
|
},
|
||||||
"http.server.response.size": {
|
"http.server.response.size": {
|
||||||
*_duration_attrs,
|
*_server_duration_attrs_old,
|
||||||
SpanAttributes.HTTP_TARGET,
|
SpanAttributes.HTTP_TARGET,
|
||||||
},
|
},
|
||||||
"http.server.request.size": {
|
"http.server.request.size": {
|
||||||
*_duration_attrs,
|
*_server_duration_attrs_old,
|
||||||
SpanAttributes.HTTP_TARGET,
|
SpanAttributes.HTTP_TARGET,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_recommended_attrs_new = {
|
||||||
|
"http.server.active_requests": _server_active_requests_count_attrs_new,
|
||||||
|
"http.server.request.duration": _server_duration_attrs_new,
|
||||||
|
"http.server.response.body.size": _server_duration_attrs_new,
|
||||||
|
"http.server.request.body.size": _server_duration_attrs_new,
|
||||||
|
}
|
||||||
|
|
||||||
|
_recommended_attrs_both = _recommended_attrs_old.copy()
|
||||||
|
_recommended_attrs_both.update(_recommended_attrs_new)
|
||||||
|
_recommended_attrs_both["http.server.active_requests"].extend(
|
||||||
|
_server_active_requests_count_attrs_old
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestBaseFastAPI(TestBase):
|
class TestBaseFastAPI(TestBase):
|
||||||
def _create_app(self):
|
def _create_app(self):
|
||||||
@ -88,10 +129,23 @@ class TestBaseFastAPI(TestBase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
|
test_name = ""
|
||||||
|
if hasattr(self, "_testMethodName"):
|
||||||
|
test_name = self._testMethodName
|
||||||
|
sem_conv_mode = "default"
|
||||||
|
if "new_semconv" in test_name:
|
||||||
|
sem_conv_mode = "http"
|
||||||
|
elif "both_semconv" in test_name:
|
||||||
|
sem_conv_mode = "http/dup"
|
||||||
self.env_patch = patch.dict(
|
self.env_patch = patch.dict(
|
||||||
"os.environ",
|
"os.environ",
|
||||||
{"OTEL_PYTHON_FASTAPI_EXCLUDED_URLS": "/exclude/123,healthzz"},
|
{
|
||||||
|
"OTEL_PYTHON_FASTAPI_EXCLUDED_URLS": "/exclude/123,healthzz",
|
||||||
|
OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
_OpenTelemetrySemanticConventionStability._initialized = False
|
||||||
self.env_patch.start()
|
self.env_patch.start()
|
||||||
self.exclude_patch = patch(
|
self.exclude_patch = patch(
|
||||||
"opentelemetry.instrumentation.fastapi._excluded_urls_from_env",
|
"opentelemetry.instrumentation.fastapi._excluded_urls_from_env",
|
||||||
@ -142,7 +196,6 @@ class TestBaseFastAPI(TestBase):
|
|||||||
|
|
||||||
|
|
||||||
class TestBaseManualFastAPI(TestBaseFastAPI):
|
class TestBaseManualFastAPI(TestBaseFastAPI):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
if cls is TestBaseManualFastAPI:
|
if cls is TestBaseManualFastAPI:
|
||||||
@ -196,7 +249,6 @@ class TestBaseManualFastAPI(TestBaseFastAPI):
|
|||||||
|
|
||||||
|
|
||||||
class TestBaseAutoFastAPI(TestBaseFastAPI):
|
class TestBaseAutoFastAPI(TestBaseFastAPI):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
if cls is TestBaseAutoFastAPI:
|
if cls is TestBaseAutoFastAPI:
|
||||||
@ -259,6 +311,7 @@ class TestBaseAutoFastAPI(TestBaseFastAPI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-public-methods
|
||||||
class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
||||||
def test_instrument_app_with_instrument(self):
|
def test_instrument_app_with_instrument(self):
|
||||||
if not isinstance(self, TestAutoInstrumentation):
|
if not isinstance(self, TestAutoInstrumentation):
|
||||||
@ -358,7 +411,7 @@ class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
|||||||
)
|
)
|
||||||
self.assertTrue(len(scope_metric.metrics) == 3)
|
self.assertTrue(len(scope_metric.metrics) == 3)
|
||||||
for metric in scope_metric.metrics:
|
for metric in scope_metric.metrics:
|
||||||
self.assertIn(metric.name, _expected_metric_names)
|
self.assertIn(metric.name, _expected_metric_names_old)
|
||||||
data_points = list(metric.data.data_points)
|
data_points = list(metric.data.data_points)
|
||||||
self.assertEqual(len(data_points), 1)
|
self.assertEqual(len(data_points), 1)
|
||||||
for point in data_points:
|
for point in data_points:
|
||||||
@ -369,7 +422,71 @@ class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
|||||||
number_data_point_seen = True
|
number_data_point_seen = True
|
||||||
for attr in point.attributes:
|
for attr in point.attributes:
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
attr, _recommended_attrs[metric.name]
|
attr, _recommended_attrs_old[metric.name]
|
||||||
|
)
|
||||||
|
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
|
||||||
|
|
||||||
|
def test_fastapi_metrics_new_semconv(self):
|
||||||
|
self._client.get("/foobar")
|
||||||
|
self._client.get("/foobar")
|
||||||
|
self._client.get("/foobar")
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
number_data_point_seen = False
|
||||||
|
histogram_data_point_seen = False
|
||||||
|
self.assertTrue(len(metrics_list.resource_metrics) == 1)
|
||||||
|
for resource_metric in metrics_list.resource_metrics:
|
||||||
|
self.assertTrue(len(resource_metric.scope_metrics) == 1)
|
||||||
|
for scope_metric in resource_metric.scope_metrics:
|
||||||
|
self.assertEqual(
|
||||||
|
scope_metric.scope.name,
|
||||||
|
"opentelemetry.instrumentation.fastapi",
|
||||||
|
)
|
||||||
|
self.assertTrue(len(scope_metric.metrics) == 3)
|
||||||
|
for metric in scope_metric.metrics:
|
||||||
|
self.assertIn(metric.name, _expected_metric_names_new)
|
||||||
|
data_points = list(metric.data.data_points)
|
||||||
|
self.assertEqual(len(data_points), 1)
|
||||||
|
for point in data_points:
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 3)
|
||||||
|
histogram_data_point_seen = True
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
number_data_point_seen = True
|
||||||
|
for attr in point.attributes:
|
||||||
|
self.assertIn(
|
||||||
|
attr, _recommended_attrs_new[metric.name]
|
||||||
|
)
|
||||||
|
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
|
||||||
|
|
||||||
|
def test_fastapi_metrics_both_semconv(self):
|
||||||
|
self._client.get("/foobar")
|
||||||
|
self._client.get("/foobar")
|
||||||
|
self._client.get("/foobar")
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
number_data_point_seen = False
|
||||||
|
histogram_data_point_seen = False
|
||||||
|
self.assertTrue(len(metrics_list.resource_metrics) == 1)
|
||||||
|
for resource_metric in metrics_list.resource_metrics:
|
||||||
|
self.assertTrue(len(resource_metric.scope_metrics) == 1)
|
||||||
|
for scope_metric in resource_metric.scope_metrics:
|
||||||
|
self.assertEqual(
|
||||||
|
scope_metric.scope.name,
|
||||||
|
"opentelemetry.instrumentation.fastapi",
|
||||||
|
)
|
||||||
|
self.assertTrue(len(scope_metric.metrics) == 5)
|
||||||
|
for metric in scope_metric.metrics:
|
||||||
|
self.assertIn(metric.name, _expected_metric_names_both)
|
||||||
|
data_points = list(metric.data.data_points)
|
||||||
|
self.assertEqual(len(data_points), 1)
|
||||||
|
for point in data_points:
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 3)
|
||||||
|
histogram_data_point_seen = True
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
number_data_point_seen = True
|
||||||
|
for attr in point.attributes:
|
||||||
|
self.assertIn(
|
||||||
|
attr, _recommended_attrs_both[metric.name]
|
||||||
)
|
)
|
||||||
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
|
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
|
||||||
|
|
||||||
@ -378,21 +495,21 @@ class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
|||||||
self._client.get("/foobar")
|
self._client.get("/foobar")
|
||||||
duration = max(round((default_timer() - start) * 1000), 0)
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
expected_duration_attributes = {
|
expected_duration_attributes = {
|
||||||
"http.method": "GET",
|
SpanAttributes.HTTP_METHOD: "GET",
|
||||||
"http.host": "testserver:443",
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
"http.scheme": "https",
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
"http.flavor": "1.1",
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
"http.server_name": "testserver",
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
"net.host.port": 443,
|
SpanAttributes.NET_HOST_PORT: 443,
|
||||||
"http.status_code": 200,
|
SpanAttributes.HTTP_STATUS_CODE: 200,
|
||||||
"http.target": "/foobar",
|
SpanAttributes.HTTP_TARGET: "/foobar",
|
||||||
}
|
}
|
||||||
expected_requests_count_attributes = {
|
expected_requests_count_attributes = {
|
||||||
"http.method": "GET",
|
SpanAttributes.HTTP_METHOD: "GET",
|
||||||
"http.host": "testserver:443",
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
"http.scheme": "https",
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
"http.flavor": "1.1",
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
"http.server_name": "testserver",
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
}
|
}
|
||||||
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
for metric in (
|
for metric in (
|
||||||
@ -413,6 +530,288 @@ class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
|||||||
)
|
)
|
||||||
self.assertEqual(point.value, 0)
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_metric_success_new_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
self._client.get("/foobar")
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
expected_duration_attributes = {
|
||||||
|
HTTP_REQUEST_METHOD: "GET",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
NETWORK_PROTOCOL_VERSION: "1.1",
|
||||||
|
HTTP_RESPONSE_STATUS_CODE: 200,
|
||||||
|
HTTP_ROUTE: "/foobar",
|
||||||
|
}
|
||||||
|
expected_requests_count_attributes = {
|
||||||
|
HTTP_REQUEST_METHOD: "GET",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
}
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_requests_count_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_metric_success_both_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
self._client.get("/foobar")
|
||||||
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
expected_duration_attributes_old = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "GET",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
SpanAttributes.NET_HOST_PORT: 443,
|
||||||
|
SpanAttributes.HTTP_STATUS_CODE: 200,
|
||||||
|
SpanAttributes.HTTP_TARGET: "/foobar",
|
||||||
|
}
|
||||||
|
expected_duration_attributes_new = {
|
||||||
|
HTTP_REQUEST_METHOD: "GET",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
NETWORK_PROTOCOL_VERSION: "1.1",
|
||||||
|
HTTP_RESPONSE_STATUS_CODE: 200,
|
||||||
|
HTTP_ROUTE: "/foobar",
|
||||||
|
}
|
||||||
|
expected_requests_count_attributes = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "GET",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
HTTP_REQUEST_METHOD: "GET",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
}
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.duration":
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.response.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.request.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_requests_count_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_metric_nonstandard_http_method_success(self):
|
||||||
|
start = default_timer()
|
||||||
|
self._client.request("NONSTANDARD", "/foobar")
|
||||||
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
|
expected_duration_attributes = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "_OTHER",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
SpanAttributes.NET_HOST_PORT: 443,
|
||||||
|
SpanAttributes.HTTP_STATUS_CODE: 405,
|
||||||
|
SpanAttributes.HTTP_TARGET: "/foobar",
|
||||||
|
}
|
||||||
|
expected_requests_count_attributes = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "_OTHER",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
}
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_requests_count_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_metric_nonstandard_http_method_success_new_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
self._client.request("NONSTANDARD", "/foobar")
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
expected_duration_attributes = {
|
||||||
|
HTTP_REQUEST_METHOD: "_OTHER",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
NETWORK_PROTOCOL_VERSION: "1.1",
|
||||||
|
HTTP_RESPONSE_STATUS_CODE: 405,
|
||||||
|
HTTP_ROUTE: "/foobar",
|
||||||
|
}
|
||||||
|
expected_requests_count_attributes = {
|
||||||
|
HTTP_REQUEST_METHOD: "_OTHER",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
}
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(31, point.sum)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_requests_count_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_metric_nonstandard_http_method_success_both_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
self._client.request("NONSTANDARD", "/foobar")
|
||||||
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
expected_duration_attributes_old = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "_OTHER",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
SpanAttributes.NET_HOST_PORT: 443,
|
||||||
|
SpanAttributes.HTTP_STATUS_CODE: 405,
|
||||||
|
SpanAttributes.HTTP_TARGET: "/foobar",
|
||||||
|
}
|
||||||
|
expected_duration_attributes_new = {
|
||||||
|
HTTP_REQUEST_METHOD: "_OTHER",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
NETWORK_PROTOCOL_VERSION: "1.1",
|
||||||
|
HTTP_RESPONSE_STATUS_CODE: 405,
|
||||||
|
HTTP_ROUTE: "/foobar",
|
||||||
|
}
|
||||||
|
expected_requests_count_attributes = {
|
||||||
|
SpanAttributes.HTTP_METHOD: "_OTHER",
|
||||||
|
SpanAttributes.HTTP_HOST: "testserver:443",
|
||||||
|
SpanAttributes.HTTP_SCHEME: "https",
|
||||||
|
SpanAttributes.HTTP_FLAVOR: "1.1",
|
||||||
|
SpanAttributes.HTTP_SERVER_NAME: "testserver",
|
||||||
|
HTTP_REQUEST_METHOD: "_OTHER",
|
||||||
|
URL_SCHEME: "https",
|
||||||
|
}
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(31, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_new,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.duration":
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.response.size":
|
||||||
|
self.assertEqual(31, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
elif metric.name == "http.server.request.size":
|
||||||
|
self.assertEqual(25, point.sum)
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_duration_attributes_old,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertDictEqual(
|
||||||
|
expected_requests_count_attributes,
|
||||||
|
dict(point.attributes),
|
||||||
|
)
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
def test_basic_post_request_metric_success(self):
|
def test_basic_post_request_metric_success(self):
|
||||||
start = default_timer()
|
start = default_timer()
|
||||||
response = self._client.post(
|
response = self._client.post(
|
||||||
@ -438,6 +837,63 @@ class TestFastAPIManualInstrumentation(TestBaseManualFastAPI):
|
|||||||
if isinstance(point, NumberDataPoint):
|
if isinstance(point, NumberDataPoint):
|
||||||
self.assertEqual(point.value, 0)
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_post_request_metric_success_new_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
response = self._client.post(
|
||||||
|
"/foobar",
|
||||||
|
json={"foo": "bar"},
|
||||||
|
)
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
response_size = int(response.headers.get("content-length"))
|
||||||
|
request_size = int(response.request.headers.get("content-length"))
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(response_size, point.sum)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(request_size, point.sum)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
|
def test_basic_post_request_metric_success_both_semconv(self):
|
||||||
|
start = default_timer()
|
||||||
|
response = self._client.post(
|
||||||
|
"/foobar",
|
||||||
|
json={"foo": "bar"},
|
||||||
|
)
|
||||||
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
|
duration_s = max(default_timer() - start, 0)
|
||||||
|
response_size = int(response.headers.get("content-length"))
|
||||||
|
request_size = int(response.request.headers.get("content-length"))
|
||||||
|
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||||
|
for metric in (
|
||||||
|
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||||
|
):
|
||||||
|
for point in list(metric.data.data_points):
|
||||||
|
if isinstance(point, HistogramDataPoint):
|
||||||
|
self.assertEqual(point.count, 1)
|
||||||
|
if metric.name == "http.server.request.duration":
|
||||||
|
self.assertAlmostEqual(duration_s, point.sum, places=1)
|
||||||
|
elif metric.name == "http.server.response.body.size":
|
||||||
|
self.assertEqual(response_size, point.sum)
|
||||||
|
elif metric.name == "http.server.request.body.size":
|
||||||
|
self.assertEqual(request_size, point.sum)
|
||||||
|
elif metric.name == "http.server.duration":
|
||||||
|
self.assertAlmostEqual(duration, point.sum, delta=40)
|
||||||
|
elif metric.name == "http.server.response.size":
|
||||||
|
self.assertEqual(response_size, point.sum)
|
||||||
|
elif metric.name == "http.server.request.size":
|
||||||
|
self.assertEqual(request_size, point.sum)
|
||||||
|
if isinstance(point, NumberDataPoint):
|
||||||
|
self.assertEqual(point.value, 0)
|
||||||
|
|
||||||
def test_metric_uninstrument_app(self):
|
def test_metric_uninstrument_app(self):
|
||||||
self._client.get("/foobar")
|
self._client.get("/foobar")
|
||||||
self._instrumentor.uninstrument_app(self._app)
|
self._instrumentor.uninstrument_app(self._app)
|
||||||
|
@ -543,7 +543,9 @@ class _InstrumentedFlask(flask.Flask):
|
|||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
_InstrumentedFlask._meter_provider,
|
_InstrumentedFlask._meter_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(
|
||||||
|
_InstrumentedFlask._sem_conv_opt_in_mode
|
||||||
|
),
|
||||||
)
|
)
|
||||||
duration_histogram_old = None
|
duration_histogram_old = None
|
||||||
if _report_old(_InstrumentedFlask._sem_conv_opt_in_mode):
|
if _report_old(_InstrumentedFlask._sem_conv_opt_in_mode):
|
||||||
@ -579,7 +581,9 @@ class _InstrumentedFlask(flask.Flask):
|
|||||||
__name__,
|
__name__,
|
||||||
__version__,
|
__version__,
|
||||||
_InstrumentedFlask._tracer_provider,
|
_InstrumentedFlask._tracer_provider,
|
||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url=_get_schema_url(
|
||||||
|
_InstrumentedFlask._sem_conv_opt_in_mode
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
_before_request = _wrapped_before_request(
|
_before_request = _wrapped_before_request(
|
||||||
|
Reference in New Issue
Block a user