Flask: Setting Up Logging
Logging is essential for monitoring, debugging, and maintaining Flask applications, providing insights into application behavior, errors, and performance. Flask integrates with Python’s built-in logging
module, allowing developers to capture and analyze events effectively. This tutorial explores setting up logging in Flask, covering configuration, log levels, handlers, and best practices for robust application monitoring.
01. Why Use Logging in Flask?
Logging helps track application events, diagnose issues, and audit user actions, making it critical for development and production environments. Flask’s minimalistic design relies on Python’s logging
module, which supports customizable log levels, output destinations, and formats. Proper logging ensures proactive issue resolution, security monitoring, and compliance with operational requirements.
Example: Basic Flask Logging
from flask import Flask
import logging
app = Flask(__name__)
# Configure basic logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.route('/')
def index():
logger.info('Homepage accessed')
return "Welcome to Flask!"
if __name__ == '__main__':
app.run(debug=True)
Output:
* Running on http://127.0.0.1:5000
INFO:__main__:Homepage accessed
Explanation:
logging.basicConfig
- Sets up logging with a default console handler.logger.info
- Logs an informational message when the homepage is accessed.
02. Key Logging Techniques
Flask logging can be tailored to capture specific events, route logs to various outputs, and integrate with external systems. These techniques ensure comprehensive monitoring and scalability. The table below summarizes key techniques and their applications:
Technique | Description | Use Case |
---|---|---|
Log Levels | Use DEBUG, INFO, WARNING, ERROR, CRITICAL | Control log verbosity |
File Handler | Save logs to files | Persistent error tracking |
Rotating File Handler | Manage log file size | Prevent disk overuse |
Custom Formatter | Customize log message format | Include timestamps, request data |
External Integration | Send logs to external systems | Centralized monitoring (e.g., Sentry) |
2.1 Using Log Levels
Example: Different Log Levels
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
@app.route('/test')
def test():
logger.debug('Debugging route access')
logger.info('Processing request')
logger.warning('Potential issue detected')
logger.error('Error occurred')
logger.critical('Critical failure')
return "Log levels tested"
if __name__ == '__main__':
app.run(debug=True)
Output:
* Running on http://127.0.0.1:5000
DEBUG:__main__:Debugging route access
INFO:__main__:Processing request
WARNING:__main__:Potential issue detected
ERROR:__main__:Error occurred
CRITICAL:__main__:Critical failure
Explanation:
level=logging.DEBUG
- Captures all log levels (DEBUG and above).- Different levels help categorize messages by severity.
2.2 Logging to a File
Example: File Handler Setup
from flask import Flask
import logging
app = Flask(__name__)
# Configure file logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)
@app.route('/')
def index():
logger.info('Homepage accessed')
return "Logged to file"
if __name__ == '__main__':
app.run(debug=True)
Output (app.log):
INFO:__main__:Homepage accessed
Explanation:
FileHandler
- Writes logs toapp.log
.- Persistent storage for later analysis.
2.3 Rotating File Handler for Log Management
Example: Rotating Logs
from flask import Flask
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
# Configure rotating file logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
rotating_handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=5)
rotating_handler.setLevel(logging.INFO)
logger.addHandler(rotating_handler)
@app.route('/')
def index():
logger.info('Homepage accessed')
return "Logged with rotation"
if __name__ == '__main__':
app.run(debug=True)
Output (app.log, app.log.1, etc.):
INFO:__main__:Homepage accessed
Explanation:
RotatingFileHandler
- Rotates logs whenmaxBytes
(10KB) is reached, keeping up to 5 backups.- Prevents log files from consuming excessive disk space.
2.4 Custom Log Formatter
Example: Custom Log Format
from flask import Flask, request
import logging
app = Flask(__name__)
# Configure logging with custom format
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s')
file_handler = logging.FileHandler('custom.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
@app.route('/')
def index():
logger.info('Homepage accessed', extra={'ip': request.remote_addr})
return "Custom log format"
if __name__ == '__main__':
app.run(debug=True)
Output (custom.log):
2025-05-12 10:00:00,123 - INFO - Homepage accessed - IP: 127.0.0.1
Explanation:
Formatter
- Customizes log output with timestamp, level, message, and client IP.extra
- Passes additional data likerequest.remote_addr
.
2.5 External Logging Integration (Sentry)
Example: Logging to Sentry
from flask import Flask
import logging
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.logging import LoggingIntegration
app = Flask(__name__)
# Configure Sentry
sentry_sdk.init(
dsn='your-sentry-dsn',
integrations=[FlaskIntegration(), LoggingIntegration(level=logging.INFO)]
)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
@app.route('/')
def index():
logger.info('Homepage accessed')
try:
1 / 0 # Simulate error
except ZeroDivisionError:
logger.error('Division by zero', exc_info=True)
return "Logged to Sentry"
if __name__ == '__main__':
app.run(debug=True)
Output (Sentry Dashboard):
Event: Homepage accessed (INFO)
Event: Division by zero (ERROR with stack trace)
Explanation:
sentry_sdk
- Sends logs and errors to Sentry for centralized monitoring.exc_info=True
- Includes stack traces for exceptions.
2.6 Incorrect Logging Setup
Example: Misconfigured Logging
from flask import Flask
import logging
app = Flask(__name__)
# Incorrect: No handler configured
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
@app.route('/')
def index():
logger.info('This will not be logged')
return "No logging output"
if __name__ == '__main__':
app.run(debug=True)
Output:
* Running on http://127.0.0.1:5000
(No log output due to missing handler)
Explanation:
- Logger requires a handler (e.g.,
FileHandler
,StreamHandler
) to output logs. - Solution: Add a handler like
logging.StreamHandler()
orlogging.basicConfig()
.
03. Effective Usage
3.1 Recommended Practices
- Use rotating file handlers and custom formatters for production.
Example: Comprehensive Logging Setup
from flask import Flask, request
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
# Configure logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Console handler for development
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# Rotating file handler for production
file_handler = RotatingFileHandler('app.log', maxBytes=1000000, backupCount=10)
file_handler.setLevel(logging.INFO)
# Custom formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s - URL: %(url)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
@app.route('/')
def index():
logger.info('Homepage accessed', extra={'ip': request.remote_addr, 'url': request.url})
return "Comprehensive logging"
@app.route('/error')
def error():
try:
1 / 0
except ZeroDivisionError:
logger.error('Division by zero error', extra={'ip': request.remote_addr, 'url': request.url}, exc_info=True)
return "Error logged", 500
if __name__ == '__main__':
app.run(debug=True)
Output (Console and app.log):
2025-05-12 10:00:00,123 - INFO - Homepage accessed - IP: 127.0.тик0.1 - URL: http://127.0.0.1:5000/
2025-05-12 10:00:01,456 - ERROR - Division by zero error - IP: 127.0.0.1 - URL: http://127.0.0.1:5000/error
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
- Combines console and rotating file handlers with custom formatting.
- Logs request metadata (IP, URL) and exceptions with stack traces.
- Scalable for production with log rotation.
3.2 Practices to Avoid
- Avoid excessive DEBUG logging in production to prevent performance issues.
Example: Excessive Debug Logging
from flask import Flask
import logging
app = Flask(__name__)
# Incorrect: DEBUG level in production
logging.basicConfig(level=logging.DEBUG, filename='app.log')
logger = logging.getLogger(__name__)
@app.route('/')
def index():
logger.debug('Every request logged with excessive detail')
return "Overloaded log file"
if __name__ == '__main__':
app.run(debug=True)
Output (app.log):
DEBUG:__main__:Every request logged with excessive detail
(Fills log file rapidly with verbose output)
- DEBUG logs generate excessive data, slowing performance and filling storage.
- Solution: Use
INFO
or higher in production, reserveDEBUG
for development.
04. Common Use Cases
4.1 Debugging Application Errors
Log errors with stack traces to diagnose issues.
Example: Error Logging
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR, filename='errors.log')
logger = logging.getLogger(__name__)
@app.route('/divide/<int:num>')
def divide(num):
try:
result = 100 / num
return f"Result: {result}"
except ZeroDivisionError:
logger.error('Division by zero', exc_info=True)
return "Error: Division by zero", 500
if __name__ == '__main__':
app.run(debug=True)
Output (errors.log):
ERROR:__main__:Division by zero
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Explanation:
- Logs exceptions with stack traces for debugging.
- Isolates error logs to a dedicated file.
4.2 Monitoring User Activity
Log user actions for auditing and analytics.
Example: User Activity Logging
from flask import Flask, request
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = RotatingFileHandler('activity.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s - IP: %(ip)s'))
logger.addHandler(handler)
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username', 'anonymous')
logger.info(f'User {username} logged in', extra={'ip': request.remote_addr})
return "Login logged"
if __name__ == '__main__':
app.run(debug=True)
Output (activity.log):
2025-05-12 10:00:00,123 - INFO - User alice logged in - IP: 127.0.0.1
Explanation:
- Logs user login events with IP addresses for auditing.
- Rotating handler manages log file size.
Conclusion
Setting up logging in Flask enhances application monitoring, debugging, and security. Key takeaways:
- Use appropriate log levels (
DEBUG
for development,INFO
/ERROR
for production). - Implement file and rotating handlers for persistent, manageable logs.
- Customize formats to include context like IP or request URLs.
- Integrate with external tools like Sentry for centralized monitoring.
With these practices, you can build Flask applications with effective logging for reliable operation and troubleshooting!
Comments
Post a Comment