Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/introduction/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ Additional parameters:
health-check path. While it is `False`, OpenTelemetry's HTTP metrics also leave health-check
requests out, because the instrumentation skips an excluded URL entirely. Set it to `True` to
trace probes, for example to watch their latency.
- `opentelemetry_excluded_urls` - extra regexes for URLs excluded from tracing; the metrics path and (unless health-check spans are enabled) the health-check path are excluded automatically. Litestar searches them in the request path, FastAPI and FastMCP in the full URL. The environment variable OpenTelemetry defines for the framework (`OTEL_PYTHON_LITESTAR_EXCLUDED_URLS`, `OTEL_PYTHON_FASTAPI_EXCLUDED_URLS` or `OTEL_PYTHON_STARLETTE_EXCLUDED_URLS` for FastMCP), falling back to `OTEL_PYTHON_EXCLUDED_URLS`, adds to them.
- `opentelemetry_excluded_urls` - extra regexes for URLs excluded from tracing; the metrics path and (unless health-check spans are enabled) the health-check path are excluded automatically. Litestar searches them in the request path, FastAPI and FastMCP in the full URL. FastAPI and FastMCP raise `ConfigurationError` at config construction if an entry contains a comma because OpenTelemetry uses commas to separate patterns. Litestar accepts commas in config entries. The environment variable OpenTelemetry defines for the framework (`OTEL_PYTHON_LITESTAR_EXCLUDED_URLS`, `OTEL_PYTHON_FASTAPI_EXCLUDED_URLS` or `OTEL_PYTHON_STARLETTE_EXCLUDED_URLS` for FastMCP), falling back to `OTEL_PYTHON_EXCLUDED_URLS`, adds to them.
- `opentelemetry_metrics_endpoint` - will be passed to `OTLPMetricExporter` as endpoint, and turns on the metrics signal. Its own field rather than a flag on `opentelemetry_endpoint`, so upgrading never starts exporting metrics you did not ask for. Under `opentelemetry_exporter_protocol="http"` this is a full URL (e.g. `http://collector:4318/v1/metrics`); `opentelemetry_exporter_protocol` and `opentelemetry_insecure` are shared with traces.

### Metrics
Expand Down
1 change: 1 addition & 0 deletions lite_bootstrap/bootstrappers/fastapi_bootstrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class FastAPIConfig(
prometheus_instrument_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
prometheus_expose_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
logging_access_log_enabled: bool = False
_opentelemetry_exclusions_use_comma_separator: typing.ClassVar[bool] = True
_sentry_lean_replaced_integrations: typing.ClassVar[Mapping[str, str]] = {
"starlette": "StarletteIntegration",
"fastapi": "FastApiIntegration",
Expand Down
1 change: 1 addition & 0 deletions lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class FastMcpConfig(
fastmcp_prometheus_tool_metrics_enabled: bool = True
prometheus_instrumentator_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
prometheus_instrument_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict)
_opentelemetry_exclusions_use_comma_separator: typing.ClassVar[bool] = True
_sentry_lean_replaced_integrations: typing.ClassVar[Mapping[str, str]] = {"starlette": "StarletteIntegration"}


Expand Down
12 changes: 11 additions & 1 deletion lite_bootstrap/instruments/opentelemetry_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import urllib.parse

from lite_bootstrap import import_checker
from lite_bootstrap.exceptions import InstrumentDependencyMissingWarning, collect_teardown_errors
from lite_bootstrap.exceptions import ConfigurationError, InstrumentDependencyMissingWarning, collect_teardown_errors
from lite_bootstrap.helpers.warn import warn_at_caller
from lite_bootstrap.instruments.base import BaseConfig, BaseInstrument

Expand Down Expand Up @@ -137,6 +137,16 @@ class OpenTelemetryAsgiConfig(OpenTelemetryConfig):
default_factory=lambda: ["receive", "send"]
)

_opentelemetry_exclusions_use_comma_separator: typing.ClassVar[bool] = False

def __post_init__(self) -> None:
super().__post_init__()
if self._opentelemetry_exclusions_use_comma_separator:
for pattern in self.opentelemetry_excluded_urls:
if "," in pattern:
msg = f"opentelemetry_excluded_urls entry {pattern!r} contains a comma, which separates patterns"
raise ConfigurationError(msg)


if import_checker.is_opentelemetry_sdk_installed and import_checker.is_pyroscope_installed:
_OTEL_PROFILE_ID_KEY: typing.Final = "pyroscope.profile.id"
Expand Down
12 changes: 11 additions & 1 deletion tests/test_fastapi_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import dataclasses
import json
import logging
import re
import typing
import uuid
import warnings
Expand All @@ -20,7 +21,7 @@
from starlette import status
from starlette.testclient import TestClient

from lite_bootstrap import FastAPIBootstrapper, FastAPIConfig, import_checker
from lite_bootstrap import FastAPIBootstrapper, FastAPIConfig, FastMcpConfig, import_checker
from lite_bootstrap.bootstrappers import fastapi_bootstrapper
from lite_bootstrap.exceptions import ConfigurationError, InstrumentDependencyMissingWarning
from lite_bootstrap.instruments.healthchecks_instrument import HealthProbe
Expand All @@ -45,6 +46,15 @@
std_logger = logging.getLogger()


@pytest.mark.parametrize("config_class", [FastAPIConfig, FastMcpConfig])
def test_config_rejects_comma_in_opentelemetry_excluded_url(
config_class: type[FastAPIConfig] | type[FastMcpConfig],
) -> None:
pattern = r"/v\d{1,2}/ping$"
with pytest.raises(ConfigurationError, match=re.escape(repr(pattern))):
config_class(opentelemetry_excluded_urls=[pattern])


@pytest.fixture
def fastapi_config() -> FastAPIConfig:
return FastAPIConfig(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_litestar_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ async def get_item(item_id: int) -> dict[str, int]:
config = dataclasses.replace(
litestar_config,
application_config=AppConfig(route_handlers=[get_item]),
opentelemetry_excluded_urls=[r"/items/\d+$"],
opentelemetry_excluded_urls=[r"/items/\d{1,2}$"],
)
application = LitestarBootstrapper(bootstrap_config=config).bootstrap()

Expand Down
Loading