Home Python Python Logging: Production Patterns and Best Practices
Intermediate 3 min · July 14, 2026

Python Logging: Production Patterns and Best Practices

Master Python logging for production systems.

N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Everything here is grounded in real deployments.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic Python syntax and functions
  • Understanding of file I/O and exception handling
  • Familiarity with multi-threading or asyncio (for advanced sections)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Use the built-in logging module instead of print statements.
  • Configure loggers with appropriate levels (DEBUG, INFO, WARNING, ERROR, CRITICAL).
  • Use structured logging (JSON) for machine parsing.
  • Implement log rotation to manage disk space.
  • Centralize logs with tools like ELK or Datadog.
✦ Definition~90s read
What is Python Logging?

Python logging is a built-in module that provides a flexible framework for emitting log messages from your application, allowing you to control the level, format, and destination of output.

Think of logging like a flight recorder for your code.
Plain-English First

Think of logging like a flight recorder for your code. It records events, errors, and important milestones so you can replay what happened when something goes wrong. Just as a pilot uses the black box to understand a crash, you use logs to debug production issues.

In production, your code runs without a debugger. When something breaks, you need a detailed record of what happened. That's where logging comes in. Python's built-in logging module is powerful but often misused. Many developers start with print() statements, but that's like using a sticky note when you need a filing cabinet. In this tutorial, you'll learn production-ready logging patterns: how to configure loggers, use structured logging, handle log rotation, and avoid common mistakes. We'll cover real-world scenarios like debugging a silent failure in a web service and tracing requests across microservices. By the end, you'll be able to set up a logging system that helps you sleep better at night.

Why Logging Matters in Production

When your application runs on a server, you can't attach a debugger. Logs are your eyes and ears. They help you understand the flow of execution, detect anomalies, and diagnose failures. Without proper logging, you're flying blind. Consider a web service that occasionally returns 500 errors. Without logs, you have no idea why. With structured logs, you can see the exact request parameters, the stack trace, and the database query that failed. Logging also aids in auditing, compliance, and performance monitoring. In this section, we'll explore the core concepts of Python's logging module: loggers, handlers, formatters, and filters. You'll learn how to set up a basic configuration that scales.

basic_logging.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
import logging

# Basic configuration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# Create a logger
logger = logging.getLogger(__name__)

logger.info('Application started')
try:
    1 / 0
except ZeroDivisionError:
    logger.exception('Division by zero occurred')
Output
2025-03-15 10:30:00,123 - __main__ - INFO - Application started
2025-03-15 10:30:00,124 - __main__ - ERROR - Division by zero occurred
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
ZeroDivisionError: division by zero
💡Use logger.exception()
📊 Production Insight
In production, avoid using basicConfig multiple times; it's a no-op after the first call. Use a configuration file or dictConfig.
🎯 Key Takeaway
Always configure logging at the start of your application. Use basicConfig for simple setups, but move to dictConfig for production.

Structured Logging with JSON

Plain text logs are hard to parse. Structured logging outputs logs in JSON format, making it easy for log aggregation tools like ELK, Splunk, or Datadog to index and search. Each log entry becomes a JSON object with fields like timestamp, level, logger, message, and custom context. Python's logging module can be extended with a custom formatter that outputs JSON. Alternatively, you can use libraries like python-json-logger. In this section, we'll implement a JSON formatter from scratch and show how to add extra context (e.g., request ID, user ID) using the extra parameter or a custom filter.

json_logging.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import logging
import json

class JsonFormatter(logging.Formatter):
    def format(self, record):
        log_record = {
            'timestamp': self.formatTime(record, self.datefmt),
            'level': record.levelname,
            'logger': record.name,
            'message': record.getMessage(),
        }
        if hasattr(record, 'extra_fields'):
            log_record.update(record.extra_fields)
        return json.dumps(log_record)

# Setup
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger = logging.getLogger('myapp')
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Log with extra context
logger.info('User login', extra={'extra_fields': {'user_id': 123, 'ip': '192.168.1.1'}})
Output
{"timestamp": "2025-03-15 10:30:00,123", "level": "INFO", "logger": "myapp", "message": "User login", "user_id": 123, "ip": "192.168.1.1"}
🔥Structured logging is searchable
📊 Production Insight
Be careful not to log sensitive fields like passwords. Use a filter to redact them before formatting.
🎯 Key Takeaway
Use structured logging (JSON) for production systems. It makes log analysis and alerting much easier.

Log Rotation and Management

Log files grow indefinitely and can fill up your disk. Python's logging.handlers module provides RotatingFileHandler and TimedRotatingFileHandler to manage log files. RotatingFileHandler rotates logs when they reach a certain size, keeping a specified number of backup files. TimedRotatingFileHandler rotates at specific intervals (e.g., daily). In production, you should also consider log compression and cleanup. We'll demonstrate how to configure rotation and discuss best practices like using a separate partition for logs and setting appropriate retention policies.

log_rotation.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler('app.log', maxBytes=1024*1024, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))

logger = logging.getLogger('myapp')
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Simulate log entries
for i in range(10000):
    logger.info(f'Log entry {i}')
Output
Creates app.log, app.log.1, app.log.2, ... up to 5 backups, each max 1MB.
⚠ Avoid log file contention
📊 Production Insight
In containerized environments, logs should be written to stdout/stderr and collected by the container runtime (e.g., Docker logs). Avoid file handlers in containers.
🎯 Key Takeaway
Always implement log rotation to prevent disk full issues. Choose between size-based and time-based rotation based on your needs.

Logging in Multi-Threaded and Async Applications

In multi-threaded applications, logging from multiple threads can cause interleaved messages. Python's logging module is thread-safe, but you need to ensure that your handlers are also thread-safe. For async applications using asyncio, logging can be tricky because the default logging is blocking. You can use a queue-based handler to offload logging to a separate thread. We'll show how to use QueueHandler and QueueListener for non-blocking logging in async apps.

async_logging.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import logging
import asyncio
from logging.handlers import QueueHandler, QueueListener
import queue

# Create a queue and handler
log_queue = queue.Queue()
queue_handler = QueueHandler(log_queue)

# Create a listener that writes to a file
file_handler = logging.FileHandler('async_app.log')
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
listener = QueueListener(log_queue, file_handler)
listener.start()

# Configure root logger
root_logger = logging.getLogger()
root_logger.addHandler(queue_handler)
root_logger.setLevel(logging.INFO)

async def task(name):
    logging.info(f'Task {name} started')
    await asyncio.sleep(1)
    logging.info(f'Task {name} finished')

async def main():
    await asyncio.gather(task('A'), task('B'))

asyncio.run(main())
listener.stop()
Output
Logs are written asynchronously to async_app.log without blocking the event loop.
💡Use QueueHandler for async apps
📊 Production Insight
In high-throughput async apps, consider using a dedicated logging thread or process to handle log writes.
🎯 Key Takeaway
For async applications, use QueueHandler and QueueListener to avoid blocking the event loop.

Centralized Logging and Monitoring

In a distributed system, logs are scattered across multiple servers. Centralized logging aggregates logs into a single platform for search, alerting, and visualization. Common solutions include the ELK stack (Elasticsearch, Logstash, Kibana), Graylog, and cloud services like AWS CloudWatch or Datadog. Python applications can send logs directly to these systems using handlers like logstash.LogstashHandler or watchtower for CloudWatch. We'll show how to send structured logs to a remote syslog server and discuss best practices for log shipping.

centralized_logging.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
import logging
from logging.handlers import SysLogHandler

# Syslog handler (UDP to localhost)
handler = SysLogHandler(address=('localhost', 514), facility=SysLogHandler.LOG_USER)
handler.setFormatter(logging.Formatter('%(name)s: %(message)s'))

logger = logging.getLogger('myapp')
logger.addHandler(handler)
logger.setLevel(logging.INFO)

logger.info('Application started')
Output
Sends log to syslog server (e.g., rsyslog) which can forward to Elasticsearch.
🔥Use a log shipper
📊 Production Insight
Always include a unique identifier (e.g., request ID) in logs to correlate events across services.
🎯 Key Takeaway
Centralize logs for better visibility. Use a log shipper or direct handlers to send logs to a central platform.

Best Practices and Common Pitfalls

In this final section, we summarize best practices: use appropriate log levels, avoid logging sensitive data, log at the right granularity, and use lazy evaluation for expensive log messages. Common pitfalls include logging in tight loops, using print() instead of logging, and forgetting to configure logging in library code. We'll also discuss how to use logging.getLogger(__name__) to create hierarchical loggers and how to configure logging via a dictionary for complex applications.

best_practices.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import logging
import logging.config

# Dictionary configuration
LOGGING_CONFIG = {
    'version': 1,
    'formatters': {
        'default': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'default',
        },
    },
    'root': {
        'level': 'INFO',
        'handlers': ['console'],
    },
}

logging.config.dictConfig(LOGGING_CONFIG)

logger = logging.getLogger(__name__)

# Lazy evaluation: use %s formatting
logger.debug('User %s logged in', user_id)  # Good: only evaluated if DEBUG enabled
# Avoid: logger.debug(f'User {user_id} logged in')  # Always evaluated
Output
Logs to console with the specified format.
⚠ Don't log in tight loops
📊 Production Insight
In production, set root logger to WARNING or ERROR to reduce noise. Use DEBUG only for debugging sessions.
🎯 Key Takeaway
Use dictConfig for complex setups, lazy formatting for performance, and hierarchical loggers for modularity.
● Production incidentPOST-MORTEMseverity: high

The Silent Crash: When Logs Don't Log

Symptom
Users reported intermittent failures, but the application logs showed no errors.
Assumption
Developers assumed the logging was working correctly because they saw some log lines.
Root cause
The logging handler used a buffered mode that lost messages on crash. Also, exception handling was swallowing errors without logging.
Fix
Switched to synchronous logging with immediate flush, added exception logging in all catch blocks, and used a structured format.
Key lesson
  • Always flush logs immediately in production (use delay=False for file handlers).
  • Log exceptions with logger.exception() in except blocks.
  • Use structured logging to capture context (request ID, user ID).
  • Test your logging configuration under load.
  • Centralize logs for search and alerting.
Production debug guideSymptom to Action4 entries
Symptom · 01
No logs appear
Fix
Check log level, handler configuration, and file permissions.
Symptom · 02
Logs are truncated or missing
Fix
Check for log rotation misconfiguration or buffer overflow.
Symptom · 03
Too many logs (noise)
Fix
Adjust log levels; use different loggers for different components.
Symptom · 04
Sensitive data in logs
Fix
Implement a filter to redact passwords, tokens, etc.
★ Quick Debug Cheat SheetCommon logging problems and immediate fixes.
No logs to console
Immediate action
Add a StreamHandler
Commands
import logging; logging.basicConfig(level=logging.DEBUG)
logging.getLogger().addHandler(logging.StreamHandler())
Fix now
Add basicConfig at startup.
Logs not written to file+
Immediate action
Check file path and permissions
Commands
ls -la /var/log/myapp.log
touch /var/log/myapp.log && chmod 666 /var/log/myapp.log
Fix now
Ensure directory exists and is writable.
Logs contain sensitive data+
Immediate action
Add a filter
Commands
class SensitiveFilter(logging.Filter): ...
logger.addFilter(SensitiveFilter())
Fix now
Implement filter to redact patterns.
Featureprint()logging module
Configurable levelsNoYes
Output routingstdout onlyMultiple handlers (file, console, syslog, etc.)
FormattingManualFormatters with templates
Thread-safeNoYes
Production readyNoYes
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
basic_logging.pylogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(level...Why Logging Matters in Production
json_logging.pyclass JsonFormatter(logging.Formatter):Structured Logging with JSON
log_rotation.pyfrom logging.handlers import RotatingFileHandlerLog Rotation and Management
async_logging.pyfrom logging.handlers import QueueHandler, QueueListenerLogging in Multi-Threaded and Async Applications
centralized_logging.pyfrom logging.handlers import SysLogHandlerCentralized Logging and Monitoring
best_practices.pyLOGGING_CONFIG = {Best Practices and Common Pitfalls

Key takeaways

1
Use Python's built-in logging module with proper configuration for production systems.
2
Adopt structured logging (JSON) for easier analysis and integration with log management tools.
3
Implement log rotation to prevent disk space issues.
4
Use QueueHandler in async applications to avoid blocking the event loop.
5
Centralize logs for better observability across distributed systems.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Explain the logging levels in Python and when to use each.
Q02SENIOR
How would you implement structured logging in Python?
Q03SENIOR
Describe how to handle logging in a multi-threaded or async application.
Q01 of 03JUNIOR

Explain the logging levels in Python and when to use each.

ANSWER
DEBUG: detailed info for debugging; INFO: confirmation that things are working; WARNING: unexpected but not error; ERROR: serious problem; CRITICAL: program may abort. Use DEBUG in development, INFO in production for normal operations, WARNING for potential issues, ERROR for failures, CRITICAL for fatal errors.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
What is the difference between logging and print()?
02
How do I avoid logging sensitive data?
03
Should I use a logging library like loguru instead of the standard module?
04
How do I log from multiple modules?
N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Advanced Python. Mark it forged?

3 min read · try the examples if you haven't

Previous
Pre-commit Hooks for Python Projects
26 / 35 · Advanced Python
Next
Python Profiling: cProfile, py-spy, and Scalene