tests/test_telemetry/test_environment_integrations.py PYTHON 246 lines View on github.com → Search inside
1"""Environment export remains active alongside independently configured SDKs."""23import pytest45from ._otlp import otlp_collector6from ._subprocess import run_in_subprocess789@pytest.mark.parametrize(10    "mode",11    [12        "global",13        "explicit",14        "sentry-first",15        "fastapi-first",16        "sentry-classic",17        "logfire",18        "logfire-opt-out",19    ],20)21@run_in_subprocess22def test_environment_export_with_existing_integrations(mode):23    import os2425    from fastapi import FastAPI26    from fastapi.telemetry import TelemetryConfig, _runtime27    from fastapi.testclient import TestClient28    from opentelemetry import _logs, metrics, trace29    from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import (30        ExportLogsServiceRequest,31    )32    from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import (33        ExportMetricsServiceRequest,34    )35    from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (36        ExportTraceServiceRequest,37    )38    from opentelemetry.proto.trace.v1.trace_pb2 import Span39    from opentelemetry.sdk._logs import LoggerProvider40    from opentelemetry.sdk._logs.export import (41        InMemoryLogRecordExporter,42        SimpleLogRecordProcessor,43    )44    from opentelemetry.sdk.metrics import MeterProvider45    from opentelemetry.sdk.metrics.export import InMemoryMetricReader46    from opentelemetry.sdk.trace import TracerProvider47    from opentelemetry.sdk.trace.export import SimpleSpanProcessor48    from opentelemetry.sdk.trace.export.in_memory_span_exporter import (49        InMemorySpanExporter,50    )5152    with otlp_collector() as (base, received):53        os.environ["FASTAPI_OTEL_AUTO_CONFIGURE"] = "true"54        os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = base + "/environment"55        os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = "x-cloud=preserved"56        os.environ["OTEL_BSP_SCHEDULE_DELAY"] = "60000"57        os.environ["OTEL_BLRP_SCHEDULE_DELAY"] = "60000"58        os.environ["OTEL_METRIC_EXPORT_INTERVAL"] = "60000"59        settings: TelemetryConfig = {}60        original_spans = InMemorySpanExporter()61        original_logs = InMemoryLogRecordExporter()62        original_metrics = InMemoryMetricReader()63        if mode in ("global", "explicit"):64            tp = TracerProvider(shutdown_on_exit=False)65            tp.add_span_processor(SimpleSpanProcessor(original_spans))66            mp = MeterProvider(67                metric_readers=[original_metrics], shutdown_on_exit=False68            )69            lp = LoggerProvider(shutdown_on_exit=False)70            lp.add_log_record_processor(SimpleLogRecordProcessor(original_logs))71            if mode == "global":72                trace.set_tracer_provider(tp)73                metrics.set_meter_provider(mp)74                _logs.set_logger_provider(lp)75            else:76                settings = {77                    "tracer_provider": tp,78                    "meter_provider": mp,79                    "logger_provider": lp,80                }81        elif mode.startswith("logfire"):82            import logfire8384            logfire.configure(85                send_to_logfire=False,86                console=False,87                additional_span_processors=[SimpleSpanProcessor(original_spans)],88                metrics=logfire.MetricsOptions(additional_readers=[original_metrics]),89                advanced=logfire.AdvancedOptions(90                    log_record_processors=[SimpleLogRecordProcessor(original_logs)]91                ),92            )93            settings["auto_configure"] = mode != "logfire-opt-out"94        else:95            import sentry_sdk96            from sentry_sdk.integrations.fastapi import FastApiIntegration97            from sentry_sdk.integrations.otlp import OTLPIntegration98            from sentry_sdk.integrations.starlette import StarletteIntegration99            from sentry_sdk.transport import Transport100101            items = []102103            class LocalTransport(Transport):104                def capture_envelope(self, envelope):105                    items.extend(item.type for item in envelope.items)106107            if mode == "fastapi-first":108                with TestClient(FastAPI()):109                    pass110            integrations = (111                [StarletteIntegration(), FastApiIntegration()]112                if mode == "sentry-classic"113                else [114                    OTLPIntegration(115                        collector_url=base + "/sentry/traces", setup_propagator=False116                    )117                ]118            )119            sentry_sdk.init(120                dsn="https://public@example.invalid/1",121                transport=LocalTransport,122                default_integrations=False,123                auto_enabling_integrations=False,124                integrations=integrations,125                traces_sample_rate=1.0,126                send_client_reports=False,127            )128129        app = FastAPI(telemetry=settings)130131        @app.get("/items/{item_id}")132        def endpoint(item_id: int):133            raise ValueError("environment export")134135        try:136            for _ in range(2):137                with TestClient(app, raise_server_exceptions=False) as client:138                    assert client.get("/items/1").status_code == 500139            if mode.startswith("logfire"):140                logfire.force_flush()141            elif mode.startswith("sentry") or mode == "fastapi-first":142                tracer_provider = trace.get_tracer_provider()143                assert isinstance(tracer_provider, TracerProvider)144                tracer_provider.force_flush()145                sentry_sdk.flush()146147            def spans_at(path):148                requests = [149                    ExportTraceServiceRequest.FromString(body)150                    for url, body, _ in received151                    if url == path152                ]153                return [154                    span155                    for request in requests156                    for resource in request.resource_spans157                    for scope in resource.scope_spans158                    for span in scope.spans159                ]160161            environment_spans = spans_at("/environment/v1/traces")162            multiplier = 2 if mode == "logfire" else 1163            servers = [164                span for span in environment_spans if span.kind == Span.SPAN_KIND_SERVER165            ]166            assert len(servers) == 2 * multiplier, (mode, len(servers))167            assert len({span.span_id for span in servers}) == 2168            assert all(span.name == "GET /items/{item_id}" for span in servers)169            if mode in ("global", "explicit"):170                assert sorted(171                    (span.context.trace_id, span.context.span_id, span.name)172                    for span in original_spans.get_finished_spans()173                ) == sorted(174                    (175                        int.from_bytes(span.trace_id, "big"),176                        int.from_bytes(span.span_id, "big"),177                        span.name,178                    )179                    for span in environment_spans180                )181            if mode in ("sentry-first", "fastapi-first"):182                sentry_spans = spans_at("/sentry/traces")183                assert {span.span_id for span in sentry_spans} == {184                    span.span_id for span in environment_spans185                }186            if mode == "sentry-classic":187                assert items.count("event") == 2, items188                assert items.count("transaction") == 2, items189190            log_requests = [191                ExportLogsServiceRequest.FromString(body)192                for url, body, _ in received193                if url == "/environment/v1/logs"194            ]195            logs = [196                record197                for request in log_requests198                for resource in request.resource_logs199                for scope in resource.scope_logs200                for record in scope.log_records201            ]202            assert len(logs) == 2 * multiplier, (mode, len(logs))203            assert all(204                record.trace_id in {span.trace_id for span in servers}205                for record in logs206            )207            metric_requests = [208                ExportMetricsServiceRequest.FromString(body)209                for url, body, _ in received210                if url == "/environment/v1/metrics"211            ]212            counts = [213                point.count214                for request in metric_requests215                for resource in request.resource_metrics216                for scope in resource.scope_metrics217                for metric in scope.metrics218                if metric.name == "http.server.request.duration"219                for point in getattr(metric, metric.WhichOneof("data")).data_points220            ]221            assert counts and counts[-1] == 2, counts222            assert all(223                headers.get("x-cloud") == "preserved"224                for url, _, headers in received225                if url.startswith("/environment/")226            )227            assert len(_runtime._configured) == (0 if mode == "logfire-opt-out" else 3)228            _runtime._shutdown()229            if mode in ("global", "explicit"):230                # FastAPI's cleanup closes its own exporters, leaving the provider and231                # previously installed vendor components usable.232                count = len(original_spans.get_finished_spans())233                with tp.get_tracer("vendor").start_as_current_span("still-running"):234                    pass235                assert len(original_spans.get_finished_spans()) == count + 1236                lp.get_logger("vendor").emit(body="still-running")237                assert len(original_logs.get_finished_logs()) == 3238                assert original_metrics.get_metrics_data() is not None239                tp.shutdown()240                mp.shutdown()241                lp.shutdown()242        finally:243            _runtime._shutdown()244            if mode.startswith("logfire"):245                logfire.shutdown()

Code quality findings 3

Ensure functions have docstrings for documentation
info maintainability missing-docstring
def capture_envelope(self, envelope):
Ensure functions have docstrings for documentation
info maintainability missing-docstring
def endpoint(item_id: int):
Ensure functions have docstrings for documentation
info maintainability missing-docstring
def spans_at(path):

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings