Logging
errortools.logging is a loguru-inspired structured logger with no external dependencies.
Quick start
from errortools.logging import logger
logger.info("Server started on port {}", 8080)
logger.warning("Disk at {pct:.1f}%", pct=92.5)
logger.success("All systems operational")
Output (colorized in terminal):
2026-04-30 08:34:21.850 | ℹ INFO | <string>:<module>:3 - Server started on port 8080
2026-04-30 08:34:21.851 | ⚠ WARNING | <string>:<module>:4 - Disk at 92.5%
2026-04-30 08:34:21.851 | ✔ SUCCESS | <string>:<module>:5 - All systems operational
Log levels
Method |
Level |
Number |
|---|---|---|
|
TRACE |
5 |
|
DEBUG |
10 |
|
INFO |
20 |
|
SUCCESS |
25 |
|
WARNING |
30 |
|
ERROR |
40 |
|
CRITICAL |
50 |
Sinks
Add and remove log destinations at runtime.
Stream sink
import sys
from errortools.logging import logger
# Add stdout sink
logger.add(sys.stdout, level="WARNING")
File sink
# File with rotation and retention
sid = logger.add(
"logs/app.log",
rotation=10_485_760, # 10 MB
retention=5 # Keep 5 old files
)
Callable sink
# Any callable
logger.add(print)
logger.add(lambda msg: send_to_service(msg))
Remove sinks
# Remove by ID
logger.remove(sid)
# Remove all sinks
logger.remove()
Level filtering
from errortools.logging import logger, Level
# Set global level
logger.set_level("WARNING") # or Level.WARNING or 30
logger.debug("dropped") # Below threshold — not emitted
logger.warning("kept") # At threshold — emitted
Context binding
bind() returns a new logger with extra fields in every record.
# Bind request context
req_log = logger.bind(request_id="abc-123", user="alice")
req_log.info("Request received")
# Stack bindings
db_log = req_log.bind(db="postgres")
db_log.debug("Query OK") # Contains: request_id, user, db
Exception capture
exception()
Log with automatic traceback capture:
try:
connect()
except ConnectionError:
logger.exception("DB connection failed") # Logs at ERROR + traceback
opt(exception=True)
Equivalent long-hand:
logger.opt(exception=True).error("DB connection failed")
catch()
Auto-log and suppress exceptions.
As context manager
# Suppress and log
with logger.catch():
int("not a number") # Logged at ERROR, then suppressed
# Re-raise after logging
with logger.catch(ConnectionError, reraise=True):
connect()
As decorator
@logger.catch(ValueError)
def parse(s: str) -> int:
return int(s)
parse("bad") # Logged and suppressed
Custom format
logger.add(
"debug.log",
fmt="{time} | {level} | {name}:{function}:{line} - {message}",
)
Available placeholders
{time}- Timestamp{level}- Log level{name}- Module name{file}- File name{line}- Line number{function}- Function name{message}- Log message
Advanced usage
Multiple sinks with different levels
# Console: WARNING and above
logger.add(sys.stderr, level="WARNING")
# File: DEBUG and above
logger.add("debug.log", level="DEBUG")
# Critical alerts: CRITICAL only
logger.add(send_alert, level="CRITICAL")
Structured logging
req_log = logger.bind(
request_id="abc-123",
user_id=42,
endpoint="/api/users"
)
req_log.info("Request started")
req_log.success("Request completed in {ms}ms", ms=123)