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
4 changes: 2 additions & 2 deletions docs/introduction/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ 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)
logging_log_level=logging.INFO, # your application logs
faststream_log_level=logging.WARNING, # broker "Received"/"Processed" messages (default)
)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/introduction/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ bootstrapper:
bootstrapper = FastAPIBootstrapper(config)
app = bootstrapper.bootstrap()

print(bootstrapper.build_summary()) # human-readable summary
print(bootstrapper.build_summary()) # human-readable summary
for cls, reason in bootstrapper.skipped_instruments:
print(f"skipped {cls.__name__}: {reason}")
```
Expand Down
3 changes: 2 additions & 1 deletion planning/audits/2026-06-05-bug-audit-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ no-op default:

```python
from opentelemetry.trace import NoOpTracerProvider

set_tracer_provider(NoOpTracerProvider())
```

Expand Down Expand Up @@ -216,7 +217,7 @@ def teardown(self) -> None:
root_logger = logging.getLogger()
for h in root_logger.handlers[:]:
root_logger.removeHandler(h)
h.close() # ← unprotected; can raise
h.close() # ← unprotected; can raise
root_logger.setLevel(logging.WARNING)
if self._logger_factory is not None:
try:
Expand Down
1 change: 1 addition & 0 deletions planning/changes/2026-05-31.01-audit-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ non-negotiable.
```python
ConfigT = typing.TypeVar("ConfigT", bound=BaseConfig)


@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
class BaseInstrument(typing.Generic[ConfigT]):
bootstrap_config: ConfigT
Expand Down
13 changes: 8 additions & 5 deletions planning/changes/2026-06-01.02-fastmcp-bootstrapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ def _make_fastmcp() -> "FastMCP[typing.Any]":


@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
class FastMcpConfig(
HealthChecksConfig, LoggingConfig, PrometheusConfig, PyroscopeConfig, SentryConfig
):
class FastMcpConfig(HealthChecksConfig, LoggingConfig, PrometheusConfig, PyroscopeConfig, SentryConfig):
application: "FastMCP[typing.Any]" = dataclasses.field(default_factory=_make_fastmcp)
logging_turn_off_middleware: bool = False
```
Expand Down Expand Up @@ -127,11 +125,15 @@ class FastMcpLoggingMiddleware(Middleware):
result = await call_next(context)
except Exception:
fastmcp_access_logger.exception(
context.method or "unknown", mcp=mcp_fields, duration=time.perf_counter_ns() - start,
context.method or "unknown",
mcp=mcp_fields,
duration=time.perf_counter_ns() - start,
)
raise
fastmcp_access_logger.info(
context.method or "unknown", mcp=mcp_fields, duration=time.perf_counter_ns() - start,
context.method or "unknown",
mcp=mcp_fields,
duration=time.perf_counter_ns() - start,
)
return result
```
Expand Down Expand Up @@ -210,6 +212,7 @@ if import_checker.is_fastmcp_installed:

if import_checker.is_structlog_installed:
import structlog

fastmcp_access_logger: typing.Final = structlog.get_logger("mcp.access")

if import_checker.is_prometheus_client_installed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ try:
return structlog.get_logger(__name__)

except ImportError:

def _get_logger() -> typing.Any: # noqa: ANN401
return logging.getLogger(__name__)
```
Expand Down Expand Up @@ -156,9 +157,7 @@ Replaces `_get_logger().warning(f"Error tearing down {name}: {e}")`. Uses stdlib
### `test_teardown_error_isolation`

```python
def test_teardown_error_isolation(
free_bootstrapper_config: FreeConfig, caplog: pytest.LogCaptureFixture
) -> None:
def test_teardown_error_isolation(free_bootstrapper_config: FreeConfig, caplog: pytest.LogCaptureFixture) -> None:
bootstrapper = FreeBootstrapper(bootstrap_config=free_bootstrapper_config)
bootstrapper.bootstrap()

Expand Down
13 changes: 8 additions & 5 deletions planning/changes/2026-06-23.01-structured-log-payload.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ A consumer-side interpretation type living beside the existing serializer:
```python
@dataclasses.dataclass(frozen=True, slots=True)
class StructuredLogPayload:
message: str | None # the structlog `event` key; None when absent
extra: dict[str, typing.Any] # user fields, meta-keys already stripped
message: str | None # the structlog `event` key; None when absent
extra: dict[str, typing.Any] # user fields, meta-keys already stripped
skip_sentry: bool

@classmethod
Expand Down Expand Up @@ -124,9 +124,12 @@ forgets the set.

```python
payload = StructuredLogPayload.parse(formatted_message)
if payload is None: return event # not a structlog JSON line
if payload.skip_sentry: return None # drop — checked BEFORE message
if not payload.message: return event # JSON without an `event` key
if payload is None:
return event # not a structlog JSON line
if payload.skip_sentry:
return None # drop — checked BEFORE message
if not payload.message:
return event # JSON without an `event` key
event["logentry"]["formatted"] = payload.message
if payload.extra:
event["contexts"]["structlog"] = payload.extra
Expand Down
6 changes: 3 additions & 3 deletions planning/changes/2026-06-24.02-otel-excluded-urls-home.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ instead of silently dropping URLs from the trace-exclusion set.
stringly-typed cross-config reads:

```python
excluded_urls = set(getattr(self.bootstrap_config, "opentelemetry_excluded_urls", [])) # OTel's OWN field
prometheus_path = getattr(self.bootstrap_config, "prometheus_metrics_path", None) # PrometheusConfig
excluded_urls = set(getattr(self.bootstrap_config, "opentelemetry_excluded_urls", [])) # OTel's OWN field
prometheus_path = getattr(self.bootstrap_config, "prometheus_metrics_path", None) # PrometheusConfig
if not self.bootstrap_config.opentelemetry_generate_health_check_spans:
health_path = getattr(self.bootstrap_config, "health_checks_path", None) # HealthChecksConfig
health_path = getattr(self.bootstrap_config, "health_checks_path", None) # HealthChecksConfig
```

These are two different kinds of read, and conflating them hides the real issue:
Expand Down
10 changes: 8 additions & 2 deletions planning/changes/2026-07-18.01-free-threaded-python-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,14 @@ import. Two explicit implementations so both branches are unit-testable on a
normal (orjson-present) run:

```python
def _dumps_orjson(value, **kw): return orjson.dumps(value, **kw).decode()
def _dumps_stdlib(value, **kw): return json.dumps(value, separators=(",", ":"), ensure_ascii=False, **kw)
def _dumps_orjson(value, **kw):
return orjson.dumps(value, **kw).decode()


def _dumps_stdlib(value, **kw):
return json.dumps(value, separators=(",", ":"), ensure_ascii=False, **kw)


_serialize_log_to_string = _dumps_orjson if import_checker.is_orjson_installed else _dumps_stdlib
```

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ ignore = [
"COM812", # flake8-commas "Trailing comma missing"
"ISC001", # flake8-implicit-str-concat
"G004", # allow f-strings in logging
"CPY001", # no per-file copyright header
]
isort.lines-after-imports = 2
isort.no-lines-before = ["standard-library", "local-folder"]
Expand Down
Loading