From 7a94ae89f236b571830a55d70fa39382c1a5f76c Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Wed, 4 Nov 2026 13:27:40 +0300 Subject: [PATCH 1/2] docs: complete the 2.0 migration guide and release notes (#404) --- .github/release-notes/2.0.0.md | 42 ++++ .github/workflows/release.yml | 11 +- README.md | 1 + docs/migration/to-2.x.md | 448 ++++++++++++++++++++++++++++++--- 4 files changed, 462 insertions(+), 40 deletions(-) create mode 100644 .github/release-notes/2.0.0.md diff --git a/.github/release-notes/2.0.0.md b/.github/release-notes/2.0.0.md new file mode 100644 index 0000000..ac1d08c --- /dev/null +++ b/.github/release-notes/2.0.0.md @@ -0,0 +1,42 @@ +## Upgrading from 1.x + +Read the [2.x migration guide](https://github.com/modern-python/lite-bootstrap/blob/main/docs/migration/to-2.x.md) +for replacement APIs, configuration examples, and actions for each affected framework. + +## Breaking changes + +- Raise Sentry to `>=2.70,<3`; explicit `data_collection` replaces SDK event scrubbing and `send_default_pii` opt-ins (#359, #379). +- Default Sentry session tracking to off and log breadcrumbs to `WARNING` (#360). +- Use lean request error capture on Litestar, FastAPI, and FastMCP when tracing is off; stop creating request transactions and attach only bodies read by handlers. Litestar handled 4xx exceptions are no longer reported (#361, #362). +- Leave Sentry auto-enabling integrations off while tracing is off, subject to the HTTP lean-capture policy (#363). +- Omit OpenTelemetry ASGI receive/send spans by default (#364). +- Suppress duplicate Prometheus HTTP series when OpenTelemetry HTTP metrics are configured (#365). +- Omit health requests from OpenTelemetry spans and HTTP metrics by default (#366). +- Remove deprecated imports, config fields, logging protocols, and custom instrument member aliases (#367). +- Raise OpenTelemetry API/SDK/exporters to 1.42 and instrumentation to 0.63b0 (#368). +- Default `service_debug` to `False` and preserve explicitly configured Litestar debug mode (#369). +- Return 503, rather than 500, for unhealthy health checks (#370). +- Require Litestar 2.22 or later for all Litestar extras (#371). +- Default `opentelemetry_insecure` to `None`: environment settings apply, and schemeless gRPC endpoints use TLS unless overridden (#372). +- Default service identity fields to `None`, honor `OTEL_SERVICE_NAME`, omit unset resource versions, and use an explicit `service_version` as the Sentry release (#373). +- Require FastMCP `>=4.0,<5`; access logs now include notifications and pre-handler failures (#374). +- Make FastStream Prometheus routes and middleware opt-in through a non-empty metrics path, and use the default collector registry (#375). +- Require both application and config type arguments for `BaseBootstrapper` subclasses (#376). +- Remove logging buffer settings and write structlog output immediately (#378). +- Remove `MemoryLoggerFactory` and its instrument property; propagate structlog and framework loggers to root and inherit root levels (#405). +- Roll back failed bootstrap and reject reuse of the failed instance (#406). +- Normalize and redact structured Sentry events/breadcrumbs before callbacks and omit their frame locals (#408). +- Isolate Sentry context per FastStream message (#409). +- Enforce Sentry request body capture limits in lean capture (#410). +- Tear down FastAPI and FastMCP observability after user lifespan shutdown (#412, #413). +- Preserve application root handlers and restore the previous root level during logging teardown (#415). +- Reject commas in FastAPI and FastMCP OpenTelemetry URL exclusion patterns (#417). +- Respect disabled FastAPI offline docs routes independently (#418). +- Reject invalid non-empty health and metrics paths at config construction (#419). +- Serve both FastStream health/metrics slash spellings; review alternate-path custom routes (#420). +- Default CORS methods to `["*"]` on FastAPI and Litestar when origins are configured (#423). +- Exclude health and scrape traffic from Prometheus HTTP metrics (#424). +- Omit JSON log `tracing` outside a recording span (#425). +- Remove `InstrumentSkippedWarning`; use `InstrumentDependencyMissingWarning` directly (#427). +- Silently skip the default Prometheus path when `prometheus_client` is missing; custom paths still warn (#428). +- Skip SDK tracing without a span processor; install externally owned providers before bootstrap to preserve custom tracing (#430). diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff28385..cf66736 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,15 +32,15 @@ jobs: # Publisher on the lite-bootstrap PyPI project (env: pypi, workflow: release.yml). - run: just publish - # The Release body is GitHub's generated notes, rendered from the squashed - # PR titles since the previous tag — so a conventional-commit title is what - # a reader gets. A release wanting prose is edited after the fact with - # `gh release edit --notes-file`. A tag with a letter (2.0.0rc1) is a - # pre-release -> flagged so GitHub won't mark it "Latest". + # Versioned notes supplement GitHub's generated PR list when present. - name: Resolve release metadata id: meta run: | set -euo pipefail + notes_file=".github/release-notes/$GITHUB_REF_NAME.md" + if [[ -f "$notes_file" ]]; then + echo "notes_file=$notes_file" >> "$GITHUB_OUTPUT" + fi if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then echo "prerelease=true" >> "$GITHUB_OUTPUT" else @@ -50,6 +50,7 @@ jobs: - name: Publish GitHub Release uses: softprops/action-gh-release@v3 with: + body_path: ${{ steps.meta.outputs.notes_file }} generate_release_notes: true prerelease: ${{ steps.meta.outputs.prerelease }} draft: false diff --git a/README.md b/README.md index 4a692bb..a473f23 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ The following intentionally differ: ## Links - [Documentation](https://lite-bootstrap.modern-python.org) +- [Upgrading to 2.x](https://github.com/modern-python/lite-bootstrap/blob/main/docs/migration/to-2.x.md) - [PyPI](https://pypi.org/project/lite-bootstrap) - [License](https://github.com/modern-python/lite-bootstrap/blob/main/LICENSE) diff --git a/docs/migration/to-2.x.md b/docs/migration/to-2.x.md index 4e17b53..cc238ea 100644 --- a/docs/migration/to-2.x.md +++ b/docs/migration/to-2.x.md @@ -1,37 +1,76 @@ -# Migration guide for lite-bootstrap 2.x +# Migration guide: upgrading to lite-bootstrap 2.x -## CORS methods on FastAPI and Litestar +This guide covers changes since 1.16.0. Upgrade your dependency constraints first, +then review the sections for the instruments and frameworks your service uses. +The [2.0.0 release notes](https://github.com/modern-python/lite-bootstrap/releases/tag/2.0.0) +provide the release checklist. Configuration snippets below are independent examples; +use your own DSN, application, exporter, and service identity where applicable. + +## Dependency constraints + +Update lockfiles and any direct pins to match these supported versions: + +| Dependency | 2.x constraint | Applies to | +| --- | --- | --- | +| `sentry-sdk` | `>=2.70,<3` | Sentry extras | +| `opentelemetry-api`, `opentelemetry-sdk`, OTLP exporters | `>=1.42` | `otl` and `otl-http` extras | +| `opentelemetry-instrumentation` and framework instrumentation | `>=0.63b0` | OpenTelemetry extras | +| `litestar` | `>=2.22` | All Litestar extras | +| `fastmcp` | `>=4.0,<5` | All FastMCP extras | -In 2.x, `cors_allowed_methods` defaults to `["*"]`. If you configure allowed -origins and omit the method list, preflight requests can now succeed for all -standard HTTP methods, including POST. +Sentry 3 is not supported. FastMCP 3 pins must move to 4; see its +[upgrade guide](https://github.com/PrefectHQ/fastmcp/blob/main/docs/getting-started/upgrading/from-fastmcp-3.mdx) +for changes in FastMCP itself. With access logging configured, notifications and +pre-handler failures now produce records too; review log-volume assumptions. +These constraints come from +[#359](https://github.com/modern-python/lite-bootstrap/pull/359), +[#368](https://github.com/modern-python/lite-bootstrap/pull/368), +[#371](https://github.com/modern-python/lite-bootstrap/pull/371), +[#374](https://github.com/modern-python/lite-bootstrap/pull/374), and +[#379](https://github.com/modern-python/lite-bootstrap/pull/379). -Set `cors_allowed_methods` explicitly to restrict cross-origin requests to the -methods your application supports, such as `["GET", "POST"]`. +## Removed names and config fields -`cors_allowed_headers` defaults to `[]`. Both frameworks allow their built-in -CORS headers, including `Content-Type`. Add headers such as `Authorization` -explicitly if your application needs them. +Aliases deprecated in 1.x have been removed +([#367](https://github.com/modern-python/lite-bootstrap/pull/367)). Update imports, +attribute access, config constructor arguments, and keys in external settings sources: -## Prometheus HTTP metrics on FastAPI, FastMCP, and Litestar +| Removed name | Replacement | +| --- | --- | +| `FreeBootstrapperConfig` (package root or `bootstrappers.free_bootstrapper`) | `FreeConfig` | +| `OpentelemetryConfig` (`instruments.opentelemetry_instrument`) | `OpenTelemetryConfig` | +| `IGNORED_STRUCTLOG_ATTRIBUTES` (`instruments.sentry_instrument`) | `STRUCTLOG_META_KEYS` from `instruments.logging_factory` | +| `AddressProtocol`, `RequestProtocol`, `ScopeType` (logging modules) | Define the structural types your application needs | +| `prometheus_instrumentator_params` (FastAPI/FastMCP) | `prometheus_instrumentator_additional_params` | +| `prometheus_instrument_params` (FastAPI/FastMCP) | `prometheus_instrument_additional_params` | +| `prometheus_expose_params` (FastAPI) | `prometheus_expose_additional_params` | +| `swagger_extra_params` (Litestar) | `swagger_additional_params` | +| `fastapi_logging_middleware_enabled` | `logging_access_log_enabled` | +| `litestar_logging_middleware_enabled` | `logging_access_log_enabled` | +| `fastmcp_logging_middleware_enabled` | `logging_access_log_enabled` | +| Custom instrument `not_ready_message` | `not_configured_reason` | +| Custom instrument `check_dependencies` | `dependencies_installed` | -Health checks and metrics scrapes are excluded from Prometheus HTTP request -metrics. Existing user-defined exclusions still apply. Dashboards and alerts -that previously included these requests may show lower request totals and -different latency percentiles. Review thresholds that were based on the old -traffic mix. +Removed imports fail with `ImportError`; removed attributes fail with `AttributeError`. +Removed constructor keywords fail with `TypeError`. `from_dict()` and `from_object()` +still ignore unknown keys, so old settings keys can silently fall back to defaults. +Rename those keys before upgrading. Custom instruments using the old dependency +method can be instantiated despite a missing package; update both class members. -## Trace context in JSON logs +For example, replace `FreeBootstrapperConfig(...)` with: -JSON log lines include `tracing` only when emitted inside a recording -OpenTelemetry span. The object contains `span_id` and `trace_id`. -Update log parsers to treat `tracing` as optional. +```python +from lite_bootstrap import FreeConfig + +config = FreeConfig(logging_enabled=False) +``` -## Missing-dependency warning category +### Missing-dependency warning category -`InstrumentSkippedWarning` has been removed. Importing it from `lite_bootstrap` -now raises `ImportError`. Replace imports and warning filters with -`InstrumentDependencyMissingWarning`, which now derives directly from `UserWarning`: +`InstrumentSkippedWarning` has also been removed +([#427](https://github.com/modern-python/lite-bootstrap/pull/427)). Replace imports +and warning filters with `InstrumentDependencyMissingWarning`, which derives directly +from `UserWarning`: ```python import warnings @@ -41,21 +80,360 @@ from lite_bootstrap import InstrumentDependencyMissingWarning warnings.filterwarnings("error", category=InstrumentDependencyMissingWarning) ``` -Unconfigured instruments continue to be skipped silently. A configured instrument -with a missing optional dependency emits `InstrumentDependencyMissingWarning`. +Unconfigured instruments are skipped silently. A configured instrument with a missing +optional dependency warns. With `prometheus_client` missing, the default metrics path +now skips silently too, including an explicitly supplied `/metrics` on FastAPI, +Litestar, or FastMCP. A custom path still warns; install the framework's metrics extra +if you expect a scrape route +([#428](https://github.com/modern-python/lite-bootstrap/pull/428)). + +### Custom bootstrappers + +`BaseBootstrapper` now takes the application and config types. Change +`BaseBootstrapper[MyApp]` to `BaseBootstrapper[MyApp, MyConfig]`; the old form raises +`TypeError` when the class is defined +([#376](https://github.com/modern-python/lite-bootstrap/pull/376)). For example: + +```python +from lite_bootstrap import FreeConfig +from lite_bootstrap.bootstrappers.base import BaseBootstrapper + + +class MyBootstrapper(BaseBootstrapper[object, FreeConfig]): + pass +``` + +## Service identity and debug mode + +`service_debug` defaults to `False`. FastAPI apps created by lite-bootstrap and +Litestar apps no longer show debug tracebacks unless explicitly requested. Set +`service_debug=True` for development; Litestar also preserves an explicit +`AppConfig(debug=True)` +([#369](https://github.com/modern-python/lite-bootstrap/pull/369)). + +`service_name` and `service_version` now default to `None` +([#373](https://github.com/modern-python/lite-bootstrap/pull/373)): + +- OpenTelemetry resolves an unset name from `OTEL_SERVICE_NAME`, then + `service.name` in `OTEL_RESOURCE_ATTRIBUTES`, otherwise the SDK fallback + `unknown_service` (with an executable suffix when available), instead of + `micro-service`. An explicit `opentelemetry_service_name` still takes precedence + over `service_name`. +- An unset version is omitted from the OpenTelemetry resource unless supplied by + `OTEL_RESOURCE_ATTRIBUTES`. +- An explicit `service_version` becomes Sentry's release, overriding SDK discovery, + including `SENTRY_RELEASE`. Use `sentry_additional_params["release"]` to override it. +- Code reading the config can receive `None`. Use `service_name_or_default` and + `service_version_or_default` for the previous fallback values. Health responses + and framework metadata continue to use these fallbacks. + +Set explicit identity fields to retain the old telemetry resource values: + +```python +from lite_bootstrap import FreeConfig + +config = FreeConfig(service_name="micro-service", service_version="1.0.0") +``` + +Review Sentry release grouping when setting a version explicitly. + +## Logging + +### Immediate output and root propagation + +`logging_buffer_capacity` and `logging_flush_level` were removed. Delete those +constructor arguments and settings keys. Structlog lines are written immediately, +in order with stdlib lines, rather than waiting for a buffer flush +([#378](https://github.com/modern-python/lite-bootstrap/pull/378)). + +`MemoryLoggerFactory` and `LoggingInstrument.memory_logger_factory` were removed. +Structlog uses the standard-library logger factory and propagates to root. Loggers +used by structlog inherit root's level instead of receiving their own. Stdlib records +on those names and their children are now JSON lines. Litestar and FastStream loggers +also propagate to root while bootstrapped +([#405](https://github.com/modern-python/lite-bootstrap/pull/405)). + +Remove code inspecting the old factory or per-logger handlers. Review your parent +and root handlers for duplicate output, and set explicit named-logger levels if you +need them to differ from `logging_log_level`. + +### Teardown preserves application logging + +Logging teardown now removes only its own root handler and restores the root level +from before bootstrap. Other root handlers survive. If your application relied on +teardown to close its handlers or set root to `WARNING`, do that explicitly. +Removals requested through `logging_unset_handlers` remain permanent +([#415](https://github.com/modern-python/lite-bootstrap/pull/415)). + +### Trace context in JSON logs + +JSON log lines include `tracing` only inside a recording OpenTelemetry span; the +object contains `span_id` and `trace_id`. Outside one, the key is absent instead of +an empty object. Update log parsers to treat `tracing` as optional +([#425](https://github.com/modern-python/lite-bootstrap/pull/425)). + +## Sentry + +### Data collection and request bodies + +lite-bootstrap passes an explicit `data_collection` policy. Sentry's general event +scrubber is bypassed by the SDK for that policy; headers, cookies, query parameters, +and frame locals instead use collection denylists. `send_default_pii=True` alone no +longer opts into collection. User information and HTTP bodies are off by default +([#359](https://github.com/modern-python/lite-bootstrap/pull/359)). + +Review any custom scrubbing and opt-ins. To collect user information or incoming +request bodies, replace the relevant policy entries while retaining the others: + +```python +from lite_bootstrap import FastAPIConfig +from lite_bootstrap.instruments.sentry_instrument import DEFAULT_DATA_COLLECTION + +config = FastAPIConfig( + sentry_additional_params={ + "data_collection": { + **DEFAULT_DATA_COLLECTION, + "user_info": True, + "http_bodies": ["incoming_request"], + }, + "max_request_body_size": "small", + }, +) +``` + +Enable only the data your service intends to collect. Lean capture attaches a body +only after the handler reads it; FastAPI and FastMCP require the complete body. +Raw bodies are text rather than a raw-data annotation. Request body size policy is +also enforced by lean capture +([#410](https://github.com/modern-python/lite-bootstrap/pull/410)). See +[Sentry data collection](../introduction/configuration.md#sentry-data-collection) +for the full default policy. + +Structured log events and breadcrumbs are now normalized and redacted before user +callbacks. Recognized structlog events omit frame locals, which can retain the +original unredacted line. Update callbacks that relied on those locals or raw JSON +messages ([#408](https://github.com/modern-python/lite-bootstrap/pull/408)). +FastStream message scopes are isolated: set tags and breadcrumbs for each message +instead of relying on context left by a previous one +([#409](https://github.com/modern-python/lite-bootstrap/pull/409)). + +### Sessions and breadcrumbs + +Release-health session tracking defaults to off, and logging breadcrumbs start at +`WARNING` instead of `INFO`. HTTP crash-free session/user rates stop and INFO log +breadcrumbs disappear. To restore both defaults from 1.x: + +```python +import logging + +from lite_bootstrap import FreeConfig + +config = FreeConfig( + sentry_auto_session_tracking=True, + sentry_logging_breadcrumb_level=logging.INFO, +) +``` + +Only HTTP frameworks opened request sessions; FastStream and Free do not gain +request sessions from this option +([#360](https://github.com/modern-python/lite-bootstrap/pull/360)). + +### Lean request error capture + +FastAPI, Litestar, and FastMCP now use lean error capture when tracing is not +requested and no replaced framework integration is supplied. Request transactions +are no longer created, so code reading the current transaction must change. +Litestar exceptions handled as 4xx are no longer reported. FastAPI/FastMCP report +escaping exceptions and handled exceptions whose own integer `status_code` is 5xx; +a handler returning 503 alone does not make its exception reportable. -## Metrics-only OpenTelemetry tracing +Set `sentry_lean_error_capture=False` to retain Sentry's framework integrations. +Setting a tracing option, including an explicit sample rate of zero, selects the +integration path under the default `None` policy. Avoid initializing those framework +integrations earlier in the same process: lean capture cannot undo their global +patches and warns about possible duplicate events +([#361](https://github.com/modern-python/lite-bootstrap/pull/361), +[#362](https://github.com/modern-python/lite-bootstrap/pull/362)). + +### Auto-enabling integrations + +Auto-enabling integrations default to off while tracing is off (for HTTP frameworks, +while lean capture is on). SQL, Redis, httpx, aiohttp, and FastMCP's `MCPIntegration` +may therefore stop contributing breadcrumbs and event context. Set +`sentry_auto_enabling_integrations=True` to restore them, or list only the desired +integrations in `sentry_integrations` +([#363](https://github.com/modern-python/lite-bootstrap/pull/363)). + +## OpenTelemetry + +### Fewer ASGI and health-check spans + +FastAPI, Litestar, and FastMCP omit per-message `receive` and `send` spans. Set +`opentelemetry_exclude_spans=[]` to restore them +([#364](https://github.com/modern-python/lite-bootstrap/pull/364)). + +`opentelemetry_generate_health_check_spans` defaults to `False`. Health requests +are omitted from traces and OpenTelemetry HTTP metrics. Set it to `True` to observe +probe latency and restore those metrics +([#366](https://github.com/modern-python/lite-bootstrap/pull/366)). + +### OTLP transport security + +`opentelemetry_insecure` defaults to `None`, allowing OTLP insecure environment +variables to take effect. Without an override, schemeless gRPC endpoints such as +`collector:4317` now use TLS. For a plaintext collector, use an `http://` endpoint +or explicitly set `opentelemetry_insecure=True`. Unix sockets also need an explicit +`True`; `https://` retains TLS. Check both trace and metric endpoints +([#372](https://github.com/modern-python/lite-bootstrap/pull/372)). + +```python +from lite_bootstrap import FreeConfig + +config = FreeConfig( + opentelemetry_endpoint="http://localhost:4317", + opentelemetry_metrics_endpoint="http://localhost:4317", +) +``` + +### Metrics-only tracing and external providers Metrics-only configuration no longer installs an SDK `TracerProvider` or records -unexported framework spans. The same applies when a trace endpoint's exporter is -missing and no other span processor is configured; the missing-exporter warning remains. -JSON logs from these requests omit `tracing` unless external tracing records a span. +unexported framework spans. The same applies when a trace exporter is missing and +no other span processor is configured; the missing-exporter warning remains. JSON +logs from these requests omit `tracing` unless external tracing records a span. -If you add span processors yourself, create an SDK tracer provider, attach your -processors, and install it with `opentelemetry.trace.set_tracer_provider()` before -bootstrap. lite-bootstrap preserves that external provider and leaves its shutdown -to your application. Installing a provider after bootstrap does not activate tracing -for framework and custom instrumentors already given a no-op provider. +If you add span processors yourself, create and install an SDK provider before +bootstrap. lite-bootstrap preserves that provider and leaves its shutdown to your +application. Installing one later does not activate framework or custom instrumentors +already given a no-op provider +([#430](https://github.com/modern-python/lite-bootstrap/pull/430)): +```python +from opentelemetry import trace +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor + +from lite_bootstrap import FreeBootstrapper, FreeConfig + +provider = TracerProvider() +provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter())) +trace.set_tracer_provider(provider) +bootstrapper = FreeBootstrapper( + FreeConfig(logging_enabled=False, opentelemetry_metrics_endpoint="http://localhost:4317"), +) +bootstrapper.bootstrap() +try: + with trace.get_tracer(__name__).start_as_current_span("work"): + pass +finally: + bootstrapper.teardown() + provider.shutdown() +``` + +This example needs the `otl` extra and a local collector for metric export. Configurations with a working trace exporter, stdout span logging, or Pyroscope span correlation continue to use an SDK tracer provider. + +### URL exclusion validation + +FastAPI and FastMCP reject commas in `opentelemetry_excluded_urls` patterns with +`ConfigurationError`, because their integrations join patterns with commas. +Split alternatives into separate entries. For a literal comma use `\x2c`; for a +bounded repetition such as `a{1,3}` rewrite it without a comma, such as `aa?a?`. +Litestar accepts comma-containing patterns +([#417](https://github.com/modern-python/lite-bootstrap/pull/417)). + +## CORS methods on FastAPI and Litestar + +`cors_allowed_methods` defaults to `["*"]`. Configuring origins without a method +list now allows preflight for all standard HTTP methods, including POST. Set an +explicit list such as `["GET", "POST"]` to restrict methods. +`cors_allowed_headers` remains `[]`; both frameworks permit their built-in CORS +headers, including `Content-Type`. Add `Authorization` explicitly when needed +([#423](https://github.com/modern-python/lite-bootstrap/pull/423)). + +## Prometheus + +### HTTP metrics alongside OpenTelemetry + +On FastAPI, Litestar, and FastMCP, configuring OpenTelemetry HTTP metrics suppresses +the additional Prometheus HTTP series by default. Scrapes can lose series such as +`http_requests_total` or `litestar_requests_total`. Set +`prometheus_http_metrics_enabled=True` to keep both. Other collectors remain +available ([#365](https://github.com/modern-python/lite-bootstrap/pull/365)). + +### Infrastructure requests + +Health requests and scrapes are excluded from Prometheus HTTP request metrics on +FastAPI, Litestar, and FastMCP. User exclusions still apply. Review dashboards and +alert thresholds: totals can fall and latency percentiles can change. Enabling +`prometheus_http_metrics_enabled` does not restore infrastructure requests +([#424](https://github.com/modern-python/lite-bootstrap/pull/424)). + +### FastStream opt-in and registry + +`FastStreamConfig.prometheus_metrics_path` defaults to `""`. Supplying only +`prometheus_middleware_cls` no longer installs the broker middleware or mounts a +scrape route. Set the path explicitly: + +```python +from lite_bootstrap import FastStreamConfig + +config = FastStreamConfig(prometheus_metrics_path="/metrics") +``` + +Keep your existing application and middleware class settings. Scrapes now use the +default registry, so they include process/platform collectors and other globally +registered metrics alongside broker metrics. Review duplicate collector registrations +and any assumptions about an isolated registry. Set +`prometheus_collector_registry` to your own `CollectorRegistry` for isolation +([#375](https://github.com/modern-python/lite-bootstrap/pull/375)). + +## Health and metrics routes + +Unhealthy health checks return **503** instead of **500**. Update probes and +load-balancer rules that matched exactly 500; match 503 or any 5xx +([#370](https://github.com/modern-python/lite-bootstrap/pull/370)). + +Invalid non-empty `health_checks_path` and `prometheus_metrics_path` now raise +`ConfigurationError` during config construction, even when the instrument would +otherwise be skipped. Paths need a leading slash and non-empty segments using +letters, digits, `.`, `_`, or `-`; a trailing slash is allowed. `/` is supported +only for health checks. Use `""` to disable either route +([#419](https://github.com/modern-python/lite-bootstrap/pull/419)): + +```python +from lite_bootstrap import FastAPIConfig + +config = FastAPIConfig(health_checks_path="/health", prometheus_metrics_path="/metrics") +disabled = FastAPIConfig(health_checks_path="", prometheus_metrics_path="") +``` + +FastStream now serves both slash spellings directly, such as `/health` and +`/health/`, or `/metrics` and `/metrics/`. Existing probes work without changes; +review custom routes that use the alternate spelling for another purpose +([#420](https://github.com/modern-python/lite-bootstrap/pull/420)). + +## Framework lifecycle and offline docs + +FastAPI and FastMCP teardown now runs after user lifespan shutdown. Shutdown spans +and logs can still be exported. Move any manual observability shutdown out of the +user lifespan if it closes exporters before that work finishes +([#412](https://github.com/modern-python/lite-bootstrap/pull/412), +[#413](https://github.com/modern-python/lite-bootstrap/pull/413)). + +A failed bootstrap rolls back successfully started instruments and is terminal. +Create a fresh application and bootstrapper to retry; do not reuse the failed +instance ([#406](https://github.com/modern-python/lite-bootstrap/pull/406)). + +FastAPI offline docs now respect `docs_url=None` and `redoc_url=None` independently. +If you relied on offline mode recreating a disabled route, give that route a URL +explicitly ([#418](https://github.com/modern-python/lite-bootstrap/pull/418)): + +```python +from fastapi import FastAPI + +from lite_bootstrap import FastAPIConfig + +config = FastAPIConfig(application=FastAPI(docs_url="/docs", redoc_url=None)) +``` From 051c6ce8dd1464870d7313baaa057502acb6d11b Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Wed, 4 Nov 2026 13:41:54 +0300 Subject: [PATCH 2/2] docs: tighten migration guide and link to docs site --- .github/release-notes/2.0.0.md | 42 ------ .github/workflows/release.yml | 11 +- README.md | 2 +- docs/migration/to-2.x.md | 238 ++++++++++++++------------------- 4 files changed, 107 insertions(+), 186 deletions(-) delete mode 100644 .github/release-notes/2.0.0.md diff --git a/.github/release-notes/2.0.0.md b/.github/release-notes/2.0.0.md deleted file mode 100644 index ac1d08c..0000000 --- a/.github/release-notes/2.0.0.md +++ /dev/null @@ -1,42 +0,0 @@ -## Upgrading from 1.x - -Read the [2.x migration guide](https://github.com/modern-python/lite-bootstrap/blob/main/docs/migration/to-2.x.md) -for replacement APIs, configuration examples, and actions for each affected framework. - -## Breaking changes - -- Raise Sentry to `>=2.70,<3`; explicit `data_collection` replaces SDK event scrubbing and `send_default_pii` opt-ins (#359, #379). -- Default Sentry session tracking to off and log breadcrumbs to `WARNING` (#360). -- Use lean request error capture on Litestar, FastAPI, and FastMCP when tracing is off; stop creating request transactions and attach only bodies read by handlers. Litestar handled 4xx exceptions are no longer reported (#361, #362). -- Leave Sentry auto-enabling integrations off while tracing is off, subject to the HTTP lean-capture policy (#363). -- Omit OpenTelemetry ASGI receive/send spans by default (#364). -- Suppress duplicate Prometheus HTTP series when OpenTelemetry HTTP metrics are configured (#365). -- Omit health requests from OpenTelemetry spans and HTTP metrics by default (#366). -- Remove deprecated imports, config fields, logging protocols, and custom instrument member aliases (#367). -- Raise OpenTelemetry API/SDK/exporters to 1.42 and instrumentation to 0.63b0 (#368). -- Default `service_debug` to `False` and preserve explicitly configured Litestar debug mode (#369). -- Return 503, rather than 500, for unhealthy health checks (#370). -- Require Litestar 2.22 or later for all Litestar extras (#371). -- Default `opentelemetry_insecure` to `None`: environment settings apply, and schemeless gRPC endpoints use TLS unless overridden (#372). -- Default service identity fields to `None`, honor `OTEL_SERVICE_NAME`, omit unset resource versions, and use an explicit `service_version` as the Sentry release (#373). -- Require FastMCP `>=4.0,<5`; access logs now include notifications and pre-handler failures (#374). -- Make FastStream Prometheus routes and middleware opt-in through a non-empty metrics path, and use the default collector registry (#375). -- Require both application and config type arguments for `BaseBootstrapper` subclasses (#376). -- Remove logging buffer settings and write structlog output immediately (#378). -- Remove `MemoryLoggerFactory` and its instrument property; propagate structlog and framework loggers to root and inherit root levels (#405). -- Roll back failed bootstrap and reject reuse of the failed instance (#406). -- Normalize and redact structured Sentry events/breadcrumbs before callbacks and omit their frame locals (#408). -- Isolate Sentry context per FastStream message (#409). -- Enforce Sentry request body capture limits in lean capture (#410). -- Tear down FastAPI and FastMCP observability after user lifespan shutdown (#412, #413). -- Preserve application root handlers and restore the previous root level during logging teardown (#415). -- Reject commas in FastAPI and FastMCP OpenTelemetry URL exclusion patterns (#417). -- Respect disabled FastAPI offline docs routes independently (#418). -- Reject invalid non-empty health and metrics paths at config construction (#419). -- Serve both FastStream health/metrics slash spellings; review alternate-path custom routes (#420). -- Default CORS methods to `["*"]` on FastAPI and Litestar when origins are configured (#423). -- Exclude health and scrape traffic from Prometheus HTTP metrics (#424). -- Omit JSON log `tracing` outside a recording span (#425). -- Remove `InstrumentSkippedWarning`; use `InstrumentDependencyMissingWarning` directly (#427). -- Silently skip the default Prometheus path when `prometheus_client` is missing; custom paths still warn (#428). -- Skip SDK tracing without a span processor; install externally owned providers before bootstrap to preserve custom tracing (#430). diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf66736..ff28385 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,15 +32,15 @@ jobs: # Publisher on the lite-bootstrap PyPI project (env: pypi, workflow: release.yml). - run: just publish - # Versioned notes supplement GitHub's generated PR list when present. + # The Release body is GitHub's generated notes, rendered from the squashed + # PR titles since the previous tag — so a conventional-commit title is what + # a reader gets. A release wanting prose is edited after the fact with + # `gh release edit --notes-file`. A tag with a letter (2.0.0rc1) is a + # pre-release -> flagged so GitHub won't mark it "Latest". - name: Resolve release metadata id: meta run: | set -euo pipefail - notes_file=".github/release-notes/$GITHUB_REF_NAME.md" - if [[ -f "$notes_file" ]]; then - echo "notes_file=$notes_file" >> "$GITHUB_OUTPUT" - fi if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then echo "prerelease=true" >> "$GITHUB_OUTPUT" else @@ -50,7 +50,6 @@ jobs: - name: Publish GitHub Release uses: softprops/action-gh-release@v3 with: - body_path: ${{ steps.meta.outputs.notes_file }} generate_release_notes: true prerelease: ${{ steps.meta.outputs.prerelease }} draft: false diff --git a/README.md b/README.md index a473f23..ea9f3f5 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ The following intentionally differ: ## Links - [Documentation](https://lite-bootstrap.modern-python.org) -- [Upgrading to 2.x](https://github.com/modern-python/lite-bootstrap/blob/main/docs/migration/to-2.x.md) +- [Upgrading to 2.x](https://lite-bootstrap.modern-python.org/migration/to-2.x/) - [PyPI](https://pypi.org/project/lite-bootstrap) - [License](https://github.com/modern-python/lite-bootstrap/blob/main/LICENSE) diff --git a/docs/migration/to-2.x.md b/docs/migration/to-2.x.md index cc238ea..cc7a147 100644 --- a/docs/migration/to-2.x.md +++ b/docs/migration/to-2.x.md @@ -1,10 +1,8 @@ # Migration guide: upgrading to lite-bootstrap 2.x -This guide covers changes since 1.16.0. Upgrade your dependency constraints first, -then review the sections for the instruments and frameworks your service uses. -The [2.0.0 release notes](https://github.com/modern-python/lite-bootstrap/releases/tag/2.0.0) -provide the release checklist. Configuration snippets below are independent examples; -use your own DSN, application, exporter, and service identity where applicable. +This guide covers changes since 1.16.0. Update dependency constraints, then follow +the sections for your frameworks and instruments. Snippets are independent examples; +use your service's DSN, application, exporter, and identity. ## Dependency constraints @@ -18,11 +16,10 @@ Update lockfiles and any direct pins to match these supported versions: | `litestar` | `>=2.22` | All Litestar extras | | `fastmcp` | `>=4.0,<5` | All FastMCP extras | -Sentry 3 is not supported. FastMCP 3 pins must move to 4; see its -[upgrade guide](https://github.com/PrefectHQ/fastmcp/blob/main/docs/getting-started/upgrading/from-fastmcp-3.mdx) -for changes in FastMCP itself. With access logging configured, notifications and -pre-handler failures now produce records too; review log-volume assumptions. -These constraints come from +Sentry 3 is unsupported. For FastMCP 3, follow the +[FastMCP 4 upgrade guide](https://github.com/PrefectHQ/fastmcp/blob/main/docs/getting-started/upgrading/from-fastmcp-3.mdx). +FastMCP access logs now include notifications and pre-handler failures, increasing +log volume. Sources: [#359](https://github.com/modern-python/lite-bootstrap/pull/359), [#368](https://github.com/modern-python/lite-bootstrap/pull/368), [#371](https://github.com/modern-python/lite-bootstrap/pull/371), @@ -31,9 +28,8 @@ These constraints come from ## Removed names and config fields -Aliases deprecated in 1.x have been removed -([#367](https://github.com/modern-python/lite-bootstrap/pull/367)). Update imports, -attribute access, config constructor arguments, and keys in external settings sources: +Replace aliases deprecated in 1.x in imports, attributes, constructor arguments, +and settings keys ([#367](https://github.com/modern-python/lite-bootstrap/pull/367)): | Removed name | Replacement | | --- | --- | @@ -51,11 +47,11 @@ attribute access, config constructor arguments, and keys in external settings so | Custom instrument `not_ready_message` | `not_configured_reason` | | Custom instrument `check_dependencies` | `dependencies_installed` | -Removed imports fail with `ImportError`; removed attributes fail with `AttributeError`. -Removed constructor keywords fail with `TypeError`. `from_dict()` and `from_object()` -still ignore unknown keys, so old settings keys can silently fall back to defaults. -Rename those keys before upgrading. Custom instruments using the old dependency -method can be instantiated despite a missing package; update both class members. +Removed imports raise `ImportError`, attributes raise `AttributeError`, and +constructor keywords raise `TypeError`. `from_dict()` and `from_object()` ignore +unknown keys, so rename settings keys to avoid silently using defaults. Update both +custom instrument members: the old dependency method no longer prevents construction +when a package is missing. For example, replace `FreeBootstrapperConfig(...)` with: @@ -67,10 +63,9 @@ config = FreeConfig(logging_enabled=False) ### Missing-dependency warning category -`InstrumentSkippedWarning` has also been removed -([#427](https://github.com/modern-python/lite-bootstrap/pull/427)). Replace imports -and warning filters with `InstrumentDependencyMissingWarning`, which derives directly -from `UserWarning`: +Replace `InstrumentSkippedWarning` imports and filters with +`InstrumentDependencyMissingWarning`, now a direct subclass of `UserWarning` +([#427](https://github.com/modern-python/lite-bootstrap/pull/427)): ```python import warnings @@ -80,19 +75,17 @@ from lite_bootstrap import InstrumentDependencyMissingWarning warnings.filterwarnings("error", category=InstrumentDependencyMissingWarning) ``` -Unconfigured instruments are skipped silently. A configured instrument with a missing -optional dependency warns. With `prometheus_client` missing, the default metrics path -now skips silently too, including an explicitly supplied `/metrics` on FastAPI, -Litestar, or FastMCP. A custom path still warns; install the framework's metrics extra -if you expect a scrape route +Unconfigured instruments skip silently; configured instruments with missing +dependencies warn. Without `prometheus_client`, the default metrics path also skips +silently, even an explicit `/metrics` on FastAPI, Litestar, or FastMCP. Custom paths +still warn. Install your framework's metrics extra if you expect a scrape route ([#428](https://github.com/modern-python/lite-bootstrap/pull/428)). ### Custom bootstrappers -`BaseBootstrapper` now takes the application and config types. Change -`BaseBootstrapper[MyApp]` to `BaseBootstrapper[MyApp, MyConfig]`; the old form raises -`TypeError` when the class is defined -([#376](https://github.com/modern-python/lite-bootstrap/pull/376)). For example: +Change `BaseBootstrapper[MyApp]` to `BaseBootstrapper[MyApp, MyConfig]`. +The old form raises `TypeError` at class definition +([#376](https://github.com/modern-python/lite-bootstrap/pull/376)): ```python from lite_bootstrap import FreeConfig @@ -105,10 +98,9 @@ class MyBootstrapper(BaseBootstrapper[object, FreeConfig]): ## Service identity and debug mode -`service_debug` defaults to `False`. FastAPI apps created by lite-bootstrap and -Litestar apps no longer show debug tracebacks unless explicitly requested. Set -`service_debug=True` for development; Litestar also preserves an explicit -`AppConfig(debug=True)` +`service_debug=False` disables debug tracebacks on FastAPI apps created by +lite-bootstrap and on Litestar apps. Set `service_debug=True` for development. +Litestar also preserves `AppConfig(debug=True)` ([#369](https://github.com/modern-python/lite-bootstrap/pull/369)). `service_name` and `service_version` now default to `None` @@ -135,55 +127,47 @@ from lite_bootstrap import FreeConfig config = FreeConfig(service_name="micro-service", service_version="1.0.0") ``` -Review Sentry release grouping when setting a version explicitly. - ## Logging ### Immediate output and root propagation -`logging_buffer_capacity` and `logging_flush_level` were removed. Delete those -constructor arguments and settings keys. Structlog lines are written immediately, -in order with stdlib lines, rather than waiting for a buffer flush +Delete `logging_buffer_capacity` and `logging_flush_level` from constructors and +settings. Structlog lines now appear immediately, in order with stdlib lines ([#378](https://github.com/modern-python/lite-bootstrap/pull/378)). -`MemoryLoggerFactory` and `LoggingInstrument.memory_logger_factory` were removed. -Structlog uses the standard-library logger factory and propagates to root. Loggers -used by structlog inherit root's level instead of receiving their own. Stdlib records -on those names and their children are now JSON lines. Litestar and FastStream loggers -also propagate to root while bootstrapped +Remove uses of `MemoryLoggerFactory` and `LoggingInstrument.memory_logger_factory`. +Structlog uses the stdlib factory, propagates to root, and inherits root's level. +Stdlib records on structlog logger names and their children now render as JSON. +Litestar and FastStream loggers also propagate to root while bootstrapped ([#405](https://github.com/modern-python/lite-bootstrap/pull/405)). -Remove code inspecting the old factory or per-logger handlers. Review your parent -and root handlers for duplicate output, and set explicit named-logger levels if you -need them to differ from `logging_log_level`. +Check parent and root handlers for duplicate output. Set named-logger levels +explicitly when they should differ from `logging_log_level`. ### Teardown preserves application logging -Logging teardown now removes only its own root handler and restores the root level -from before bootstrap. Other root handlers survive. If your application relied on -teardown to close its handlers or set root to `WARNING`, do that explicitly. -Removals requested through `logging_unset_handlers` remain permanent +Teardown removes its own root handler, preserves other handlers, and restores the +previous root level. Close application handlers or set root to `WARNING` yourself +if needed. `logging_unset_handlers` removals remain permanent ([#415](https://github.com/modern-python/lite-bootstrap/pull/415)). ### Trace context in JSON logs -JSON log lines include `tracing` only inside a recording OpenTelemetry span; the -object contains `span_id` and `trace_id`. Outside one, the key is absent instead of -an empty object. Update log parsers to treat `tracing` as optional +Treat JSON log `tracing` as optional. It contains `span_id` and `trace_id` inside a +recording OpenTelemetry span and is absent elsewhere, replacing the empty object ([#425](https://github.com/modern-python/lite-bootstrap/pull/425)). ## Sentry ### Data collection and request bodies -lite-bootstrap passes an explicit `data_collection` policy. Sentry's general event -scrubber is bypassed by the SDK for that policy; headers, cookies, query parameters, -and frame locals instead use collection denylists. `send_default_pii=True` alone no -longer opts into collection. User information and HTTP bodies are off by default +An explicit `data_collection` policy bypasses Sentry's general event scrubber; +collection denylists filter headers, cookies, query parameters, and frame locals. +Review custom scrubbing. User information and HTTP bodies default to off; +`send_default_pii=True` alone no longer enables them ([#359](https://github.com/modern-python/lite-bootstrap/pull/359)). -Review any custom scrubbing and opt-ins. To collect user information or incoming -request bodies, replace the relevant policy entries while retaining the others: +To collect user information or incoming bodies, override those policy entries: ```python from lite_bootstrap import FastAPIConfig @@ -201,27 +185,22 @@ config = FastAPIConfig( ) ``` -Enable only the data your service intends to collect. Lean capture attaches a body -only after the handler reads it; FastAPI and FastMCP require the complete body. -Raw bodies are text rather than a raw-data annotation. Request body size policy is -also enforced by lean capture +Lean capture attaches bodies only after the handler reads them; FastAPI and +FastMCP require the complete body. Raw bodies become text. Body size limits apply ([#410](https://github.com/modern-python/lite-bootstrap/pull/410)). See [Sentry data collection](../introduction/configuration.md#sentry-data-collection) for the full default policy. -Structured log events and breadcrumbs are now normalized and redacted before user -callbacks. Recognized structlog events omit frame locals, which can retain the -original unredacted line. Update callbacks that relied on those locals or raw JSON -messages ([#408](https://github.com/modern-python/lite-bootstrap/pull/408)). -FastStream message scopes are isolated: set tags and breadcrumbs for each message -instead of relying on context left by a previous one +Structured log events and breadcrumbs reach callbacks normalized and redacted. +Structlog events omit frame locals to avoid leaking the original line. Update +callbacks that relied on those locals or raw JSON messages ([#408](https://github.com/modern-python/lite-bootstrap/pull/408)). +FastStream isolates message scopes. Set tags and breadcrumbs per message ([#409](https://github.com/modern-python/lite-bootstrap/pull/409)). ### Sessions and breadcrumbs -Release-health session tracking defaults to off, and logging breadcrumbs start at -`WARNING` instead of `INFO`. HTTP crash-free session/user rates stop and INFO log -breadcrumbs disappear. To restore both defaults from 1.x: +Session tracking defaults to off; HTTP crash-free session/user rates stop. +Log breadcrumbs start at `WARNING`, omitting INFO records. Restore the 1.x defaults: ```python import logging @@ -234,34 +213,30 @@ config = FreeConfig( ) ``` -Only HTTP frameworks opened request sessions; FastStream and Free do not gain -request sessions from this option +Request sessions apply only to HTTP frameworks, not FastStream or Free ([#360](https://github.com/modern-python/lite-bootstrap/pull/360)). ### Lean request error capture -FastAPI, Litestar, and FastMCP now use lean error capture when tracing is not -requested and no replaced framework integration is supplied. Request transactions -are no longer created, so code reading the current transaction must change. -Litestar exceptions handled as 4xx are no longer reported. FastAPI/FastMCP report -escaping exceptions and handled exceptions whose own integer `status_code` is 5xx; -a handler returning 503 alone does not make its exception reportable. - -Set `sentry_lean_error_capture=False` to retain Sentry's framework integrations. -Setting a tracing option, including an explicit sample rate of zero, selects the -integration path under the default `None` policy. Avoid initializing those framework -integrations earlier in the same process: lean capture cannot undo their global +FastAPI, Litestar, and FastMCP use lean capture when tracing is unrequested and no +replaced framework integration is supplied. It creates no request transaction; +update code that reads one. Litestar no longer reports exceptions handled as 4xx. +FastAPI/FastMCP report escaping exceptions and handled exceptions with their own +integer 5xx `status_code`; a handler returning 503 alone does not qualify. + +Set `sentry_lean_error_capture=False` to keep Sentry's framework integrations. +With the default `None`, any tracing option, even a zero sample rate, selects them. +Avoid initializing them earlier: lean capture cannot undo global patches and warns about possible duplicate events ([#361](https://github.com/modern-python/lite-bootstrap/pull/361), [#362](https://github.com/modern-python/lite-bootstrap/pull/362)). ### Auto-enabling integrations -Auto-enabling integrations default to off while tracing is off (for HTTP frameworks, +Auto-enabling integrations default to off without tracing (on HTTP frameworks, while lean capture is on). SQL, Redis, httpx, aiohttp, and FastMCP's `MCPIntegration` -may therefore stop contributing breadcrumbs and event context. Set -`sentry_auto_enabling_integrations=True` to restore them, or list only the desired -integrations in `sentry_integrations` +may stop adding breadcrumbs and event context. Restore them with `sentry_auto_enabling_integrations=True` +or select integrations through `sentry_integrations` ([#363](https://github.com/modern-python/lite-bootstrap/pull/363)). ## OpenTelemetry @@ -272,18 +247,16 @@ FastAPI, Litestar, and FastMCP omit per-message `receive` and `send` spans. Set `opentelemetry_exclude_spans=[]` to restore them ([#364](https://github.com/modern-python/lite-bootstrap/pull/364)). -`opentelemetry_generate_health_check_spans` defaults to `False`. Health requests -are omitted from traces and OpenTelemetry HTTP metrics. Set it to `True` to observe -probe latency and restore those metrics +`opentelemetry_generate_health_check_spans=False` omits health requests from traces +and OpenTelemetry HTTP metrics. Set it to `True` to restore both ([#366](https://github.com/modern-python/lite-bootstrap/pull/366)). ### OTLP transport security -`opentelemetry_insecure` defaults to `None`, allowing OTLP insecure environment -variables to take effect. Without an override, schemeless gRPC endpoints such as -`collector:4317` now use TLS. For a plaintext collector, use an `http://` endpoint -or explicitly set `opentelemetry_insecure=True`. Unix sockets also need an explicit -`True`; `https://` retains TLS. Check both trace and metric endpoints +`opentelemetry_insecure=None` lets OTLP insecure environment variables apply. +Without an override, schemeless gRPC endpoints such as `collector:4317` use TLS. +For plaintext, use `http://` or `opentelemetry_insecure=True`. Unix sockets need +explicit `True`; `https://` keeps TLS. Check trace and metric endpoints ([#372](https://github.com/modern-python/lite-bootstrap/pull/372)). ```python @@ -297,15 +270,14 @@ config = FreeConfig( ### Metrics-only tracing and external providers -Metrics-only configuration no longer installs an SDK `TracerProvider` or records -unexported framework spans. The same applies when a trace exporter is missing and -no other span processor is configured; the missing-exporter warning remains. JSON -logs from these requests omit `tracing` unless external tracing records a span. +Without a span processor, metrics-only bootstrap installs no SDK `TracerProvider` +and records no framework spans. This also applies when the trace exporter is +missing; its warning remains. JSON logs omit `tracing` unless an external provider +records a span. -If you add span processors yourself, create and install an SDK provider before -bootstrap. lite-bootstrap preserves that provider and leaves its shutdown to your -application. Installing one later does not activate framework or custom instrumentors -already given a no-op provider +Install your SDK provider and processors before bootstrap; your application owns +its shutdown. A provider installed later cannot activate instrumentors already +bound to a no-op provider ([#430](https://github.com/modern-python/lite-bootstrap/pull/430)): ```python @@ -336,11 +308,10 @@ span correlation continue to use an SDK tracer provider. ### URL exclusion validation -FastAPI and FastMCP reject commas in `opentelemetry_excluded_urls` patterns with -`ConfigurationError`, because their integrations join patterns with commas. -Split alternatives into separate entries. For a literal comma use `\x2c`; for a -bounded repetition such as `a{1,3}` rewrite it without a comma, such as `aa?a?`. -Litestar accepts comma-containing patterns +FastAPI and FastMCP raise `ConfigurationError` for commas in +`opentelemetry_excluded_urls`, which their integrations join with commas. Split +alternatives into entries, use `\x2c` for a literal comma, and rewrite bounded +repetitions such as `a{1,3}` as `aa?a?`. Litestar accepts commas ([#417](https://github.com/modern-python/lite-bootstrap/pull/417)). ## CORS methods on FastAPI and Litestar @@ -372,9 +343,8 @@ alert thresholds: totals can fall and latency percentiles can change. Enabling ### FastStream opt-in and registry -`FastStreamConfig.prometheus_metrics_path` defaults to `""`. Supplying only -`prometheus_middleware_cls` no longer installs the broker middleware or mounts a -scrape route. Set the path explicitly: +FastStream's metrics path defaults to `""`, disabling the scrape route and broker +middleware even with `prometheus_middleware_cls` set. Supply a path: ```python from lite_bootstrap import FastStreamConfig @@ -382,11 +352,10 @@ from lite_bootstrap import FastStreamConfig config = FastStreamConfig(prometheus_metrics_path="/metrics") ``` -Keep your existing application and middleware class settings. Scrapes now use the -default registry, so they include process/platform collectors and other globally -registered metrics alongside broker metrics. Review duplicate collector registrations -and any assumptions about an isolated registry. Set -`prometheus_collector_registry` to your own `CollectorRegistry` for isolation +Keep your application and middleware settings. Scrapes use the default registry, +including process/platform and other global collectors. Check for duplicate +registrations. For isolation, pass your own `CollectorRegistry` through +`prometheus_collector_registry` ([#375](https://github.com/modern-python/lite-bootstrap/pull/375)). ## Health and metrics routes @@ -395,11 +364,10 @@ Unhealthy health checks return **503** instead of **500**. Update probes and load-balancer rules that matched exactly 500; match 503 or any 5xx ([#370](https://github.com/modern-python/lite-bootstrap/pull/370)). -Invalid non-empty `health_checks_path` and `prometheus_metrics_path` now raise -`ConfigurationError` during config construction, even when the instrument would -otherwise be skipped. Paths need a leading slash and non-empty segments using -letters, digits, `.`, `_`, or `-`; a trailing slash is allowed. `/` is supported -only for health checks. Use `""` to disable either route +Invalid non-empty health or metrics paths raise `ConfigurationError` at config +construction, even for skipped instruments. Use a leading slash and non-empty +segments of letters, digits, `.`, `_`, or `-`; a trailing slash is allowed. +`/` works only for health checks. `""` disables either route ([#419](https://github.com/modern-python/lite-bootstrap/pull/419)): ```python @@ -409,26 +377,22 @@ config = FastAPIConfig(health_checks_path="/health", prometheus_metrics_path="/m disabled = FastAPIConfig(health_checks_path="", prometheus_metrics_path="") ``` -FastStream now serves both slash spellings directly, such as `/health` and -`/health/`, or `/metrics` and `/metrics/`. Existing probes work without changes; -review custom routes that use the alternate spelling for another purpose +FastStream serves `/health` and `/health/`, and `/metrics` and `/metrics/`, directly. +Existing probes keep working. Check for custom routes using the alternate spelling ([#420](https://github.com/modern-python/lite-bootstrap/pull/420)). ## Framework lifecycle and offline docs -FastAPI and FastMCP teardown now runs after user lifespan shutdown. Shutdown spans -and logs can still be exported. Move any manual observability shutdown out of the -user lifespan if it closes exporters before that work finishes +FastAPI and FastMCP teardown runs after user lifespan shutdown, preserving shutdown +spans and logs. Keep exporters open until that work finishes ([#412](https://github.com/modern-python/lite-bootstrap/pull/412), [#413](https://github.com/modern-python/lite-bootstrap/pull/413)). -A failed bootstrap rolls back successfully started instruments and is terminal. -Create a fresh application and bootstrapper to retry; do not reuse the failed -instance ([#406](https://github.com/modern-python/lite-bootstrap/pull/406)). +Failed bootstrap rolls back successfully started instruments and cannot be reused. Retry with +a fresh application and bootstrapper ([#406](https://github.com/modern-python/lite-bootstrap/pull/406)). -FastAPI offline docs now respect `docs_url=None` and `redoc_url=None` independently. -If you relied on offline mode recreating a disabled route, give that route a URL -explicitly ([#418](https://github.com/modern-python/lite-bootstrap/pull/418)): +FastAPI offline docs respect `docs_url=None` and `redoc_url=None` independently. +To serve a disabled route again, give it a URL ([#418](https://github.com/modern-python/lite-bootstrap/pull/418)): ```python from fastapi import FastAPI