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

Sponsored

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:

  1. Set FLASK_APP to your application entry point.
  2. Set FLASK_DEBUG=1 in your environment.
  3. Run flask run from 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:

  1. Import the logging module.
  2. Configure the root logger or the Flask app logger.
  3. Add handlers (console, file) and formatters.
  4. 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

  1. “Debugger PIN not found” or debugger not active - Ensure you set FLASK_DEBUG=1 before running flask run, not after. Check that your app doesn't override app.debug later.

  2. 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 to DEBUG. - If using logging.basicConfig, make sure you call it before any logging happens. If you import other modules that log, they may set up handlers first.

  3. 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 = False or configure only one logger.

  4. 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.

  5. 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.logger to 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

  1. Use environment variables for configuration instead of hardcoding debug flag, combined with python-dotenv.
  2. Implement structured logging with JSON formatter for easier parsing by log aggregators like ELK.
  3. 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.