Skip to content
C

Debugging & Logging

Reading tracebacks, print()-debugging, Python's built-in pdb debugger and breakpoint(), the logging module, log levels, and logging to files.


Bugs are inevitable — even experienced developers write code that doesn't work the first time. What separates a confident developer from a frustrated one is having good tools and habits for finding out why something went wrong. This file covers exactly that: reading error messages, using debuggers, and logging what your program does.


1. What is Debugging?

What is it?

Debugging is the process of finding and fixing errors (bugs) in your code — figuring out why the program isn't behaving the way you expect.

Definition: Debugging is the systematic process of identifying, analyzing, and fixing errors in a program.

Why do we use structured debugging techniques?

Randomly changing code and re-running it hoping something works is slow and frustrating. Structured debugging — reading error messages carefully, checking variable values step by step — finds the actual root cause far faster.


2. Reading Stack Traces (Tracebacks)

What is it?

When Python hits an unhandled error, it prints a traceback — a report showing exactly where the error occurred and the chain of function calls that led there.

Simple Example

python
def divide(a, b): return a / b def calculate(): return divide(10, 0) calculate()

Output:

Traceback (most recent call last):
  File "app.py", line 6, in <module>
    calculate()
  File "app.py", line 4, in calculate
    return divide(10, 0)
  File "app.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero

Explanation of the Code

  • Read a traceback from the bottom up: the last line (ZeroDivisionError: division by zero) tells you what went wrong.
  • The lines above it show the call chaincalculate() called divide(), and it was inside divide() (line 2) where the actual error occurred.
  • This lets you trace exactly which function, and which line, caused the problem — even in a large program with many function calls.

Common Mistakes

  • Panicking at a long traceback instead of reading the last line first (the actual error type and message) and then tracing upward.
  • Ignoring the file names and line numbers, which point you directly to the problem location.

Important Points

  • Always read the last line of a traceback first — it tells you the exception type and message.
  • The call chain above it shows exactly how the program reached that point.

3. print() Debugging

What is it?

The simplest debugging technique: temporarily adding print() statements to see what values variables actually hold at different points in your code.

Simple Example

python
def calculate_discount(price, discount_percent): print(f"DEBUG: price={price}, discount_percent={discount_percent}") discount = price * (discount_percent / 100) print(f"DEBUG: discount={discount}") final_price = price - discount return final_price print(calculate_discount(1000, 10))

Explanation

  • Adding print() statements at key points reveals exactly what values a function is working with, helping you spot exactly where a calculation goes wrong.

Common Mistakes

  • Leaving debug print() statements in production code — always remove or replace them with proper logging (see below) once done debugging.
  • Overusing print() debugging for complex bugs, where a real debugger (below) would be far more efficient.

Important Points

  • print() debugging is quick and simple for small issues, but doesn't scale well for complex bugs or larger programs.

4. Using pdb — Python's Built-in Debugger

What is it?

pdb is Python's built-in interactive debugger — instead of guessing with print() statements, it lets you pause your program at a specific line and inspect everything happening at that exact moment.

Setting a Breakpoint

python
import pdb def calculate_discount(price, discount_percent): discount = price * (discount_percent / 100) pdb.set_trace() # execution pauses here final_price = price - discount return final_price print(calculate_discount(1000, 10))

When execution reaches pdb.set_trace(), it drops into an interactive prompt:

(Pdb) 

Useful pdb Commands

CommandPurpose
n (next)Run the next line
s (step)Step into a function call
c (continue)Continue running until the next breakpoint
p variable_namePrint a variable's current value
l (list)Show the surrounding code
q (quit)Exit the debugger

Modern Alternative — breakpoint()

python
def calculate_discount(price, discount_percent): discount = price * (discount_percent / 100) breakpoint() # same effect as pdb.set_trace(), built in since Python 3.7 final_price = price - discount return final_price

Real-World Example

Instead of adding ten different print() statements trying to find where a value became incorrect, a single breakpoint() lets you pause exactly there and inspect every variable interactively, in real time.

Common Mistakes

  • Forgetting to remove breakpoint()/pdb.set_trace() calls before deploying code — this would pause your program unexpectedly in production.

Important Points

  • breakpoint() (Python 3.7+) is the modern, preferred way to trigger pdb — no import needed.
  • Debuggers let you pause and inspect a running program, rather than guessing from static code alone.

Using Breakpoints in VS Code

Instead of typing breakpoint() in code, VS Code lets you click in the margin next to a line number to set a visual breakpoint, then run your file using the debugger (the "Run and Debug" panel) — pausing at that exact line with a full inspection panel for variables, without touching your actual code.


5. The logging Module

What is it?

While print() is fine for quick debugging, real applications use the logging module to record what's happening — with more control over severity levels, output destinations (console, file), and formatting, without cluttering actual program output.

Simple Example

python
import logging logging.basicConfig(level=logging.DEBUG) logging.debug("This is a debug message") logging.info("Application started") logging.warning("This is a warning") logging.error("Something went wrong") logging.critical("Critical failure!")

Output:

DEBUG:root:This is a debug message
INFO:root:Application started
WARNING:root:This is a warning
ERROR:root:Something went wrong
CRITICAL:root:Critical failure!

Explanation of the Code

  • logging.basicConfig(level=logging.DEBUG) sets the minimum severity level that will actually be shown — here, everything (DEBUG and above) is shown.
  • Each logging.xxx() call records a message at a specific severity level.

6. Log Levels

What is it?

Log levels indicate the severity or importance of a logged message, from least to most severe.

Log Level Hierarchy

LevelNumeric ValueWhen to Use
DEBUG10Detailed diagnostic info, useful only during development
INFO20General confirmation that things are working as expected
WARNING30Something unexpected happened, but the program still works
ERROR40A serious problem — some functionality failed
CRITICAL50A very serious error — the program itself may be unable to continue

Simple Example

python
import logging logging.basicConfig(level=logging.WARNING) logging.debug("This won't show") # below the WARNING threshold logging.info("This won't show") # below the WARNING threshold logging.warning("This WILL show") logging.error("This WILL show")

Explanation: Setting level=logging.WARNING means only WARNING and above are actually shown — DEBUG and INFO messages are silently filtered out. This lets you control verbosity: use DEBUG level during development, and something like WARNING or ERROR in production, to avoid drowning in noise.

Common Mistakes

  • Using print() everywhere instead of proper log levels, making it impossible to filter out noisy detail in production.
  • Logging sensitive information (passwords, API keys) — never log secrets, even at DEBUG level.

Important Points

  • Log levels let you control exactly how much detail is recorded, without deleting the actual logging code.
  • Higher severity levels always include being shown when a lower threshold is set (e.g., setting level=INFO also shows WARNING, ERROR, and CRITICAL).

7. Logging to a File

What is it?

Instead of (or in addition to) printing to the console, logs can be written to a file — essential for tracking what happened in a program long after it finished running, especially for servers running continuously.

Simple Example

python
import logging logging.basicConfig( filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logging.info("Application started") logging.warning("Low disk space") logging.error("Failed to connect to database")

File: `app.log`

2026-01-15 10:30:00,123 - INFO - Application started
2026-01-15 10:30:05,456 - WARNING - Low disk space
2026-01-15 10:30:10,789 - ERROR - Failed to connect to database

Explanation of the Code

  • filename="app.log" redirects all log output to a file instead of the console.
  • The format string customizes exactly what each log line includes — here, a timestamp (%(asctime)s), the severity level (%(levelname)s), and the actual message.

Real-World Example

Web servers and long-running applications rely heavily on log files to diagnose issues that happened hours or days earlier — since you can't attach a live debugger to a server that already crashed and restarted.

Common Mistakes

  • Never rotating or limiting log file size, causing log files to grow indefinitely over time (real production systems use log rotation to manage this).
  • Logging way too much at INFO or DEBUG level in production, making log files huge and hard to search through.

Important Points

  • Logging to files is essential for diagnosing issues in applications that run continuously, especially on servers.
  • The format string lets you customize exactly what information appears in each log entry.

8. Error Tracking (Brief Overview)

What is it?

In professional production systems, dedicated error-tracking tools (like Sentry or Rollbar) automatically collect, group, and alert developers about exceptions happening in a live application — going beyond simple log files.

Why This Matters

A log file sitting on a server nobody's watching isn't very useful. Error-tracking tools automatically notify the team (e.g., via email or Slack) the moment something breaks in production, along with the full traceback and context — dramatically speeding up how quickly real problems get noticed and fixed.

Important Points

  • This is a conceptual introduction — actual setup of tools like Sentry is beyond this course's scope, but knowing they exist (and why) is valuable, especially for placement interviews discussing real-world software practices.

Common Beginner Mistakes — Summary for This Section

  • Panicking at a traceback instead of reading the last line first.
  • Leaving debug print() statements or breakpoint() calls in code meant for production.
  • Using print() everywhere instead of the logging module in real applications.
  • Logging sensitive data like passwords or API keys.
  • Letting log files grow unbounded without any rotation strategy.

Cheat Sheet — Debugging & Logging

python
# Reading tracebacks: read the LAST line first, then trace the call chain upward # pdb / breakpoint breakpoint() # pauses execution here # pdb commands: n (next), s (step), c (continue), p var (print), q (quit) # logging import logging logging.basicConfig(level=logging.INFO, filename="app.log", format="%(asctime)s - %(levelname)s - %(message)s") logging.debug("...") logging.info("...") logging.warning("...") logging.error("...") logging.critical("...")

Interview Questions

Q1. How should you read a Python traceback? Answer: Start from the last line, which shows the exception type and message. Then trace upward through the call chain to see exactly which function calls led to where the error actually occurred.

Q2. What is the difference between `print()` debugging and using `pdb`? Answer: print() debugging requires manually adding statements and re-running the program each time. pdb (or breakpoint()) pauses the program at a specific point, letting you interactively inspect any variable's current value without modifying the code repeatedly.

Q3. What are the five standard logging levels, in order of severity? Answer: DEBUG, INFO, WARNING, ERROR, CRITICAL (from least to most severe).

Q4. Why is the `logging` module preferred over `print()` in real applications? Answer: It provides severity levels (allowing filtering), can write to files, supports custom formatting (like timestamps), and can be configured differently for development versus production without changing the actual logging calls in the code.

Q5. Why should you never log sensitive information like passwords? Answer: Log files can be read by anyone with access to the server or log storage, so logging secrets creates a serious security risk.


Practice Questions

Beginner

  1. Write a small program that raises a ZeroDivisionError, run it, and identify the exact line from the traceback.
  2. Add print() statements to debug a function that isn't calculating a total correctly.
  3. Use breakpoint() inside a function and step through it using pdb commands (n, p, c).
  4. Configure basic logging with level=logging.INFO and log three different messages.
  5. Log a warning message and an error message, and observe which ones appear when the level is set to WARNING.

Intermediate

  1. Set up logging to write to a file called debug.log, including timestamps in the format.
  2. Write a function with a deliberate bug, then use pdb to step through it and identify the problem.
  3. Add appropriate logging (INFO for normal operations, ERROR for failures) to the Expense Tracker mini project from the Functions file.
  4. Write a program that logs DEBUG messages during development but only logs WARNING and above once you change the configured level.
  5. Explain, using a short paragraph as a comment, the difference between a stack trace and a log file.

Challenge

  1. Add comprehensive logging (with appropriate levels) to the Student Management System mini project from the Database Programming file, logging every add/update/delete operation.
  2. Deliberately introduce three different bugs into a working program (an off-by-one error, a type mismatch, and a logic error), then use tracebacks and pdb to find and fix each one.
  3. Research (and briefly explain in comments) how log rotation works and why it's necessary for long-running production applications.

Mock Test

  • Debugging & Logging - Quick Test

    10 questions covering tracebacks, pdb/breakpoint(), the logging module, log levels, and logging to files.

    10 questions · 10 min · Easy
    Start Mock Test

Coding Problems