Why fapilog?
Fapilog is an async-first structured logging library for Python services. If you’re building FastAPI applications, microservices, or any system where logging shouldn’t slow down your requests, fapilog was designed for your use case.
Quick Decision Guide
If you need… |
Best choice |
|---|---|
Async-first logging with backpressure handling |
fapilog |
Simplest possible API, sync is fine |
loguru |
Processor ecosystem, stdlib compatibility |
structlog |
Full observability (logs + traces + metrics) |
OpenTelemetry |
Zero dependencies |
stdlib logging |
Not sure? Read on for details, or jump to the full comparison.
Is Fapilog Right for Your Project?
Use fapilog if you’re building:
Microservices and distributed systems - Request ID tracking, async-first, multiple cloud sinks
FastAPI/async applications - Built-in middleware, non-blocking logging
High-throughput services - Backpressure handling, won’t impact request latency
Production services at scale - Reliability features (circuit breaker, sink routing, batching)
Applications with compliance needs - Built-in PII redaction (field/regex/URL)
Cloud-native applications - Native CloudWatch, Loki, PostgreSQL sinks
Services with variable load patterns - Backpressure policies handle bursts gracefully
What Makes Fapilog Different
1. True Async-First Architecture
Most Python logging libraries are synchronous—they block your application while writing to disk or network. With fapilog, your log calls never block on I/O. Whether you use get_logger() in sync code or get_async_logger() in async code, the actual writes happen in background workers. A slow CloudWatch API or full disk won’t stall your request handlers.
Your code Background worker
│ │
log.info("msg") ──queue──→ │
│ (returns) ↓
│ serialize → enrich → write to sink
↓
(continues)
The difference between the two APIs is the calling convention, not the architecture:
# Sync API - for sync code or when you don't want to await
from fapilog import get_logger
logger = get_logger()
logger.info("Request processed") # Enqueues and returns
# Async API - for async code with tighter event loop integration
from fapilog import get_async_logger
logger = await get_async_logger()
await logger.info("Request processed") # Enqueues and returns
In async contexts, both are non-blocking. In sync contexts, get_logger() may briefly wait (up to 50ms by default) for queue space before returning—still far faster than blocking on network I/O.
2. Backpressure Handling
What happens when logs arrive faster than they can be written? Most libraries either block (hurting latency) or silently drop logs (hurting reliability). Fapilog lets you configure the tradeoff:
from fapilog import LoggerBuilder
# Protect latency: wait briefly, then drop if still full
logger = (
LoggerBuilder()
.with_backpressure(wait_ms=50, drop_on_full=True)
.build()
)
# Protect durability: never lose logs (may block longer)
logger = (
LoggerBuilder()
.with_backpressure(wait_ms=0, drop_on_full=False)
.build()
)
Configuration |
Behavior |
|---|---|
|
Wait up to 50ms for space, then drop (default) |
|
Drop immediately if queue full |
|
Block indefinitely until space available |
|
Wait 100ms, then block indefinitely |
3. Built-in Redaction
PII and secrets are masked by default with the production preset. No compliance surprises.
from fapilog import get_logger
logger = get_logger(preset="production")
logger.info("User login", password="secret123", email="user@example.com")
# Output: password=**REDACTED**, email=**REDACTED**
4. FastAPI Integration
One line gets you request logging, correlation IDs, and context propagation:
from fastapi import FastAPI
from fapilog.fastapi import FastAPIBuilder
app = FastAPI(
lifespan=FastAPIBuilder()
.with_preset("fastapi")
.build()
)
5. Level-Based Sink Routing
Send errors to a database for alerting while info logs go to stdout:
from fapilog import LoggerBuilder
logger = (
LoggerBuilder()
.with_routing(
rules=[
{"levels": ["ERROR", "CRITICAL"], "sinks": ["postgres"]},
{"levels": ["DEBUG", "INFO", "WARNING"], "sinks": ["stdout"]},
]
)
.build()
)
Honest Trade-offs
Consideration |
Reality |
|---|---|
Maturity |
New (2024) vs structlog (2013), loguru (2018) |
Community |
Growing vs established ecosystems with more tutorials and Stack Overflow answers |
Sync use cases |
If you don’t need async, loguru is simpler |
Processor ecosystem |
structlog has more built-in processors and community extensions |
When fapilog might not be the best fit
Simple scripts or CLIs: If you’re writing to fast local stdout with minimal structure, stdlib or loguru is simpler.
Existing structlog codebases: Migration effort may not be worth it unless you’re hitting blocking I/O issues.
Need full observability: If you want unified logs + traces + metrics, OpenTelemetry is the industry standard.
Learn More
Feature List - Complete overview of what fapilog offers
Full Feature Comparison - Detailed comparison with structlog, loguru, stdlib, and OpenTelemetry
Getting Started - Install and start logging in 2 minutes
Architecture - How the async pipeline works
Cookbook - Copy-paste solutions for common problems