import logging
logging.basicConfig(level=logging.INFO)
logging.info('Application started')
logging.warning('Config value missing, using default')
Topics
20
Async Generators & Async Context Managers
Command-Line Interfaces (argparse)
Comprehensions & Generators
Concurrency (asyncio/threading/multiprocessing)
Data Types & Structures
Dataclasses & NamedTuples
Debugging & Profiling
Decorators
Descriptors & Properties
Exception Handling
File I/O & Context Managers
Functions & Scope
functools & Functional Programming Tools
Iterators & the Iterator Protocol
Logging
Magic Methods & Operator Overloading
Memory Management & Garbage Collection
Metaclasses & Class Customization
Modules & Packaging
Multiple Inheritance & MRO
Logging
15 questions found
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.
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.
Debugging & Profiling;Exception Handling
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.
Logging;Debugging & Profiling
What is the relationship between Logger, Handler, Formatter, and Filter in the logging module?
IntermediateA 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.
Logging;Debugging & Profiling
Why is it recommended to use logging.getLogger(__name__) instead of the root logger in library and module code?
IntermediateUsing __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.
Modules & Packaging;Logging
How do RotatingFileHandler and TimedRotatingFileHandler prevent log files from growing unbounded?
AdvancedRotatingFileHandler 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.
File I/O & Context Managers;Memory Management & Garbage Collection
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.
Modules & Packaging;Logging
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().
Exception Handling;Debugging & Profiling
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.
Logging;Modules & Packaging
How do you include structured context (like a request ID) in every log message using LoggerAdapter or extra?
IntermediateThe 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.
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?
AdvancedThe 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.
Concurrency (asyncio/threading/multiprocessing);File I/O & Context Managers
Showing 1–10 of 15