Flask: Logging Errors
Logging errors in Flask is critical for diagnosing issues, monitoring application health, and ensuring reliability in production environments. By capturing detailed error information, such as stack traces, request contexts, and timestamps, developers can troubleshoot effectively. Flask integrates seamlessly with Python’s logging
module and middleware for centralized error logging. This tutorial explores logging errors in Flask, covering setup, middleware for error capture, file-based logging, and best practices for robust error tracking.
01. Why Log Errors in Flask?
Errors, such as unhandled exceptions or HTTP errors (e.g., 500 Internal Server Error), can disrupt user experience and indicate underlying issues. Logging errors provides visibility into what went wrong, where, and why, enabling developers to debug efficiently, monitor application performance, and prevent future failures. Centralized error logging in Flask ensures consistency, scalability, and integration with monitoring tools.
Example: Basic Error Logging
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
@app.errorhandler(Exception)
def handle_error(error):
logger.error(f"Error occurred: {str(error)}", exc_info=True)
return "Internal Server Error", 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (Console):
* Running on http://127.0.0.1:5000
ERROR:__main__:Error occurred: Test error
Traceback (most recent call last):
...
ValueError: Test error
Explanation:
logging.basicConfig
- Configures logging withERROR
level.logger.error
- Logs the error with stack trace (exc_info=True
).@app.errorhandler(Exception)
- Catches all unhandled exceptions.
02. Key Techniques for Logging Errors
Flask supports multiple approaches to log errors, from simple console logging to advanced file-based logging and middleware for centralized error capture. These techniques enhance observability and debugging. The table below summarizes key techniques and their applications:
Technique | Description | Use Case |
---|---|---|
Console Logging | Log errors to console | Development, quick debugging |
File-Based Logging | Save logs to files with rotation | Production, persistent records |
Middleware Logging | Centralize error logging | Global error capture |
Request Context | Log request details with errors | Contextual debugging |
External Integration | Send logs to monitoring tools | Real-time monitoring |
2.1 Console-Based Error Logging
Example: Logging Errors to Console
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
@app.errorhandler(Exception)
def handle_error(error):
logger.error(f"Error: {str(error)}", exc_info=True)
return render_template('500.html'), 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Template (templates/500.html):
<!DOCTYPE html>
<html>
<head>
<title>500 - Server Error</title>
</head>
<body>
<h1>500 - Internal Server Error</h1>
<p>Something went wrong. Please try again later.</p>
<a href="/">Go Home</a>
</body>
</html>
Output (Console):
* Running on http://127.0.0.1:5000
2025-05-12 10:00:00,123 - ERROR - Error: Test error
Traceback (most recent call last):
...
ValueError: Test error
Explanation:
- Logs errors with timestamps and stack traces to the console.
- Suitable for development and quick debugging.
2.2 File-Based Error Logging
Example: Logging Errors to File with Rotation
from flask import Flask
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
@app.errorhandler(Exception)
def handle_error(error):
logger.error(f"Error: {str(error)}", exc_info=True)
return render_template('500.html'), 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (errors.log):
2025-05-12 10:00:00,123 - ERROR - Error: Test error
Traceback (most recent call last):
...
ValueError: Test error
Explanation:
RotatingFileHandler
- Logs errors to a file with size-based rotation (1MB, 5 backups).- Ensures persistent logs for production environments.
2.3 Middleware for Centralized Error Logging
Example: Error Logging Middleware
from flask import Flask, render_template
from werkzeug.wrappers import Request, Response
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
class ErrorLoggingMiddleware:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, environ, start_response):
request = Request(environ)
try:
return self.wsgi_app(environ, start_response)
except Exception as e:
logger.error(f"Error in {request.method} {request.path}: {str(e)}", exc_info=True)
res = Response(render_template('500.html'), status=500, mimetype='text/html')
return res(environ, start_response)
app.wsgi_app = ErrorLoggingMiddleware(app.wsgi_app)
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (errors.log):
2025-05-12 10:00:00,123 - ERROR - Error in GET /crash: Test error
Traceback (most recent call last):
...
ValueError: Test error
Output (Browser):
* Running on http://127.0.0.1:5000
(Accessing /crash: Renders 500.html)
Explanation:
- Middleware captures all unhandled exceptions, logs them with request context, and renders an error page.
- Centralizes error logging for consistency.
2.4 Logging with Request Context
Example: Logging Request Details
from flask import Flask, render_template
from werkzeug.wrappers import Request, Response
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
class RequestErrorLoggingMiddleware:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, environ, start_response):
request = Request(environ)
try:
return self.wsgi_app(environ, start_response)
except Exception as e:
logger.error(
f"Error in {request.method} {request.path}, "
f"IP: {request.remote_addr}, User-Agent: {request.user_agent}: {str(e)}",
exc_info=True
)
res = Response(render_template('500.html'), status=500, mimetype='text/html')
return res(environ, start_response)
app.wsgi_app = RequestErrorLoggingMiddleware(app.wsgi_app)
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (errors.log):
2025-05-12 10:00:00,123 - ERROR - Error in GET /crash, IP: 127.0.0.1, User-Agent: Mozilla/5.0 ...: Test error
Traceback (most recent call last):
...
ValueError: Test error
Explanation:
- Logs request details (method, path, IP, User-Agent) alongside errors.
- Provides rich context for debugging.
2.5 Integration with External Monitoring (Sentry)
Example: Logging Errors to Sentry
from flask import Flask, render_template
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
app = Flask(__name__)
# Initialize Sentry
sentry_sdk.init(
dsn="your-sentry-dsn", # Replace with your Sentry DSN
integrations=[FlaskIntegration()],
traces_sample_rate=1.0
)
@app.errorhandler(Exception)
def handle_error(error):
return render_template('500.html'), 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (Sentry Dashboard):
Error: ValueError: Test error
Path: /crash
Method: GET
IP: 127.0.0.1
User-Agent: Mozilla/5.0 ...
Stacktrace: ...
Explanation:
sentry_sdk
- Sends errors with request context to Sentry.- Enables real-time monitoring and alerting in production.
2.6 Incorrect Error Logging
Example: Incomplete Error Logging
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
@app.errorhandler(Exception)
def handle_error(error):
logger.error("An error occurred") # Missing details
return "Internal Server Error", 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (Console):
* Running on http://127.0.0.1:5000
ERROR:__main__:An error occurred
Explanation:
- Generic error message lacks context and stack trace.
- Solution: Include
str(error)
andexc_info=True
for detailed logging.
03. Effective Usage
3.1 Recommended Practices
- Combine middleware with file-based logging and external monitoring for comprehensive error tracking.
Example: Comprehensive Error Logging
from flask import Flask, render_template
from werkzeug.wrappers import Request, Response
import logging
from logging.handlers import RotatingFileHandler
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
app = Flask(__name__)
# Initialize Sentry
sentry_sdk.init(
dsn="your-sentry-dsn", # Replace with your Sentry DSN
integrations=[FlaskIntegration()],
traces_sample_rate=1.0
)
# Setup file logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
# Middleware for error logging
class ComprehensiveErrorLoggingMiddleware:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, environ, start_response):
request = Request(environ)
try:
return self.wsgi_app(environ, start_response)
except Exception as e:
error_message = (
f"Error in {request.method} {request.path}, "
f"IP: {request.remote_addr}, User-Agent: {request.user_agent}: {str(e)}"
)
logger.error(error_message, exc_info=True)
# Sentry automatically captures the error via integration
res = Response(render_template('500.html'), status=500, mimetype='text/html')
return res(environ, start_response)
app.wsgi_app = ComprehensiveErrorLoggingMiddleware(app.wsgi_app)
# Error handler for specific exceptions
@app.errorhandler(ValueError)
def handle_value_error(error):
logger.error(f"ValueError: {str(error)}", exc_info=True)
return render_template('400.html', message=str(error)), 400
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Template (templates/500.html):
<!DOCTYPE html>
<html>
<head>
<title>500 - Server Error</title>
</head>
<body>
<h1>500 - Internal Server Error</h1>
<p>Something went wrong. Please try again later.</p>
<a href="/">Go Home</a>
</body>
</html>
Template (templates/400.html):
<!DOCTYPE html>
<html>
<head>
<title>400 - Bad Request</title>
</head>
<body>
<h1>400 - Bad Request</h1>
<p>{{ message }}</p>
<a href="/">Go Home</a>
</body>
</html>
Output (errors.log):
2025-05-12 10:00:00,123 - ERROR - Error in GET /crash, IP: 127.0.0.1, User-Agent: Mozilla/5.0 ...: Test error
Traceback (most recent call last):
...
ValueError: Test error
Output (Sentry Dashboard):
Error: ValueError: Test error
Path: /crash
Method: GET
IP: 127.0.0.1
User-Agent: Mozilla/5.0 ...
Stacktrace: ...
- Logs errors to a rotating file with request context.
- Integrates with Sentry for real-time monitoring.
- Handles specific exceptions (e.g.,
ValueError
) with custom responses.
3.2 Practices to Avoid
- Avoid logging sensitive information (e.g., passwords, tokens).
Example: Logging Sensitive Data
from flask import Flask
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
@app.errorhandler(Exception)
def handle_error(error):
sensitive_data = "user_token=secret123"
logger.error(f"Error with {sensitive_data}: {str(error)}", exc_info=True)
return "Internal Server Error", 500
@app.route('/')
def index():
return "Home Page"
@app.route('/crash')
def crash():
raise ValueError("Test error")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (Console):
* Running on http://127.0.0.1:5000
ERROR:__main__:Error with user_token=secret123: Test error
- Logging sensitive data risks security breaches.
- Solution: Sanitize logs by excluding sensitive fields.
04. Common Use Cases
4.1 Logging Application Errors
Capture and log errors for debugging application logic.
Example: Logging Application Errors
from flask import Flask, render_template
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('app_errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
@app.errorhandler(ValueError)
def handle_value_error(error):
logger.error(f"Application error: {str(error)}", exc_info=True)
return render_template('400.html', message=str(error)), 400
@app.route('/')
def index():
return "Home Page"
@app.route('/process')
def process():
raise ValueError("Invalid data format")
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (app_errors.log):
2025-05-12 10:00:00,123 - ERROR - Application error: Invalid data format
Traceback (most recent call last):
...
ValueError: Invalid data format
Explanation:
- Logs specific application errors (
ValueError
) to a file. - Provides user-friendly error responses.
4.2 Monitoring API Errors
Log errors in API endpoints for monitoring and debugging.
Example: Logging API Errors
from flask import Flask, jsonify, render_template
from werkzeug.wrappers import Request, Response
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
logger = logging.getLogger(__name__)
logger.setLevel(logging.ERROR)
handler = RotatingFileHandler('api_errors.log', maxBytes=1000000, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
class APIErrorLoggingMiddleware:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, environ, start_response):
request = Request(environ)
if not request.path.startswith('/api/'):
return self.wsgi_app(environ, start_response)
try:
return self.wsgi_app(environ, start_response)
except Exception as e:
logger.error(
f"API Error in {request.method} {request.path}, "
f"IP: {request.remote_addr}: {str(e)}",
exc_info=True
)
res = Response(
jsonify({"error": "Internal Server Error"}),
status=500,
mimetype='application/json'
)
return res(environ, start_response)
app.wsgi_app = APIErrorLoggingMiddleware(app.wsgi_app)
@app.route('/api/data')
def data():
raise ValueError("Invalid API request")
@app.route('/')
def index():
return "Home Page"
if __name__ == '__main__':
app.run(debug=True, port=5000)
Output (api_errors.log):
2025-05-12 10:00:00,123 - ERROR - API Error in GET /api/data, IP: 127.0.0.1: Invalid API request
Traceback (most recent call last):
...
ValueError: Invalid API request
Output (Browser):
* Running on http://127.0.0.1:5000
(Accessing /api/data: Returns {"error": "Internal Server Error"})
Explanation:
- Logs errors for API routes with request context.
- Returns JSON error responses for API consistency.
Conclusion
Logging errors in Flask enhances debugging, monitoring, and reliability. Key takeaways:
- Use middleware for centralized error logging with request context.
- Implement file-based logging with rotation for persistent records.
- Integrate with tools like Sentry for real-time monitoring.
- Avoid logging sensitive data or incomplete error details.
With these practices, you can build Flask applications with robust error logging for effective troubleshooting and maintenance!
Comments
Post a Comment