Loguru Python Logging

raw JSON →
0.7.3 verified Tue May 12 auth: no python install: verified quickstart: stale

Simple Python logging library. Current version: 0.7.3 (Mar 2026). One global logger — no instantiation needed, just import. Default sink is stderr (not stdout). diagnose=True is the DEFAULT — shows variable values in tracebacks — leaks sensitive data in production. Must call logger.remove() before reconfiguring. Library authors must never call logger.add() — use logger.disable() instead. enqueue=True requires logger.complete() on shutdown to flush queued messages.

pip install loguru
error ModuleNotFoundError: No module named 'loguru'
cause The Loguru library has not been installed in the active Python environment.
fix
Run pip install loguru in your terminal.
error Sensitive data leakage in production logs due to diagnose=True
cause The default `diagnose=True` parameter for sinks in Loguru includes local variable values in exception tracebacks, which can expose sensitive information in production environments.
fix
Explicitly set diagnose=False when adding sinks in production: logger.add('file.log', diagnose=False).
error Logs are duplicated
cause The logger is being configured multiple times by repeatedly calling `logger.add()` without first calling `logger.remove()`, or by running configuration code in a multiprocessing environment without proper `if __name__ == '__main__':` guards.
fix
Call logger.remove() (optionally with the handler ID, e.g., logger.remove(0) for the default handler) before logger.add() to prevent duplicate handlers. For multiprocessing, wrap logger configuration in if __name__ == '__main__':.
error KeyError: 'some_key' or ValueError: Single '{' encountered in format string when logging f-strings
cause Loguru's message formatting uses `str.format()` semantics for positional and keyword arguments, which can conflict with f-strings that contain unescaped curly braces not intended as format placeholders.
fix
Escape literal curly braces in f-strings with double braces ({{ and }}) or pass the data as separate arguments to Loguru's logging methods if they are intended for structured logging.
error ValueError: I/O operation error on closed file
cause Another library, IDE, or environment tool has replaced or closed `sys.stderr` or `sys.stdout` while Loguru was still attempting to write to it.
fix
Configure Loguru to use a lambda function as the sink to dynamically retrieve sys.stderr or sys.stdout (e.g., logger.add(lambda m: sys.stderr.write(m))), or ensure the logger is re-initialized if the standard streams might be replaced.
breaking diagnose=True is the DEFAULT for all sinks. It displays variable values in exception tracebacks — leaks passwords, tokens, PII in production logs.
fix Always set diagnose=False in production: logger.add('prod.log', diagnose=False)
gotcha Default sink outputs to stderr, not stdout. Many developers expect stdout. Adding a new sink without calling logger.remove() causes duplicate log output.
fix Call logger.remove() before logger.add() to remove the default stderr handler.
gotcha Library authors must never call logger.add(). Call logger.disable(__name__) instead. Calling add() in a library forces logging config on every downstream application.
fix In library code: logger.disable(__name__). In app code: logger.enable('library_name') to see library logs.
gotcha enqueue=True (multiprocess/async safe) requires logger.complete() on shutdown. Without it, buffered messages in the queue are lost when the process exits.
fix await logger.complete() in async code, or asyncio.run(logger.complete()) at shutdown.
gotcha Passing integer log levels to logger.log() displays as anonymous level ('Level 20') instead of level name. Loguru identifies levels by name, not number.
fix Use string names: logger.log('INFO', 'message') not logger.log(20, 'message')
gotcha logger.add() returns a handler ID integer. logger.remove() requires this ID to remove a specific handler. Calling logger.remove() with no args removes ALL handlers.
fix handler_id = logger.add(...); logger.remove(handler_id) to remove specific sink.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.00s 18.2M
3.10 slim (glibc) - - 0.00s 19M
3.11 alpine (musl) - - 0.00s 20.1M
3.11 slim (glibc) - - 0.00s 21M
3.12 alpine (musl) - - 0.00s 12.0M
3.12 slim (glibc) - - 0.00s 13M
3.13 alpine (musl) - - 0.00s 11.6M
3.13 slim (glibc) - - 0.00s 12M
3.9 alpine (musl) - - 0.00s 17.7M
3.9 slim (glibc) - - 0.00s 18M

Loguru — reconfigure, file rotation, exception catching, structured context.

# pip install loguru
import sys
from loguru import logger

# Reconfigure: remove default stderr, add stdout + file
logger.remove()
logger.add(sys.stdout, level='INFO', diagnose=False)
logger.add('app.log', level='DEBUG', rotation='50 MB', diagnose=False)

# Basic logging
logger.debug('Debug message')
logger.info('Server started on port 8000')
logger.warning('Low disk space')
logger.error('Connection failed')
logger.critical('Database unreachable')

# Exception logging with full traceback
try:
    result = 1 / 0
except ZeroDivisionError:
    logger.exception('Calculation failed')  # logs traceback automatically

# Catch decorator
@logger.catch
def risky_function(x):
    return 100 / x

risky_function(0)  # caught and logged automatically

# Structured context with bind()
request_logger = logger.bind(request_id='req-123', user_id='usr-456')
request_logger.info('Processing payment')

# JSON output for log aggregators
logger.add('app.json', serialize=True, diagnose=False)

# Async / multiprocess safe
logger.add('async.log', enqueue=True, diagnose=False)
# On shutdown:
import asyncio
asyncio.run(logger.complete())  # flush queued messages