mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 14:51:39 +08:00
68 lines
2.6 KiB
Python
68 lines
2.6 KiB
Python
"""
|
|
Guards the OpenTelemetry Flask instrumentation contract Dify relies on.
|
|
|
|
Flask can run teardown handlers more than once for a request. Instrumentation
|
|
versions before 0.63b0 left the span activation and context token in
|
|
``request.environ`` after the first teardown, so the second one detached an
|
|
already-used token and the OTel API logged "Failed to detach context" for every
|
|
affected request.
|
|
"""
|
|
|
|
import logging
|
|
from collections.abc import Iterator
|
|
|
|
import flask
|
|
import pytest
|
|
from opentelemetry.instrumentation.flask import FlaskInstrumentor
|
|
|
|
from tests.unit_tests.extensions.otel.conftest import memory_span_exporter as memory_span_exporter # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import mock_account_user as mock_account_user # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import mock_app_model as mock_app_model # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import mock_end_user as mock_end_user # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import mock_workflow_runner as mock_workflow_runner # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import reset_handler_instances as reset_handler_instances # noqa: PLC0414
|
|
from tests.unit_tests.extensions.otel.conftest import (
|
|
tracer_provider_with_memory_exporter as tracer_provider_with_memory_exporter, # noqa: PLC0414
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def instrumented_app() -> Iterator[flask.Flask]:
|
|
app = flask.Flask(__name__)
|
|
|
|
@app.route("/ping")
|
|
def ping() -> str:
|
|
return "pong"
|
|
|
|
instrumentor = FlaskInstrumentor()
|
|
instrumentor.instrument_app(app)
|
|
yield app
|
|
instrumentor.uninstrument_app(app)
|
|
|
|
|
|
@pytest.mark.usefixtures("tracer_provider_with_memory_exporter")
|
|
def test_duplicate_teardown_does_not_log_detach_failure(
|
|
instrumented_app: flask.Flask, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
handlers = instrumented_app.teardown_request_funcs[None]
|
|
# instrument_app registers its teardown handler last.
|
|
index = len(handlers) - 1
|
|
instrumentation_teardown = handlers[index]
|
|
calls = 0
|
|
|
|
def teardown_twice(exc: BaseException | None) -> None:
|
|
nonlocal calls
|
|
calls += 1
|
|
instrumentation_teardown(exc)
|
|
instrumentation_teardown(exc)
|
|
|
|
handlers[index] = teardown_twice
|
|
try:
|
|
with caplog.at_level(logging.ERROR, logger="opentelemetry.context"):
|
|
assert instrumented_app.test_client().get("/ping").status_code == 200
|
|
finally:
|
|
handlers[index] = instrumentation_teardown
|
|
|
|
assert calls == 1, "the instrumentation teardown never ran, so nothing was exercised"
|
|
assert "Failed to detach context" not in caplog.text
|