186 lines
6.2 KiB
Python
186 lines
6.2 KiB
Python
import importlib
|
|
|
|
from fastapi import FastAPI
|
|
|
|
from app.config import ObservabilityConfig
|
|
|
|
|
|
def _reload_tracing_module():
|
|
tracing_module = importlib.import_module("app.runtime.tracing")
|
|
return importlib.reload(tracing_module)
|
|
|
|
|
|
def _make_observability_config(**overrides) -> ObservabilityConfig:
|
|
payload = {
|
|
"enabled": True,
|
|
"service_name": "delivery-aggregator",
|
|
"otlp_endpoint": "http://collector:4317",
|
|
"otlp_insecure": True,
|
|
}
|
|
payload.update(overrides)
|
|
return ObservabilityConfig(**payload)
|
|
|
|
|
|
def test_configure_tracing_skips_bootstrap_when_disabled(monkeypatch) -> None:
|
|
tracing_module = _reload_tracing_module()
|
|
calls = {"provider": 0, "fastapi": 0, "httpx": 0, "redis": 0}
|
|
|
|
def fake_get_or_create_tracer_provider(_observability):
|
|
calls["provider"] += 1
|
|
return object()
|
|
|
|
def fake_instrument_fastapi_app(_app, *, tracer_provider) -> None:
|
|
del tracer_provider
|
|
calls["fastapi"] += 1
|
|
|
|
def fake_instrument_httpx(*, tracer_provider) -> None:
|
|
del tracer_provider
|
|
calls["httpx"] += 1
|
|
|
|
def fake_instrument_redis(*, tracer_provider) -> None:
|
|
del tracer_provider
|
|
calls["redis"] += 1
|
|
|
|
monkeypatch.setattr(
|
|
tracing_module,
|
|
"_get_or_create_tracer_provider",
|
|
fake_get_or_create_tracer_provider,
|
|
)
|
|
monkeypatch.setattr(
|
|
tracing_module,
|
|
"_instrument_fastapi_app",
|
|
fake_instrument_fastapi_app,
|
|
)
|
|
monkeypatch.setattr(tracing_module, "_instrument_httpx", fake_instrument_httpx)
|
|
monkeypatch.setattr(tracing_module, "_instrument_redis", fake_instrument_redis)
|
|
|
|
tracing_module.configure_tracing(
|
|
FastAPI(),
|
|
_make_observability_config(enabled=False),
|
|
)
|
|
|
|
assert calls == {"provider": 0, "fastapi": 0, "httpx": 0, "redis": 0}
|
|
|
|
|
|
def test_build_tracer_provider_uses_otlp_exporter_and_service_name(monkeypatch) -> None:
|
|
tracing_module = _reload_tracing_module()
|
|
created: dict[str, object] = {}
|
|
|
|
class FakeExporter:
|
|
def __init__(self, *, endpoint: str, insecure: bool) -> None:
|
|
created["endpoint"] = endpoint
|
|
created["insecure"] = insecure
|
|
|
|
class FakeBatchSpanProcessor:
|
|
def __init__(self, exporter: object) -> None:
|
|
self.exporter = exporter
|
|
created["processor_exporter"] = exporter
|
|
|
|
class FakeTracerProvider:
|
|
def __init__(self, *, resource) -> None:
|
|
self.resource = resource
|
|
self.span_processors: list[object] = []
|
|
|
|
def add_span_processor(self, processor: object) -> None:
|
|
self.span_processors.append(processor)
|
|
|
|
monkeypatch.setattr(tracing_module, "OTLPSpanExporter", FakeExporter)
|
|
monkeypatch.setattr(tracing_module, "BatchSpanProcessor", FakeBatchSpanProcessor)
|
|
monkeypatch.setattr(tracing_module, "TracerProvider", FakeTracerProvider)
|
|
|
|
provider = tracing_module._build_tracer_provider(
|
|
_make_observability_config(
|
|
service_name="g2s-aggregator",
|
|
otlp_endpoint="http://trace-backend:4317",
|
|
otlp_insecure=False,
|
|
)
|
|
)
|
|
|
|
assert created["endpoint"] == "http://trace-backend:4317"
|
|
assert created["insecure"] is False
|
|
assert provider.resource.attributes["service.name"] == "g2s-aggregator"
|
|
assert len(provider.span_processors) == 1
|
|
assert created["processor_exporter"] is provider.span_processors[0].exporter
|
|
|
|
|
|
def test_configure_tracing_reuses_provider_and_global_instrumentation(monkeypatch) -> None:
|
|
tracing_module = _reload_tracing_module()
|
|
created = {
|
|
"exporters": 0,
|
|
"providers": 0,
|
|
"set_provider_calls": 0,
|
|
"fastapi_apps": [],
|
|
"httpx_calls": 0,
|
|
"redis_calls": 0,
|
|
}
|
|
|
|
class FakeExporter:
|
|
def __init__(self, *, endpoint: str, insecure: bool) -> None:
|
|
created["exporters"] += 1
|
|
self.endpoint = endpoint
|
|
self.insecure = insecure
|
|
|
|
class FakeBatchSpanProcessor:
|
|
def __init__(self, exporter: object) -> None:
|
|
self.exporter = exporter
|
|
|
|
class FakeTracerProvider:
|
|
def __init__(self, *, resource) -> None:
|
|
created["providers"] += 1
|
|
self.resource = resource
|
|
self.span_processors: list[object] = []
|
|
|
|
def add_span_processor(self, processor: object) -> None:
|
|
self.span_processors.append(processor)
|
|
|
|
class FakeInstrumentor:
|
|
def __init__(self, kind: str) -> None:
|
|
self.kind = kind
|
|
self.is_instrumented_by_opentelemetry = False
|
|
|
|
def instrument(self, **kwargs) -> None:
|
|
assert kwargs["tracer_provider"] is not None
|
|
created[f"{self.kind}_calls"] += 1
|
|
self.is_instrumented_by_opentelemetry = True
|
|
|
|
class FakeFastAPIInstrumentor:
|
|
@staticmethod
|
|
def instrument_app(app: FastAPI, *, tracer_provider) -> None:
|
|
assert tracer_provider is not None
|
|
created["fastapi_apps"].append(app)
|
|
app._is_instrumented_by_opentelemetry = True
|
|
|
|
def fake_set_tracer_provider(_provider: object) -> None:
|
|
created["set_provider_calls"] += 1
|
|
|
|
monkeypatch.setattr(tracing_module, "OTLPSpanExporter", FakeExporter)
|
|
monkeypatch.setattr(tracing_module, "BatchSpanProcessor", FakeBatchSpanProcessor)
|
|
monkeypatch.setattr(tracing_module, "TracerProvider", FakeTracerProvider)
|
|
monkeypatch.setattr(
|
|
tracing_module,
|
|
"_HTTPX_INSTRUMENTOR",
|
|
FakeInstrumentor("httpx"),
|
|
)
|
|
monkeypatch.setattr(
|
|
tracing_module,
|
|
"_REDIS_INSTRUMENTOR",
|
|
FakeInstrumentor("redis"),
|
|
)
|
|
monkeypatch.setattr(tracing_module, "FastAPIInstrumentor", FakeFastAPIInstrumentor)
|
|
monkeypatch.setattr(tracing_module.trace, "set_tracer_provider", fake_set_tracer_provider)
|
|
|
|
app_one = FastAPI()
|
|
app_two = FastAPI()
|
|
observability = _make_observability_config()
|
|
|
|
tracing_module.configure_tracing(app_one, observability)
|
|
tracing_module.configure_tracing(app_one, observability)
|
|
tracing_module.configure_tracing(app_two, observability)
|
|
|
|
assert created["exporters"] == 1
|
|
assert created["providers"] == 1
|
|
assert created["set_provider_calls"] == 1
|
|
assert created["fastapi_apps"] == [app_one, app_two]
|
|
assert created["httpx_calls"] == 1
|
|
assert created["redis_calls"] == 1
|