Logging

15 questions found

Why should you use the logging module instead of print() for diagnostic output?

Beginner
logging supports severity levels (DEBUG, INFO, WARNING, ERROR, CRITICAL), can be routed to multiple destinations (console, files, network) simultaneously, includes timestamps and context automatically, and can be toggled or filtered without editing code -- print() offers none of this and is hard to disable in production.
import logging

logging.basicConfig(level=logging.INFO)
logging.info('Application started')
logging.warning('Config value missing, using default')
Real-world example A production web service logs request errors to a file and sends CRITICAL-level logs to a monitoring alert system, something print() statements can't do.

Common follow-ups: What are the five standard logging levels in order?;How do you disable logging below a certain level?

Debugging & Profiling;Exception Handling

What are Python's five standard logging severity levels, from lowest to highest?

Beginner
DEBUG (detailed diagnostic info), INFO (confirmation things work as expected), WARNING (something unexpected but not fatal), ERROR (a serious problem, functionality failed), and CRITICAL (a very serious error, program may be unable to continue). Each level has a numeric value, and a logger only emits messages at or above its configured threshold.
import logging
logging.basicConfig(level=logging.WARNING)
logging.debug('not shown')
logging.info('not shown')
logging.warning('shown')  # WARNING:root:shown
logging.error('shown')
Real-world example A deployment sets the root logger to WARNING in production to reduce noise, but switches to DEBUG temporarily when investigating a reported bug.

Common follow-ups: How do numeric level values compare (e.g., logging.INFO value)?;How do you set different levels per module?

Logging;Debugging & Profiling

What is the relationship between Logger, Handler, Formatter, and Filter in the logging module?

Intermediate
A Logger is the entry point your code calls (logger.info(...)); it passes log records to one or more Handlers, which decide where output goes (console, file, network). Each Handler can have a Formatter controlling the output's text layout, and Filters can be attached to either Loggers or Handlers to allow/reject records based on custom logic beyond just level.
import logging

logger = logging.getLogger('myapp')
handler = logging.FileHandler('app.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info('Started')
Real-world example A microservice attaches both a console Handler (for local dev) and a FileHandler (for persistent logs) to the same Logger, each with a different Formatter.

Common follow-ups: Can a single logger have multiple handlers with different levels?;What is propagate and when would you disable it?

Logging;Debugging & Profiling

Why is it recommended to use logging.getLogger(__name__) instead of the root logger in library and module code?

Intermediate
Using __name__ creates a logger named after the module's dotted path, forming a hierarchy that mirrors your package structure. This lets consumers of your code configure logging per-module (e.g., silence a noisy submodule while keeping others verbose) and makes log output traceable to its source without every module competing for the single root logger's configuration.
# in mypackage/database.py
import logging
logger = logging.getLogger(__name__)  # 'mypackage.database'

def connect():
    logger.debug('Connecting to database...')
Real-world example A library author uses getLogger(__name__) throughout so downstream applications can selectively enable DEBUG logging just for 'mylib.network' while keeping 'mylib.parsing' at WARNING.

Common follow-ups: How does logger hierarchy and propagation work with dotted names?;Why shouldn't libraries call basicConfig()?

Modules & Packaging;Logging

How do RotatingFileHandler and TimedRotatingFileHandler prevent log files from growing unbounded?

Advanced
RotatingFileHandler rotates the log file once it reaches a specified maxBytes, renaming old files with numeric suffixes and keeping up to backupCount old files. TimedRotatingFileHandler instead rotates on a time interval (e.g., daily, hourly). Both prevent a single log file from consuming unlimited disk space over a long-running application's lifetime.
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler('app.log', maxBytes=10_000_000, backupCount=5)
logger = logging.getLogger('myapp')
logger.addHandler(handler)
Real-world example A long-running background worker uses TimedRotatingFileHandler(when='midnight', backupCount=30) to keep exactly 30 days of daily log files and auto-delete older ones.

Common follow-ups: What happens to the currently-open file handle during rotation?;How does this differ from external log rotation tools like logrotate?

File I/O & Context Managers;Memory Management & Garbage Collection

How do you configure logging using a dictionary via logging.config.dictConfig?

Intermediate
dictConfig accepts a nested dictionary describing formatters, handlers, loggers, and their relationships, letting you define a full logging setup declaratively (often loaded from a JSON or YAML file) rather than imperatively calling addHandler/setFormatter in code. This is the recommended way to manage complex, environment-specific logging configuration.
import logging.config

config = {
    'version': 1,
    'formatters': {'simple': {'format': '%(levelname)s: %(message)s'}},
    'handlers': {'console': {'class': 'logging.StreamHandler', 'formatter': 'simple'}},
    'root': {'handlers': ['console'], 'level': 'INFO'},
}
logging.config.dictConfig(config)
logging.info('Configured via dict')
Real-world example A Django or Flask application loads its LOGGING dict from settings, switching handlers and levels between development and production environments without code changes.

Common follow-ups: How does dictConfig differ from fileConfig?;How do you merge dictConfig with environment variables for secrets?

Modules & Packaging;Logging

How does logger.exception() differ from logger.error(), and when should you use it?

Advanced
logger.exception(msg) is equivalent to logger.error(msg, exc_info=True) -- it automatically attaches the current exception's traceback to the log record. It should only be called from within an except block; calling it outside one logs 'NoneType: None' for the traceback since there's no active exception.
try:
    1 / 0
except ZeroDivisionError:
    logging.exception('Division failed')
    # Logs the error message AND the full traceback automatically
Real-world example A background job's except block calls logger.exception() so the full stack trace lands in the log file for later debugging, without manually calling traceback.format_exc().

Common follow-ups: What does exc_info=True actually capture?;How do you log a traceback without re-raising the exception?

Exception Handling;Debugging & Profiling

What does logging.basicConfig() do, and what are its common pitfalls?

Beginner
basicConfig() configures the root logger with a default handler, formatter, and level in one call -- convenient for quick scripts. Its main pitfall is that it only has an effect the first time it's called (subsequent calls are ignored unless force=True), which confuses developers who call it in multiple places expecting it to reconfigure logging each time.
import logging

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(message)s')
logging.debug('This will show with the configured format')
Real-world example A script's second basicConfig() call with a different level silently has no effect because logging was already configured elsewhere, causing confusing debugging sessions.

Common follow-ups: How do you force reconfiguration with force=True?;Why shouldn't libraries call basicConfig at import time?

Logging;Modules & Packaging

How do you include structured context (like a request ID) in every log message using LoggerAdapter or extra?

Intermediate
The extra parameter passes custom key-value data into a LogRecord for use in a custom Formatter string, while LoggerAdapter wraps a logger to automatically inject the same contextual data (e.g., request_id, user_id) into every call without repeating extra= at each call site -- useful for correlating logs across a single request's lifecycle.
import logging

logger = logging.getLogger('app')
adapter = logging.LoggerAdapter(logger, {'request_id': 'abc-123'})
adapter.info('Processing request')  # includes request_id in every call automatically
Real-world example A web API creates a LoggerAdapter per incoming request carrying its unique request_id, so every log line during that request's handling can be filtered and correlated in log aggregation tools.

Common follow-ups: How does extra interact with reserved LogRecord attribute names?;What's the alternative using contextvars for async code?

Concurrency (asyncio/threading/multiprocessing);Async Generators & Async Context Managers

Why is logging generally considered thread-safe, and what should you still be careful about in multiprocessing?

Advanced
The logging module uses internal locks around handler emit() calls, making it safe for multiple threads to log through the same handler without corrupting output. However, in multiprocessing, each process has its own memory and locks, so multiple processes writing to the same file handler can interleave or corrupt output -- you typically need a QueueHandler/QueueListener setup or process-specific log files instead.
from logging.handlers import QueueHandler, QueueListener
import logging, multiprocessing

q = multiprocessing.Queue()
qh = QueueHandler(q)
logging.getLogger().addHandler(qh)
# A QueueListener in the main process consumes from q and writes safely
Real-world example A multiprocessing data pipeline routes all worker process logs through a shared Queue to a single QueueListener that writes safely to one file, avoiding interleaved or corrupted log lines.

Common follow-ups: Why can't multiple processes safely share a FileHandler directly?;How does QueueListener differ from SocketHandler for centralized logging?

Concurrency (asyncio/threading/multiprocessing);File I/O & Context Managers

Showing 1–10 of 15