# Lifecycle & Results Runtime management helpers and the `DrainResult` structure returned when stopping loggers. ## DrainResult {#drainresult} ```python @dataclass class DrainResult: submitted: int processed: int dropped: int retried: int queue_depth_high_watermark: int flush_latency_seconds: float adaptive: AdaptiveDrainSummary | None = None ``` Returned by `AsyncLoggerFacade.drain()` / `stop_and_drain()` and the sync logger's `stop_and_drain()` (which you can run via `asyncio.run`). The `adaptive` field is `None` when the adaptive pipeline is not enabled. When using `preset="adaptive"` or `with_adaptive(enabled=True)`, it contains an `AdaptiveDrainSummary` with metrics about what the adaptive system did during the logger's lifetime. ### Example (async) ```python from fapilog import get_async_logger logger = await get_async_logger("worker") await logger.info("shutting down") result = await logger.drain() print(f"processed={result.processed} dropped={result.dropped}") ``` ### Example (sync) ```python import asyncio from fapilog import get_logger logger = get_logger("cli") logger.info("done") result = asyncio.run(logger.stop_and_drain()) print(result.queue_depth_high_watermark) ``` ## AdaptiveDrainSummary {#adaptivedrainsummary} ```python @dataclass(frozen=True) class AdaptiveDrainSummary: peak_pressure_level: PressureLevel escalation_count: int deescalation_count: int time_at_level: dict[PressureLevel, float] filters_swapped: int workers_scaled: int peak_workers: int batch_resize_count: int ``` Available on `DrainResult.adaptive` when the adaptive pipeline is enabled. Provides a post-drain summary of adaptive pipeline activity. | Field | Description | |-------|-------------| | `peak_pressure_level` | Highest pressure level reached during the session | | `escalation_count` | Number of upward pressure transitions (e.g., NORMAL to ELEVATED) | | `deescalation_count` | Number of downward pressure transitions | | `time_at_level` | Wall-clock seconds spent at each `PressureLevel` | | `filters_swapped` | Number of times filters were tightened or restored | | `workers_scaled` | Number of worker scaling events | | `peak_workers` | Maximum concurrent workers reached | | `batch_resize_count` | Number of adaptive batch size changes | ### Example ```python from fapilog import get_async_logger from fapilog.core.pressure import PressureLevel logger = await get_async_logger(preset="adaptive") # ... application work ... result = await logger.drain() if result.adaptive is not None: summary = result.adaptive print(f"Peak pressure: {summary.peak_pressure_level.value}") print(f"Escalations: {summary.escalation_count}") print(f"Time at NORMAL: {summary.time_at_level[PressureLevel.NORMAL]:.1f}s") print(f"Peak workers: {summary.peak_workers}") ``` ## Context managers Prefer `runtime()` / `runtime_async()` to manage startup and shutdown automatically: ```python from fapilog import runtime, runtime_async with runtime() as logger: logger.info("sync work") async def main(): async with runtime_async() as logger: await logger.info("async work") ``` ## Shutdown timeout `Settings.core.shutdown_timeout_seconds` controls how long the shutdown path will wait for the background worker thread to complete. Configure via env var `FAPILOG_CORE__SHUTDOWN_TIMEOUT_SECONDS`. --- _Use the lifecycle helpers to ensure buffered logs are flushed before your app exits._