# Configuration Configure fapilog using presets for quick setup, environment variables, or the `Settings` class for full control. ## Choosing an Approach | Situation | Recommended Approach | Why | |-----------|---------------------|-----| | **Just getting started** | `get_logger(preset="...")` | Zero config, sensible defaults | | **FastAPI app** | `FastAPIBuilder().with_preset("production").build()` | Automatic middleware and request context | | **Writing new code** | `LoggerBuilder()` | IDE autocomplete, type safety, discoverable API | | **Config from env/files** | `Settings` + environment variables | 12-factor apps, Kubernetes, external config | | **Need compliance presets** | `LoggerBuilder().with_redaction(preset="GDPR_PII")` | One-liner GDPR, HIPAA, PCI-DSS protection | **Quick decision:** ``` Start here │ ├── Want sensible defaults with minimal code? │ └── Use presets: get_logger(preset="production") │ ├── Want IDE autocomplete and type checking? │ └── Use Builder: LoggerBuilder().with_preset("production").build() │ └── Config comes from environment or external files? └── Use Settings + env vars: FAPILOG_CORE__LOG_LEVEL=INFO ``` All approaches can be combined. For example, start with a preset and customize with the builder: ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_preset("production") # Start with production defaults .with_redaction(preset="HIPAA_PHI") # Add HIPAA compliance .with_sampling(rate=0.1) # Sample 10% of debug logs .build() ) ``` ## How Configuration Layers Work Fapilog's three configuration methods form a layered hierarchy: ``` ┌─────────────────────────────────────────────────────────┐ │ Builder Methods (highest priority) │ │ .with_level("DEBUG") │ ├─────────────────────────────────────────────────────────┤ │ Settings Object │ │ Settings(core__log_level="INFO") │ ├─────────────────────────────────────────────────────────┤ │ Environment Variables (lowest priority) │ │ FAPILOG_CORE__LOG_LEVEL=WARNING │ └─────────────────────────────────────────────────────────┘ ``` **How they interact:** 1. **Builder creates Settings** - When you call `.build()`, the builder constructs a `Settings` object internally with all your configured values. 2. **Settings reads environment variables** - The `Settings` class uses Pydantic's env var support. Any setting not explicitly provided is automatically populated from environment variables. 3. **Explicit values override env vars** - Builder methods and explicit `Settings` parameters take precedence over environment variables. **Example:** If you set `FAPILOG_CORE__LOG_LEVEL=WARNING` in your environment: ```python # Environment variable applies (WARNING) logger = LoggerBuilder().add_stdout().build() # Builder method overrides env var (DEBUG) logger = LoggerBuilder().with_level("DEBUG").add_stdout().build() # Explicit Settings parameter overrides env var (ERROR) settings = Settings(core__log_level="ERROR") logger = get_logger(settings=settings) ``` This layering enables a common 12-factor pattern: define defaults in code with the builder, allow operational overrides via environment variables at deployment time. ## Configuration Presets (Recommended) Presets provide pre-configured settings for common use cases. Use a preset when you want quick, sensible defaults: ```python from fapilog import get_logger, get_async_logger # Choose the preset that matches your use case logger = get_logger(preset="dev") # Local development logger = get_logger(preset="production") # Durable production (never drops logs) logger = get_logger(preset="adaptive") # Auto-scaling under load logger = get_logger(preset="serverless") # Lambda, Cloud Run, Azure Functions logger = get_logger(preset="hardened") # Regulated environments (HIPAA, PCI) logger = get_logger(preset="minimal") # Backwards compatible default ``` **See [Presets Guide](presets.md)** for complete documentation including: - Decision matrix for choosing the right preset - Detailed comparison of presets - Full settings tables and customization examples ### Quick Comparison | Preset | Drops Logs? | File Output | Redaction | Best For | |--------|-------------|-------------|-----------|----------| | `dev` | No | No | No | Local development | | `minimal` | Default | No | No | Migration, explicit defaults | | `production` | Never | Yes | Yes | Audit trails, compliance | | `adaptive` | If needed | Fallback only | Yes | Auto-scaling under load | | `serverless` | If needed | No | Yes | Lambda/Cloud Functions | | `hardened` | Never | Yes | Yes (HIPAA+PCI) | Regulated environments | ### FastAPI Integration Use `FastAPIBuilder` for FastAPI apps: ```python from fastapi import Depends, FastAPI from fapilog.fastapi import FastAPIBuilder, get_request_logger app = FastAPI( lifespan=FastAPIBuilder() .with_preset("production") .build() ) @app.get("/users/{user_id}") async def get_user(user_id: int, logger=Depends(get_request_logger)): await logger.info("Fetching user", user_id=user_id) return {"user_id": user_id} ``` See the [FastAPI Integration Guide](fastapi.md) for full configuration options including `skip_paths()`, `include_headers()`, and `sample_rate()`. ### Preset vs Settings Presets and `Settings` are mutually exclusive. Choose one approach: ```python # Option 1: Use a preset (simple) logger = get_logger(preset="production") # Option 2: Use Settings (full control) logger = get_logger(settings=Settings(...)) # NOT allowed - raises ValueError logger = get_logger(preset="production", settings=Settings(...)) ``` If you need customization beyond what presets offer, use the `Settings` class directly or customize via the Builder API. ## Output format Use `format` to control stdout output without building a full `Settings` object: ```python from fapilog import get_logger logger = get_logger(format="auto") # Default: pretty in TTY, JSON when piped logger = get_logger(format="pretty") # Force human-readable output logger = get_logger(format="json") # Force structured JSON ``` Notes: - `format` is mutually exclusive with `settings`. - If both `preset` and `format` are provided, `format` overrides the preset's stdout sink. - When `settings` is omitted, `format` defaults to `auto`. ## Default behaviors When you call `get_logger()` without a preset, settings, or `FAPILOG_CORE__LOG_LEVEL`, fapilog selects a sensible default log level: - TTY (interactive terminal): `DEBUG` - Non-TTY (pipes, scripts): `INFO` - CI: forces `INFO` even if TTY Explicit `core.log_level` or a preset always overrides these defaults. ## Environment Auto-Detection When you call `get_logger()` without a preset or settings, fapilog automatically detects your runtime environment and applies lightweight configuration tweaks. This is controlled by `auto_detect=True` (the default). | Detected Environment | Detection Method | Applied Configuration | |---------------------|------------------|----------------------| | Lambda | `AWS_LAMBDA_FUNCTION_NAME` env var | Smaller batches (10), faster flush (0.1s), smaller queue (1000) | | Kubernetes | `/var/run/secrets/kubernetes.io/serviceaccount` or `POD_NAME` env var | INFO level, `kubernetes` enricher | | Docker | `/.dockerenv` file or `/proc/1/cgroup` contains "docker" | INFO level | | CI | Common CI env vars (`CI`, `GITHUB_ACTIONS`, etc.) | INFO level | | Local | Default fallback | Uses TTY-based log level defaults | **Important:** Auto-detection applies incremental tweaks to the base configuration—it does **not** apply full preset configurations. For example: - Auto-detecting Lambda adds smaller batches and the `runtime_info` enricher - `preset="serverless"` provides the complete serverless config **including redactors** If you need full preset behavior (especially redaction) in cloud environments, use explicit presets: ```python # Full preset with redaction enabled logger = get_logger(preset="serverless") # Auto-detect only (applies tweaks but no redaction beyond url_credentials default) logger = get_logger() # Explicit environment without full preset logger = get_logger(environment="lambda") # Same as auto-detect for Lambda ``` To disable auto-detection entirely: ```python logger = get_logger(auto_detect=False) ``` On sink write failures (exceptions raised by a sink), fapilog falls back to stderr. If stderr fails too, the entry is dropped. Diagnostics warnings are emitted when internal diagnostics are enabled: ```bash export FAPILOG_CORE__INTERNAL_LOGGING_ENABLED=true ``` ## Quick setup (env) ```bash # Log level export FAPILOG_CORE__LOG_LEVEL=INFO # Rotating file sink (optional) export FAPILOG_SINK_CONFIG__ROTATING_FILE__DIRECTORY=/var/log/myapp export FAPILOG_SINK_CONFIG__ROTATING_FILE__MAX_BYTES="10 MB" export FAPILOG_SINK_CONFIG__ROTATING_FILE__INTERVAL_SECONDS="daily" # Performance tuning export FAPILOG_CORE__BATCH_MAX_SIZE=128 export FAPILOG_CORE__MAX_QUEUE_SIZE=10000 ``` ## Programmatic settings ```python from fapilog import Settings, get_logger settings = Settings( core__log_level="INFO", core__enable_metrics=True, http__endpoint=None, # default stdout/file selection applies ) logger = get_logger(settings=settings) logger.info("configured", queue=settings.core.max_queue_size) ``` Size and duration fields accept human-readable strings (e.g., `"10 MB"`, `"5s"`) as well as numeric values. Rotation keywords (`"hourly"`, `"daily"`, `"weekly"`) apply to rotation interval settings and represent fixed intervals (not wall-clock boundaries). ## Common patterns - **Stdout auto (default)**: pretty in TTY, JSON when piped. - **Rotating file sink**: set `FAPILOG_SINK_CONFIG__ROTATING_FILE__DIRECTORY`; tune rotation via `FAPILOG_SINK_CONFIG__ROTATING_FILE__MAX_BYTES`, `FAPILOG_SINK_CONFIG__ROTATING_FILE__MAX_FILES`. - **HTTP sink**: set `FAPILOG_HTTP__ENDPOINT` and optional timeout/retry envs. - **Metrics**: set `FAPILOG_CORE__ENABLE_METRICS=true` to record internal metrics. ## Drop and Dedupe Visibility When `drop_on_full=True` (the default), events may be dropped during queue backpressure. Similarly, error deduplication (`error_dedupe_window_seconds`) suppresses duplicate ERROR/CRITICAL messages. By default, these events are silently handled. Enable `emit_drop_summary` to receive visibility into dropped and deduplicated events: ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_drop_summary(enabled=True, window_seconds=60.0) .build() ) ``` Or via environment variables: ```bash export FAPILOG_CORE__EMIT_DROP_SUMMARY=true export FAPILOG_CORE__DROP_SUMMARY_WINDOW_SECONDS=60 ``` When enabled: - **Drop summaries**: Emitted when events are dropped due to backpressure. Contains `dropped_count` and `window_seconds`. - **Dedupe summaries**: Emitted when error deduplication window expires with suppressed messages. Contains `error_message`, `suppressed_count`, and `window_seconds`. Summary events are: - Level `WARNING` (drops) or `INFO` (dedupe) - Marked with `data._fapilog_internal: True` for filtering - Rate-limited by `drop_summary_window_seconds` (default: 60s, minimum: 1s) - Written directly to sinks, bypassing the queue This feature is disabled by default to maintain backwards compatibility. ## Deprecated setting: legacy sampling `observability.logging.sampling_rate` is deprecated and now raises a `DeprecationWarning`. Move to filter-based sampling to avoid double-sampling and to unlock sampling metrics: ```yaml core: filters: ["sampling"] filter_config: sampling: config: sample_rate: 0.25 ``` ## Plugin Security By default, fapilog only loads **built-in plugins**. External plugins (registered via Python entry points) are blocked to prevent arbitrary code execution from untrusted packages. ### Enabling External Plugins To use external plugins, explicitly opt-in using one of these approaches: **Recommended: Allowlist specific plugins** ```python from fapilog import Settings, get_logger settings = Settings(plugins={"allowlist": ["my-trusted-sink", "approved-enricher"]}) logger = get_logger(settings=settings) ``` ```bash # Via environment variable export FAPILOG_PLUGINS__ALLOWLIST='["my-trusted-sink", "approved-enricher"]' ``` **Less secure: Allow all external plugins** ```python settings = Settings(plugins={"allow_external": True}) ``` ```bash export FAPILOG_PLUGINS__ALLOW_EXTERNAL=true ``` ### Security Implications External plugins can execute arbitrary code during loading. Only enable plugins you trust: - **Allowlist approach**: Limits exposure to specific, known plugins - **allow_external=True**: Permits any entry point plugin (use with caution) When external plugins are loaded, a diagnostic warning is emitted to help track plugin sources. ### Migration from Previous Versions If you were using external plugins that now fail to load, add them to the allowlist: ```python # Before (external plugins loaded automatically) settings = Settings(core={"sinks": ["external-sink"]}) # After (explicit opt-in required) settings = Settings( core={"sinks": ["external-sink"]}, plugins={"allowlist": ["external-sink"]}, ) ``` ## Shutdown Handler Installation Fapilog automatically installs signal handlers (SIGTERM/SIGINT) and atexit handlers for graceful shutdown. These handlers ensure pending logs are drained before process exit. ### Lazy Installation (Default) Handlers are installed lazily on first logger start, not at module import time. This design: - Avoids conflicts with frameworks (FastAPI, Uvicorn, Gunicorn) that manage their own signal handlers - Follows library best practices by not modifying global state at import time - Allows opt-out before any handlers are installed ```python import fapilog # No handlers installed yet - safe to configure framework handlers first logger = fapilog.get_logger() # Handlers installed here ``` ### Manual Installation For cases where you need handlers installed before creating a logger, use `install_shutdown_handlers()`: ```python import fapilog # Explicitly install handlers early fapilog.install_shutdown_handlers() # Later, create loggers logger = fapilog.get_logger() ``` This function is idempotent - calling it multiple times has no effect after the first call. ### Disabling Handlers To prevent handler installation entirely: ```bash # Disable signal handlers (atexit still active) export FAPILOG_CORE__SIGNAL_HANDLER_ENABLED=false # Disable atexit drain (signal handlers still active) export FAPILOG_CORE__ATEXIT_DRAIN_ENABLED=false # Disable both export FAPILOG_CORE__SIGNAL_HANDLER_ENABLED=false export FAPILOG_CORE__ATEXIT_DRAIN_ENABLED=false ``` Or via code: ```python from fapilog import Settings, get_logger settings = Settings( core={ "signal_handler_enabled": False, "atexit_drain_enabled": False, } ) logger = get_logger(settings=settings) ``` ### Framework Integration When using fapilog with frameworks that manage their own shutdown: **FastAPI/Uvicorn**: The lazy installation works well because FastAPI's lifespan handlers run after fapilog handlers are installed. Use the `setup_logging()` lifespan for proper integration. **Gunicorn**: If you need to ensure fapilog handlers don't conflict, disable them and rely on Gunicorn's worker lifecycle: ```python settings = Settings(core={"signal_handler_enabled": False}) logger = get_logger(settings=settings) ``` **Testing**: The handlers are designed for test isolation. Each test can reset handler state if needed using internal APIs. ## Full reference - **[Execution Modes](execution-modes.md)** - Understanding async and sync facades with dedicated thread architecture - **[Configuration Map](../api-reference/configuration-map.md)** - Complete reference mapping every setting to its env var and builder method - **[Environment Variables](environment-variables.md)** - Full matrix of env names and aliases (including short forms like `FAPILOG_CLOUDWATCH__REGION`)