Flask Debugging and Logging
Learn to set up Flask debugging and logging. This guided lesson covers the why, how, and practical exercises to add powerful diagnostics to your Flask apps.
Focus: set up flask debugging and logging
Ever pushed a Flask app to production, only to get a cryptic 500 error with no idea what happened? That's the pain of running blind. Without proper debugging and logging, you're left staring at an empty console, guessing which line of code exploded. In this lesson, you'll learn how to set up Flask debugging and logging so you can see exactly what your app is doing — from the first request to the final response — and fix issues in minutes, not hours.
The problem this lesson solves
Flask's default behavior in development shows you errors in the browser, but that's only half the story. In production, debugging mode is off by default, and errors are just HTTP status codes. You need a systematic way to:
- See stack traces when things go wrong
- Track user requests and their outcomes
- Monitor performance bottlenecks
- Keep a permanent record of what happened
Without logging, you're flying blind. A single misconfigured route or a subtle data type mismatch can take down your app, and you won't know until users complain. This lesson gives you the toolbox to move from “why is it broken?” to “here's exactly what happened and where.”
Core concept / mental model
Think of debugging as your active tool — the magnifying glass you hold up to a problem when it happens. Logging is your passive recorder — the security camera that documents everything, always. Together, they form the diagnostic backbone of any Flask application.
Here's a simple mental model:
- Debug mode is like a high-contrast mode for your app — it shows you line-level errors, full stack traces, and even allows an interactive debugger in the browser.
- Logging is like a flight recorder — it captures events (requests, errors, warnings, info) in a structured way that you can query later.
In Flask, you configure both through the app's configuration and Python's standard logging module. You can set different levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) to control how much noise you get.
How it works step by step
Enabling Flask debug mode
Flask uses an environment variable FLASK_DEBUG or a configuration flag DEBUG to toggle debug mode. When enabled, the development server automatically reloads on code changes and shows detailed error pages.
Steps to enable it:
- Set
FLASK_APPto your application entry point. - Set
FLASK_DEBUG=1in your environment. - Run
flask runfrom the terminal.
For programmatic control, you can set app.debug = True or app.config['DEBUG'] = True.
Configuring logging
Flask's built-in logger (app.logger) is a Python logging.Logger instance. By default, it outputs to the console at WARNING level. You can override this to suit your needs.
The key steps:
- Import the
loggingmodule. - Configure the root logger or the Flask app logger.
- Add handlers (console, file) and formatters.
- Set the log level per environment.
Using the app logger
Within your route handlers, you can call app.logger.debug(), .info(), .warning(), .error(), or .critical() to record events at different severity levels. This is the recommended way to log from Flask because it automatically includes request context.
Hands-on walkthrough
Let's build a small Flask app with debugging and logging enabled. We'll create a simple API that logs each request and handles errors gracefully.
First, create a file named app.py:
from flask import Flask, jsonify, request
import logging
import os
# Create Flask app
app = Flask(__name__)
# Set debug based on environment variable
debug_enabled = os.environ.get('FLASK_DEBUG', '0') == '1'
app.config['DEBUG'] = debug_enabled
if debug_enabled:
app.debug = True
# Configure logging
if not app.debug:
# In production, log to file
logging.basicConfig(filename='app.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
else:
# In development, log to console with more detail
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
@app.route('/')
def home():
app.logger.info('Home page accessed')
return jsonify({'message': 'Hello, World!'})
@app.route('/divide/<int:a>/<int:b>')
def divide(a, b):
try:
result = a / b
app.logger.debug(f'Dividing {a} by {b} -> {result}')
return jsonify({'result': result})
except ZeroDivisionError:
app.logger.error(f'Division by zero attempted with {a}')
return jsonify({'error': 'Cannot divide by zero'}), 400
if __name__ == '__main__':
app.run(debug=debug_enabled)
Run it with:
export FLASK_APP=app.py
export FLASK_DEBUG=1
flask run
Now visit http://127.0.0.1:5000/ and http://127.0.0.1:5000/divide/10/2. Check your console — you'll see debug logs. Try http://127.0.0.1:5000/divide/10/0 and watch the error log.
Expected output in console:
* Serving Flask app 'app.py'
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 123-456-789
INFO:root:Home page accessed
INFO:root:Divide 10 by 2 -> 5.0
ERROR:root:Division by zero attempted with 10
Notice how the debug mode provides the interactive debugger PIN — that's a powerful security feature for development.
Compare options / when to choose what
| Feature | Debug mode | Logging |
|---|---|---|
| Purpose | Find bugs during development | Record events for monitoring and post-mortem |
| Enabled by default | Yes in development, No in production | Yes (WARNING level) |
| Output | Interactive error pages in browser | Console, file, or remote services |
| Cost | High (extra overhead) | Low to moderate |
| Security | Must be disabled in production (exposes internals) | Safe to enable always |
| Example tools | Flask debugger, Werkzeug debugger | Python logging, Sentry, ELK stack |
When to choose what:
- Use debug mode during local development and when you're writing new features.
- Use logging in development and production — it's the only way to know what happened in production.
- For complex apps, consider structured logging (JSON format) and external aggregation services like Sentry.
Troubleshooting & edge cases
Common errors and fixes
-
“Debugger PIN not found” or debugger not active - Ensure you set
FLASK_DEBUG=1before runningflask run, not after. Check that your app doesn't overrideapp.debuglater. -
Logs not showing in console - Your log level might be too high. If you call
app.logger.debug()but the level is set to INFO, it won't appear. Set the level toDEBUG. - If usinglogging.basicConfig, make sure you call it before any logging happens. If you import other modules that log, they may set up handlers first. -
Duplicate log lines - This happens when you configure the root logger and the Flask logger separately, causing the same message to be handled twice. Use
app.logger.propagate = Falseor configure only one logger. -
Debug mode on in production - This is a security disaster. Use environment variables to disable it. Also, never run
app.run(debug=True)in a production WSGI server — use Gunicorn or uWSGI instead. -
File permission errors when writing logs - Ensure the log file path is writable by the user running the app. Use a dedicated log directory with proper permissions.
Edge case: logging in production
In production, you often want to log errors to a file or a service. Here's a quick example:
import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=3)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)
This rotates the log file to prevent it from growing indefinitely.
What you learned & what's next
You now know how to set up Flask debugging and logging. You can:
- Toggle debug mode for development with
FLASK_DEBUG - Configure logging levels, handlers, and formatters
- Use
app.loggerto log from your routes - Select the right diagnostic tool for your environment
- Avoid common pitfalls like duplicate logs and insecure debug mode
These are essential skills for any Flask developer. Next in the track, you'll likely cover error handling and custom error pages, or maybe testing your Flask app — both become much easier when you have solid logging in place.
Now go ahead and add logging to your existing Flask projects — you'll never want to debug without it again.
Practice recap
Add rotating file logging to the sample app, then create a route that deliberately raises an exception and log it with full traceback using app.logger.exception(). Observe the log output in both console and file. Try toggling FLASK_DEBUG and see how the behavior changes.
Common mistakes
- Leaving debug mode enabled in production — exposes secrets and allows remote code execution via the debugger PIN.
- Only using print() for debugging instead of the logging module — losing timestamps, levels, and filtering capabilities.
- Configuring logging during import time, causing duplicate handlers or missing logs after the app is created.
- Setting the log level too low (e.g., DEBUG in production) and flooding storage with trivial messages.
- Forgetting to handle exceptions in routes, so uncaught errors bypass your logging entirely and get swallowed by the WSGI server.
Variations
- Use environment variables for configuration instead of hardcoding debug flag, combined with python-dotenv.
- Implement structured logging with JSON formatter for easier parsing by log aggregators like ELK.
- Use Flask-DebugToolbar to add a performance panel and SQL query debugging to your development environment.
Real-world use cases
- A production API needs to capture 500 errors with stack traces to a central log service (e.g., Sentry) without exposing internals.
- A multi-user Flask app uses logging to audit sensitive endpoints (login, payment) at INFO level for compliance and security review.
- A data pipeline built on Flask logs each request duration and query count to a file, later analyzed to identify slow endpoints.
Key takeaways
- Debug mode is for development only — never enable it in production.
- Use the built-in app.logger with proper levels to record event severity.
- Configure logging via logging.basicConfig or custom handlers based on your environment.
- Logging gives you a permanent record; debugging gives you real-time insight.
- Avoid duplicate log lines by managing handler propagation carefully.
- Structured logging and aggregation tools are the next level for large apps.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.