Skip to content

Configuration

Sentry

Sentry integration uses sentry_sdk package under the hood.

To bootstrap Sentry, you must provide at least:

  • sentry_dsn - tells sentry-sdk where to send the events.

Additional parameters can also be supplied through the settings object:

  • sentry_traces_sample_rate - in the range of 0.0 to 1.0, the percentage chance a given transaction will be sent
  • sentry_sample_rate - in the range of 0.0 to 1.0, the sample rate for error events
  • sentry_max_breadcrumbs - the total amount of breadcrumbs
  • sentry_max_value_length - the max event payload length
  • sentry_attach_stacktrace - if True, stack traces are automatically attached to all messages logged
  • sentry_auto_session_tracking - whether every request opens and closes a Sentry release-health Session (default: True), measured at ~7.7 µs per request. Set it to False if you do not use Sentry release health.
  • sentry_integrations - list of integrations to enable
  • sentry_logging_breadcrumb_level - the minimum standard-library log level recorded as a breadcrumb (default: logging.INFO). Passed as LoggingIntegration(level=...); see below.
  • sentry_tags - key/value string pairs that are both indexed and searchable
  • sentry_additional_params - additional params, which will be passed to sentry_sdk.init, overriding any of the above that map to the same keyword
  • sentry_default_integrations - whether to use sentry's default integrations (default: True)
  • sentry_before_send - optional callback chained after the built-in structlog enricher, passed to sentry_sdk.init(before_send=...)

Read more about sentry_sdk params here.

Sentry is the second most expensive instrument in the stack. Its cost comes almost entirely from the ASGI integration; see Performance.

Sentry logging integration

Unless sentry_integrations already contains a LoggingIntegration, lite-bootstrap appends one built as LoggingIntegration(level=sentry_logging_breadcrumb_level, sentry_logs_level=None). Its event handler keeps the sentry-sdk default (ERROR), so the only departure from sentry-sdk's own default integration is sentry_logs_level.

Turning sentry_logs_level off is free. lite-bootstrap never sets enable_logs, so Sentry Logs is off, but SentryLogsHandler.emit formats the record before it checks whether logs are enabled (getsentry/sentry-python#7402) - it formats every INFO+ record and discards the result. Dropping breadcrumbs as well, with sentry_logging_breadcrumb_level=None, saves more but costs you log breadcrumbs on error events, so it stays on by default. Both are measured on the performance page.

Two ways to opt out of the appended integration: supply your own LoggingIntegration in sentry_integrations, which lite-bootstrap leaves untouched, or set sentry_default_integrations=False, which suppresses it along with every other default integration. Under either, sentry_logging_breadcrumb_level is ignored and lite-bootstrap warns.

Prometheus

Prometheus is the cheapest of the three non-logging instruments; see Performance.

Prometheus is on by default when its extra is installed, serving metrics at prometheus_metrics_path (default: /metrics). Set prometheus_metrics_path to an empty string to disable it.

Additional parameters:

  • prometheus_metrics_include_in_schema.

Prometheus Litestar

Prometheus's integration for Litestar requires prometheus_client package.

Additional parameters for Litestar integration:

  • prometheus_group_path - defaults to True so the path metric label uses the route template (/users/{id}) instead of the raw URL, bounding metric cardinality. Set to False to record raw paths.
  • prometheus_additional_params - passed to litestar.plugins.prometheus.PrometheusConfig.

Prometheus FastStream

Prometheus's integration for FastStream requires prometheus_client package.

To bootstrap Prometheus for FastStream, you must provide at least one of:

  • prometheus_middleware_cls - the broker metrics middleware, e.g. faststream.redis.prometheus.RedisPrometheusMiddleware. It is constructed with the instrument's registry and added to every broker of the first broker's type. For an application with brokers of different types, pass a mapping from broker class to middleware class instead; see multiple brokers.
  • prometheus_collector_registry - a prometheus_client.CollectorRegistry of your own, used in place of the fresh one the instrument would otherwise build.

With neither, nothing would populate the registry, so the metrics endpoint is not mounted and the instrument is skipped. Unlike the other frameworks, FastStream serves a private registry rather than prometheus_client.REGISTRY, so an endpoint with no middleware and no injected registry would have nothing to report.

Prometheus FastAPI

Prometheus's integration for FastAPI uses prometheus_fastapi_instrumentator package.

Additional parameters for FastAPI integration:

  • prometheus_instrumentator_params - passed to prometheus_fastapi_instrumentator.Instrumentator
  • prometheus_instrument_params - passed to method Instrumentator(...).instrument
  • prometheus_expose_params - passed to method Instrumentator(...).expose.

Prometheus FastMCP

Prometheus's integration for FastMCP uses the prometheus_client and prometheus_fastapi_instrumentator packages, and serves prometheus_client.REGISTRY at prometheus_metrics_path.

Every ASGI application application.http_app() builds counts its requests in http_requests_total, http_request_duration_seconds and the instrumentator's other default metrics, labelled by route (handler="/mcp"); requests to unknown paths are grouped into handler="none", and the metrics route itself is not counted. Tool calls are counted in fastmcp_tool_calls_total{tool, status}, with status either success or error, and timed in fastmcp_tool_call_duration_seconds{tool}.

Additional parameters for FastMCP integration:

  • prometheus_instrumentator_params - passed to prometheus_fastapi_instrumentator.Instrumentator; excluded_handlers defaults to the metrics route.
  • prometheus_instrument_params - passed to method Instrumentator(...).instrument.
  • fastmcp_prometheus_tool_metrics_enabled - count and time tool calls (default: True).

Opentelemetry

To bootstrap Opentelemetry, you must provide at least one of:

  • opentelemetry_endpoint, for traces.
  • opentelemetry_metrics_endpoint, for metrics.
  • opentelemetry_log_traces=True, to log spans to stdout.

Additional parameters:

  • opentelemetry_service_name - if provided, will be passed to the Resource instead of service_name.
  • opentelemetry_container_name - will be passed to the Resource.
  • opentelemetry_endpoint - will be passed to OTLPSpanExporter as endpoint. Under opentelemetry_exporter_protocol="http" this is a full URL (e.g. http://collector:4318/v1/traces).
  • opentelemetry_namespace - will be passed to the Resource.
  • opentelemetry_exporter_protocol - OTLP exporter transport: "grpc" (default, needs the otl extra) or "http" (needs the otl-http extra, no grpcio - installable on free-threaded Python).
  • opentelemetry_insecure - whether the gRPC OTLP connection is insecure (default: True; gRPC only; for http the endpoint URL scheme carries security). While it is True, an endpoint pointing at a non-local host triggers a warning that telemetry is sent unencrypted.
  • opentelemetry_instrumentors - a list of extra instrumentors.
  • opentelemetry_log_traces - traces will be logged to stdout.
  • opentelemetry_sampler - an opentelemetry.sdk.trace.sampling.Sampler deciding which traces are recorded. Unset, the SDK's own default applies: parentbased_always_on, which records every trace that is not the child of a non-recording remote parent, unless OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG say otherwise.
  • opentelemetry_generate_health_check_spans - generate spans for health check handlers if True.
  • 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_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

Left unset, no MeterProvider is installed and the metrics signal stays off. Set it, and the FastAPI, Litestar, FastStream and FastMCP instrumentations start recording their duration histograms against it; until then they build those instruments against a no-op provider and the data goes nowhere.

The export interval is the SDK's, 60 seconds, overridable with OTEL_METRIC_EXPORT_INTERVAL.

If the Prometheus instrument is also configured, request metrics are recorded twice: once for the scrape endpoint and once for the OTLP pipeline, under different names. That is supported, and it is what you want when the two feed different backends, but pointing both at one backend double-counts.

config = FastAPIConfig(
    service_name="my-service",
    opentelemetry_endpoint="localhost:4317",
    opentelemetry_metrics_endpoint="localhost:4317",
)

Sampling is the cheapest way to cut what tracing costs: on a benchmark endpoint returning a constant, ParentBased(TraceIdRatioBased(0.01)) saved ~55 µs per request against the always-on default. OpenTelemetry is the most expensive instrument in the stack; see Performance.

from opentelemetry.sdk.trace.sampling import ParentBased, TraceIdRatioBased

config = FastAPIConfig(
    service_name="my-service",
    opentelemetry_endpoint="otl",
    opentelemetry_sampler=ParentBased(TraceIdRatioBased(0.01)),
)

For FastAPI and Litestar there is additionally:

  • opentelemetry_exclude_spans - drops the ASGI receive and/or send spans, leaving only the server span. Empty by default, which records all three. ["receive", "send"] is worth ~34 µs per request on the benchmark endpoint, at the cost of two thirds of the spans disappearing from your trace view.

For FastStream there is additionally:

  • opentelemetry_middleware_cls - the broker telemetry middleware, e.g. faststream.redis.opentelemetry.RedisTelemetryMiddleware, or a mapping from broker class to middleware class (see multiple brokers). You must provide it to trace broker messages. Without it the rest of the configuration above still applies: the exporter is built, the health-check span is recorded unless opentelemetry_generate_health_check_spans is False, and any opentelemetry_instrumentors are applied.

Resource

The Resource is built with the SDK's Resource.create, so OpenTelemetry resource detectors work with no lite-bootstrap setting. Name them, comma-separated, in OTEL_EXPERIMENTAL_RESOURCE_DETECTORS; each name is an entry point in the opentelemetry_resource_detector group. The SDK registers process, os and host; separately installed detector packages register their own names:

OTEL_EXPERIMENTAL_RESOURCE_DETECTORS=process,os

OTEL_RESOURCE_ATTRIBUTES is honoured as well.

The attributes lite-bootstrap sets (service.name, service.namespace, service.version, container.name) are merged last and override anything a detector or environment variable supplies. service_name defaults to "micro-service", so OTEL_SERVICE_NAME is overridden unless service_name is empty: set opentelemetry_service_name or service_name instead.

Pyroscope

Pyroscope integration uses pyroscope-io package under the hood. Install it with the pyroscope extra, e.g. lite-bootstrap[fastapi-all,pyroscope].

To bootstrap Pyroscope, you must provide at least:

  • pyroscope_endpoint - the Pyroscope server address (e.g. http://pyroscope:4040).

Additional parameters:

  • pyroscope_sample_rate - CPU profiling sample rate in Hz (default: 100).
  • pyroscope_tags - key/value string pairs attached to all profiles.
  • pyroscope_additional_params - additional params passed directly to pyroscope.configure.

When OpenTelemetry is also enabled, a PyroscopeSpanProcessor is automatically added to the tracer provider. It tags root spans with a pyroscope.profile.id attribute and sets Pyroscope thread tags so that traces and profiles can be linked in the Grafana UI.

pyroscope-io publishes no free-threaded wheels, so the pyroscope extra cannot install on free-threaded CPython (#171). Sentry's continuous profiler is pure Python and runs there; enable it through sentry_additional_params:

import sentry_sdk

config = FreeConfig(
    sentry_dsn="https://key@sentry.example.com/1",
    sentry_additional_params={"profile_session_sample_rate": 1.0, "profile_lifecycle": "manual"},
)
FreeBootstrapper(config).bootstrap()
sentry_sdk.profiler.start_profiler()

Read more about Sentry's profiling options here.

Structlog

Structlog is bootstrapped by default. To opt out, set logging_enabled=False.

Configuring it costs almost nothing; the cost arrives per log record, and most of it is Sentry's. See Performance.

Additional parameters:

  • logging_enabled - whether to configure structlog (default: True).
  • logging_log_level
  • logging_flush_level
  • logging_buffer_capacity
  • logging_extra_processors
  • logging_unset_handlers
  • logging_record_filters - standard-library logging.Filter instances to attach to named loggers (see below).
  • logging_time_stamper - a structlog.processors.TimeStamper instance controlling timestamp format (default: TimeStamper(fmt="iso")). Pass a custom instance to change the format or enable UTC:
import structlog
from lite_bootstrap import FastAPIConfig

config = FastAPIConfig(
    logging_time_stamper=structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=True),
)

Filtering standard-library log records

logging_record_filters attaches logging.Filter instances to loggers by name. A filter sees every record its logger emits, before any handler renders it, and may rewrite the record or return False to drop it. This lets you demote a third-party error you already handle:

import logging

from lite_bootstrap import FreeBootstrapper, FreeConfig


class ExpectedFailureFilter(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        if record.levelno == logging.ERROR and "expected failure" in record.getMessage():
            record.levelno = logging.WARNING
            record.levelname = "WARNING"
        return True


bootstrapper = FreeBootstrapper(
    bootstrap_config=FreeConfig(
        logging_record_filters={"some_library.worker": (ExpectedFailureFilter(),)},
    ),
)
  • Set both levelno and levelname when changing a level: handlers read them independently.
  • The logger name is exact. A filter on some_library never sees some_library.worker, and "" is the root logger, matching only records logged through root itself. That is standard logging behaviour: a logger consults its own filters, then its ancestors' handlers.
  • Teardown removes only what it attached, leaving filters your application put on the same logger in place.

Structlog Litestar

When using Litestar, the StructlogPlugin is automatically registered, which enables request.logger inside route handlers:

from litestar import Litestar, Request, get
from lite_bootstrap import LitestarConfig, LitestarBootstrapper


@get("/")
async def handler(request: Request) -> dict[str, str]:
    request.logger.info("handling request")
    return {"status": "ok"}

Additional parameters for Litestar's access-log middleware:

  • litestar_logging_middleware_enabled - turn on request/response access logging (default: False).
  • litestar_logging_middleware_config - a caller-supplied LoggingMiddlewareConfig that replaces the built-in defaults wholesale. It is ignored, with a warning, unless litestar_logging_middleware_enabled is True.

See the Litestar integration guide for what gets logged and why access logging defaults to off.

Structlog FastAPI

FastAPI ships no access log of its own, so lite-bootstrap provides one. It is off by default:

  • fastapi_logging_middleware_enabled - turn on the structured access log (default: False).

See the FastAPI integration guide for what gets logged and why it defaults to off.

Structlog FastMCP

The per-MCP-message access log is off by default:

  • fastmcp_logging_middleware_enabled - turn on the access log (default: False).

See the FastMCP integration guide for what gets logged.

Structlog FastStream

When using FastStream, the structlog logger is automatically injected into the broker so that all broker service messages (e.g. "Received", "Processed") are routed through structlog.

The broker log level is controlled independently from the application log level:

  • faststream_log_level - log level for FastStream broker service messages (default: logging.WARNING).

This allows you to suppress broker noise while keeping your application logs at a lower level:

import logging
from lite_bootstrap import FastStreamConfig

config = FastStreamConfig(
    logging_log_level=logging.INFO,  # your application logs
    faststream_log_level=logging.WARNING,  # broker "Received"/"Processed" messages (default)
)

CORS

To bootstrap CORS headers, you must provide cors_allowed_origins or cors_allowed_origin_regex.

Additional params:

  • cors_allowed_methods
  • cors_allowed_headers
  • cors_exposed_headers
  • cors_allowed_credentials
  • cors_max_age

Swagger

To bootstrap swagger, you have the following parameters:

  • swagger_static_path - path for offline docs static
  • swagger_path
  • swagger_offline_docs - option to turn on offline docs.

Swagger is on by default at swagger_path (default: /docs). For Litestar, set swagger_path to an empty string to disable it.

Health checks

Health checks are on by default; set health_checks_enabled=False to disable them.

Additional params:

  • health_checks_path
  • health_checks_include_in_schema
  • health_checks_probes: named async callables, each returning bool, that the route runs on every request

The health check route on Litestar, FastAPI, FastMCP and FastStream runs every probe concurrently. On FastStream the probes run alongside the broker pings. The route returns 200 with health_status: true when everything passes. If any probe returns False or raises, the route returns 500 with the same JSON body and health_status: false. Each failed probe logs one warning with its name; the response body does not say which probe failed.

import functools

import sqlalchemy

from lite_bootstrap import LitestarConfig


async def database_probe() -> bool:
    async with engine.connect() as connection:
        await connection.execute(sqlalchemy.text("SELECT 1"))
    return True


config = LitestarConfig(
    health_checks_probes={
        "database": database_probe,
        "outbox-broker": functools.partial(outbox_broker.ping, timeout=5),
    },
)

lite-bootstrap puts no timeout on probes, so a probe that hangs makes the route hang too. Give each probe its own timeout, as functools.partial(broker.ping, timeout=5) does.

Be careful with probes on dependencies outside the service, such as a database, when the route also serves a Kubernetes liveness probe. During the database outage every pod fails its liveness check and gets restarted, and the restarts add load once the database comes back. Readiness probes are the usual place for dependency checks.

Skipped instruments

When a bootstrapper is constructed, each registered instrument is checked twice:

  1. is_configured(config) (classmethod, runs before instantiation) returns False if the user's config indicates this instrument shouldn't run (e.g. sentry_dsn empty, logging_enabled=False, pyroscope_endpoint empty). When False, the instrument is silently skipped and recorded in bootstrapper.skipped_instruments: list[tuple[type, str]]. Each entry is the instrument class plus its not_configured_reason.

  2. dependencies_installed() runs only if is_configured() returned True. If the instrument's optional package is missing, an InstrumentDependencyMissingWarning is emitted, since a configured instrument with a missing dependency is usually a deployment mistake.

After the loop, the bootstrapper emits one INFO-level summary log listing configured + skipped instruments. Default Python logging suppresses INFO; opt in via logging.basicConfig(level=logging.INFO).

Filter the dep-missing warning the same way as any UserWarning:

import warnings
from lite_bootstrap import InstrumentDependencyMissingWarning

warnings.filterwarnings("ignore", category=InstrumentDependencyMissingWarning)

InstrumentSkippedWarning is kept as the base class for forward-compatibility (additional skip categories may emerge); today, InstrumentDependencyMissingWarning is its only concrete subclass.

To inspect skipped instruments programmatically:

bootstrapper = FastAPIBootstrapper(bootstrap_config=config)
for cls, reason in bootstrapper.skipped_instruments:
    print(f"{cls.__name__}: {reason}")

To get a human-readable view of the same information at any later point (e.g. for debugging from a REPL or a health endpoint), call bootstrapper.build_summary(). It returns the multi-line string that the INFO summary log emits, which helps when log levels are filtered or when you want to render the bootstrapper state inline.