# Migrating from Settings to Builder This guide helps you convert existing Settings-based configurations to the Builder API. ## When to Migrate **Migrate to Builder when:** - Starting a new project or feature - Refactoring existing configuration code - Want better IDE support and discoverability - Configuration is defined in Python code **Keep Settings when:** - Configuration is entirely environment-variable driven - Need runtime configuration changes after logger creation - Integrating with external config management systems (Consul, etc.) - Configuration is loaded from JSON/YAML files --- ## Quick Conversion Reference ### Core Settings | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(log_level="INFO")` | `.with_level("INFO")` | | `CoreSettings(app_name="my-app")` | `.with_app_name("my-app")` | | `CoreSettings(max_queue_size=10000)` | `.with_queue_size(10000)` | | `CoreSettings(batch_max_size=100)` | `.with_batch_size(100)` | | `CoreSettings(batch_timeout_seconds=1.0)` | `.with_batch_timeout("1s")` | | `CoreSettings(worker_count=4)` | `.with_workers(4)` | | `CoreSettings(shutdown_timeout_seconds=5.0)` | `.with_shutdown_timeout("5s")` | | `CoreSettings(backpressure_wait_ms=50)` | `.with_backpressure(wait_ms=50)` | | `CoreSettings(drop_on_full=False)` | `.with_backpressure(drop_on_full=False)` | | `CoreSettings(enable_metrics=True)` | `.with_metrics(enabled=True)` | | `CoreSettings(sink_parallel_writes=True)` | `.with_parallel_sink_writes(True)` | | `CoreSettings(sink_concurrency=8)` | `.with_sink_concurrency(limit=8)` | ### Circuit Breaker | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(sink_circuit_breaker_enabled=True)` | `.with_circuit_breaker(enabled=True)` | | `CoreSettings(sink_circuit_breaker_failure_threshold=5)` | `.with_circuit_breaker(failure_threshold=5)` | | `CoreSettings(sink_circuit_breaker_recovery_timeout_seconds=30)` | `.with_circuit_breaker(recovery_timeout="30s")` | ### Sinks | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(sinks=["stdout_json"])` | `.add_stdout()` | | `CoreSettings(sinks=["stdout_pretty"])` | `.add_stdout(format="pretty")` | | `CoreSettings(sinks=["rotating_file"])` + config | `.add_file(directory, ...)` | | `CoreSettings(sinks=["http"])` + config | `.add_http(endpoint, ...)` | | `CoreSettings(sinks=["webhook"])` + config | `.add_webhook(endpoint, ...)` | | `CoreSettings(sinks=["cloudwatch"])` + config | `.add_cloudwatch(log_group, ...)` | | `CoreSettings(sinks=["loki"])` + config | `.add_loki(url, ...)` | | `CoreSettings(sinks=["postgres"])` + config | `.add_postgres(...)` | ### Filters | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(filters=["sampling"])` + config | `.with_sampling(rate=0.1)` | | `CoreSettings(filters=["adaptive_sampling"])` + config | `.with_adaptive_sampling(...)` | | `CoreSettings(filters=["trace_sampling"])` + config | `.with_trace_sampling(...)` | | `CoreSettings(filters=["rate_limit"])` + config | `.with_rate_limit(...)` | | `CoreSettings(filters=["first_occurrence"])` + config | `.with_first_occurrence(...)` | ### Redaction | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(redactors=["field_mask"])` + config | `.with_field_mask(fields, ...)` | | `CoreSettings(redactors=["regex_mask"])` + config | `.with_regex_mask(patterns, ...)` | | `CoreSettings(redactors=["url_credentials"])` | `.with_url_credential_redaction()` | ### Enrichers | Settings Pattern | Builder Pattern | |------------------|-----------------| | `CoreSettings(enrichers=["runtime_info", "context_vars"])` | `.with_enrichers("runtime_info", "context_vars")` | | `CoreSettings(default_bound_context={...})` | `.with_context(key=value, ...)` | --- ## Step-by-Step Migration ### Example 1: Basic Configuration **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings settings = Settings( core=CoreSettings( log_level="INFO", sinks=["stdout_json"], ) ) logger = get_logger(name="app", settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_name("app") .with_level("INFO") .add_stdout() .build() ) ``` --- ### Example 2: File Sink with Rotation **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings, SinkConfig, RotatingFileSinkSettings settings = Settings( core=CoreSettings( log_level="INFO", sinks=["rotating_file"], ), sink_config=SinkConfig( rotating_file=RotatingFileSinkSettings( directory="/var/log/myapp", max_bytes="50 MB", max_files=10, compress_rotated=True, ) ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_level("INFO") .add_file( "/var/log/myapp", max_bytes="50 MB", max_files=10, compress=True, ) .build() ) ``` --- ### Example 3: CloudWatch with Circuit Breaker **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings, SinkConfig, CloudWatchSinkSettings settings = Settings( core=CoreSettings( log_level="INFO", sinks=["cloudwatch"], sink_circuit_breaker_enabled=True, sink_circuit_breaker_failure_threshold=5, sink_circuit_breaker_recovery_timeout_seconds=30.0, ), sink_config=SinkConfig( cloudwatch=CloudWatchSinkSettings( log_group_name="/myapp/prod", region="us-east-1", ) ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_level("INFO") .add_cloudwatch("/myapp/prod", region="us-east-1") .with_circuit_breaker( enabled=True, failure_threshold=5, recovery_timeout="30s", ) .build() ) ``` --- ### Example 4: Sampling and Rate Limiting **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings, FilterConfig, SamplingFilterSettings, RateLimitFilterSettings settings = Settings( core=CoreSettings( log_level="INFO", sinks=["stdout_json"], filters=["sampling", "rate_limit"], ), filter_config=FilterConfig( sampling=SamplingFilterSettings(sample_rate=0.1), rate_limit=RateLimitFilterSettings( capacity=100, refill_rate_per_sec=10.0, ), ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_level("INFO") .add_stdout() .with_sampling(rate=0.1) .with_rate_limit(capacity=100, refill_rate=10.0) .build() ) ``` --- ### Example 5: Redaction Configuration **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings, RedactorConfig, FieldMaskSettings, RegexMaskSettings settings = Settings( core=CoreSettings( log_level="INFO", sinks=["stdout_json"], redactors=["field_mask", "regex_mask", "url_credentials"], ), redactor_config=RedactorConfig( field_mask=FieldMaskSettings( fields_to_mask=["password", "api_key"], mask_string="[REDACTED]", ), regex_mask=RegexMaskSettings( patterns=["(?i).*secret.*"], ), ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_level("INFO") .add_stdout() .with_field_mask(["password", "api_key"], mask="[REDACTED]") .with_regex_mask(["(?i).*secret.*"]) .with_url_credential_redaction() .build() ) ``` --- ### Example 6: Sink Routing **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import CoreSettings, SinkConfig, CloudWatchSinkSettings, SinkRoutingSettings settings = Settings( core=CoreSettings( sinks=["stdout_json", "cloudwatch"], ), sink_config=SinkConfig( cloudwatch=CloudWatchSinkSettings( log_group_name="/myapp/errors", region="us-east-1", ) ), sink_routing=SinkRoutingSettings( enabled=True, rules=[ {"levels": ["ERROR", "CRITICAL"], "sinks": ["cloudwatch"]}, {"levels": ["INFO", "WARNING"], "sinks": ["stdout_json"]}, ], fallback_sinks=["stdout_json"], ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .add_stdout() .add_cloudwatch("/myapp/errors", region="us-east-1") .with_routing( rules=[ {"levels": ["ERROR", "CRITICAL"], "sinks": ["cloudwatch"]}, {"levels": ["INFO", "WARNING"], "sinks": ["stdout_json"]}, ], fallback=["stdout_json"], ) .build() ) ``` --- ### Example 7: Full Production Configuration **Before (Settings):** ```python from fapilog import Settings, get_logger from fapilog.core.settings import ( CoreSettings, SinkConfig, FilterConfig, RedactorConfig, RotatingFileSinkSettings, CloudWatchSinkSettings, SamplingFilterSettings, FieldMaskSettings, ) settings = Settings( core=CoreSettings( log_level="INFO", app_name="my-service", sinks=["rotating_file", "cloudwatch"], filters=["sampling"], redactors=["field_mask", "url_credentials"], max_queue_size=10000, batch_max_size=100, worker_count=2, sink_circuit_breaker_enabled=True, sink_circuit_breaker_failure_threshold=5, drop_on_full=False, enable_metrics=True, ), sink_config=SinkConfig( rotating_file=RotatingFileSinkSettings( directory="logs/app", max_bytes="100 MB", max_files=20, compress_rotated=True, ), cloudwatch=CloudWatchSinkSettings( log_group_name="/myapp/prod", region="us-east-1", ), ), filter_config=FilterConfig( sampling=SamplingFilterSettings(sample_rate=0.5), ), redactor_config=RedactorConfig( field_mask=FieldMaskSettings( fields_to_mask=["password", "api_key", "ssn"], ), ), ) logger = get_logger(settings=settings) ``` **After (Builder):** ```python from fapilog import LoggerBuilder logger = ( LoggerBuilder() .with_level("INFO") .with_app_name("my-service") # Sinks .add_file("logs/app", max_bytes="100 MB", max_files=20, compress=True) .add_cloudwatch("/myapp/prod", region="us-east-1") # Filters .with_sampling(rate=0.5) # Redaction .with_field_mask(["password", "api_key", "ssn"]) .with_url_credential_redaction() # Performance .with_queue_size(10000) .with_batch_size(100) .with_workers(2) # Reliability .with_circuit_breaker(enabled=True, failure_threshold=5) .with_backpressure(drop_on_full=False) # Observability .with_metrics(enabled=True) .build() ) ``` --- ## Handling Presets If you're using environment variables with Settings, consider using Builder presets: **Before (Environment-driven Settings):** ```python from fapilog import Settings, get_logger # Settings reads from FAPILOG_* environment variables settings = Settings() logger = get_logger(settings=settings) ``` **After (Builder with Preset):** ```python from fapilog import LoggerBuilder import os # Use preset based on environment preset = "production" if os.getenv("ENV") == "production" else "dev" logger = ( LoggerBuilder() .with_preset(preset) .build() ) ``` Or continue using Settings for pure environment-variable configuration - the Builder is not required for all use cases. --- ## Migration Tips 1. **Start with presets** - Use `.with_preset()` as a base and override specific values 2. **Method chaining order doesn't matter** - Builder accumulates configuration; order is flexible 3. **Duration strings accepted** - Use `"30s"`, `"5m"`, `"1h"` instead of seconds 4. **Size strings accepted** - Use `"10 MB"`, `"1 GB"` instead of bytes 5. **Sinks auto-registered** - `add_*` methods handle both sink registration and configuration 6. **Filters auto-enabled** - `with_sampling()`, `with_rate_limit()`, etc. enable the filter automatically --- ## Next Steps - [Builder API Reference](../api-reference/builder.md) - Complete method documentation - [Builder Configuration Guide](../user-guide/builder-configuration.md) - Task-oriented tutorials - [Example Configurations](../examples/builder-configurations.md) - Real-world examples