Skip to content

Debugging Guide

Tools and techniques for debugging the Odoo 15 system.

Overview

Effective debugging saves time and reduces frustration. This guide covers tools, techniques, and common debugging scenarios.


Quick Debugging Commands

# View Odoo logs in real-time
docker compose logs -f odoo

# View last 100 lines with errors
docker compose logs --tail=100 odoo | grep -i error

# Access Odoo shell
docker compose exec odoo odoo shell -d odoo_test

# Access database directly
docker compose exec db psql -U odoo -d odoo_test

# Check container status
docker compose ps

Logging for Debugging

Add Debug Logging

import logging
_logger = logging.getLogger(__name__)

def my_method(self):
    _logger.debug(f"Entering my_method with self={self}")
    _logger.debug(f"Context: {self.env.context}")

    for record in self:
        _logger.debug(f"Processing record {record.id}: {record.name}")

    _logger.debug("Exiting my_method")

Enable Debug Logging

# odoo.conf
log_level = debug
log_handler = :DEBUG,odoo.addons.my_module:DEBUG

Or via command line:

docker compose exec odoo odoo --log-level=debug -u my_module --stop-after-init -d odoo_test


Odoo Shell

Starting the Shell

# Interactive Python shell with Odoo environment
docker compose exec odoo odoo shell -d odoo_test

Common Shell Commands

# Get current user
>>> self.env.user
res.users(1,)

# Search records
>>> orders = self.env['sale.order'].search([('state', '=', 'draft')])
>>> len(orders)
5

# Read record data
>>> order = self.env['sale.order'].browse(1)
>>> order.name
'SO001'
>>> order.partner_id.name
'Customer Name'

# Check field values
>>> order.read(['name', 'state', 'amount_total'])
[{'id': 1, 'name': 'SO001', 'state': 'draft', 'amount_total': 100.0}]

# Call methods
>>> order.action_confirm()

# Execute SQL
>>> self.env.cr.execute("SELECT id, name FROM sale_order LIMIT 5")
>>> self.env.cr.fetchall()
[(1, 'SO001'), (2, 'SO002'), ...]

# Check module
>>> self.env['ir.module.module'].search([('name', '=', 'my_module')]).state
'installed'

# Exit shell
>>> exit()

Debugging in Shell

# Import pdb for debugging
>>> import pdb

# Check object attributes
>>> dir(order)
['__class__', 'action_confirm', 'amount_total', ...]

# Check field definition
>>> order._fields['state']
Selection(...)

# Check model info
>>> order._name
'sale.order'
>>> order._description
'Sales Order'

# Inspect method
>>> import inspect
>>> print(inspect.getsource(order.action_confirm))

Database Debugging

Useful SQL Queries

-- Connect to database
docker compose exec db psql -U odoo -d odoo_test

-- List tables
\dt

-- Describe table
\d+ sale_order

-- Find records
SELECT id, name, state, amount_total
FROM sale_order
WHERE state = 'draft'
LIMIT 10;

-- Check for NULLs
SELECT COUNT(*) FROM sale_order WHERE partner_id IS NULL;

-- Find duplicates
SELECT email, COUNT(*)
FROM res_partner
GROUP BY email
HAVING COUNT(*) > 1;

-- Check module state
SELECT name, state, latest_version
FROM ir_module_module
WHERE name LIKE 'jdx_%';

-- Check recent changes
SELECT id, name, write_date
FROM sale_order
ORDER BY write_date DESC
LIMIT 10;

-- Exit
\q

Analyze Query Performance

-- Enable timing
\timing on

-- Explain query
EXPLAIN ANALYZE
SELECT * FROM sale_order
WHERE partner_id = 1;

Basic Print Debugging

def my_method(self):
    print("=" * 50)
    print(f"DEBUG: my_method called")
    print(f"DEBUG: self = {self}")
    print(f"DEBUG: self.ids = {self.ids}")
    print("=" * 50)

    for record in self:
        print(f"DEBUG: Processing {record.id}")
        print(f"DEBUG: record.name = {record.name}")
        print(f"DEBUG: record.state = {record.state}")

Formatted Debug Output

def debug_print(label, obj):
    print(f"\n{'='*60}")
    print(f"DEBUG: {label}")
    print(f"{'='*60}")
    if hasattr(obj, 'read'):
        import pprint
        pprint.pprint(obj.read())
    else:
        print(obj)
    print(f"{'='*60}\n")

# Usage
debug_print("Order Data", order)
debug_print("Context", self.env.context)

Breakpoint Debugging

Using pdb

def my_method(self):
    import pdb; pdb.set_trace()  # Breakpoint

    for record in self:
        # Code will stop here
        result = self._process(record)

pdb Commands

Command Description
n Next line
s Step into function
c Continue execution
p var Print variable
pp var Pretty print variable
l List source code
w Show stack trace
q Quit debugger
h Help

Using breakpoint() (Python 3.7+)

def my_method(self):
    breakpoint()  # Modern way
    # ...

Remote Debugging (VS Code)

Setup

  1. Install debugpy in container:

    # Dockerfile
    RUN pip install debugpy
    

  2. Add debug entrypoint:

    # debug_entrypoint.py
    import debugpy
    debugpy.listen(("0.0.0.0", 5678))
    print("Waiting for debugger...")
    debugpy.wait_for_client()
    

  3. VS Code launch.json:

    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Attach to Odoo",
                "type": "python",
                "request": "attach",
                "connect": {
                    "host": "localhost",
                    "port": 5678
                },
                "pathMappings": [
                    {
                        "localRoot": "${workspaceFolder}/extra-addons",
                        "remoteRoot": "/mnt/extra-addons"
                    }
                ]
            }
        ]
    }
    


Common Debugging Scenarios

Module Won't Load

# Check for syntax errors
docker compose exec odoo python -c "import sys; sys.path.insert(0, '/mnt/extra-addons/odoo'); import my_module"

# Check manifest
docker compose exec odoo python -c "
import ast
with open('/mnt/extra-addons/odoo/my_module/__manifest__.py') as f:
    print(ast.literal_eval(f.read()))
"

# Check Odoo logs
docker compose logs odoo | grep -i "my_module"

Record Not Found

# In Odoo shell
>>> record = self.env['my.model'].browse(123)
>>> record.exists()
my.model()  # Empty = doesn't exist

# Check in database
>>> self.env.cr.execute("SELECT * FROM my_model WHERE id = 123")
>>> self.env.cr.fetchone()
None  # Doesn't exist

Field Value Not Updating

# Check if field is computed
>>> field = self.env['my.model']._fields['my_field']
>>> field.compute
'_compute_my_field'  # It's computed

# Force recompute
>>> records = self.env['my.model'].search([])
>>> records._compute_my_field()

# Check if stored
>>> field.store
True

Method Not Being Called

# Add logging to verify
def my_method(self):
    _logger.info(f"my_method called with {self.ids}")
    import traceback
    _logger.info(f"Call stack:\n{traceback.format_stack()}")

Access Rights Issues

# In shell, check as different user
>>> user = self.env['res.users'].browse(2)
>>> self.env = self.env(user=user)
>>> self.env['my.model'].search([])  # Will fail if no access

# Check access rights
>>> self.env['ir.model.access'].search([
...     ('model_id.model', '=', 'my.model'),
...     ('group_id', 'in', user.groups_id.ids)
... ])

Performance Debugging

Profile Code

import cProfile
import pstats

def my_method(self):
    profiler = cProfile.Profile()
    profiler.enable()

    # Code to profile
    result = self._do_heavy_work()

    profiler.disable()
    stats = pstats.Stats(profiler)
    stats.sort_stats('cumulative')
    stats.print_stats(20)  # Top 20

    return result

Log SQL Queries

# Enable SQL logging
import logging
logging.getLogger('odoo.sql_db').setLevel(logging.DEBUG)

# Or in odoo.conf
log_handler = odoo.sql_db:DEBUG

Count Queries

def my_method(self):
    # Count queries before
    query_count_before = self.env.cr.sql_log_count

    # Do work
    self._do_work()

    # Count queries after
    query_count = self.env.cr.sql_log_count - query_count_before
    _logger.info(f"Method executed {query_count} queries")

Network Debugging

Test API Endpoints

# Test from inside container
docker compose exec odoo curl http://localhost:8069/web/health

# Test from host
curl http://localhost:8069/web/health
curl http://localhost:5000/health

# Test with authentication
curl -X POST http://localhost:8069/jsonrpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"call","params":{},"id":1}'

Check Container Networking

# Check network
docker network inspect odoo15-jdx-production_default

# Test connectivity between containers
docker compose exec pwa ping odoo
docker compose exec nginx ping odoo

Debugging Checklist

Before Debugging

  • Can reproduce the issue?
  • Checked the logs?
  • Recent code changes?
  • Works in other environment?

During Debugging

  • Add logging at key points
  • Check input values
  • Verify database state
  • Test in isolation

After Fixing

  • Remove debug code
  • Add test to prevent regression
  • Document the fix
  • Update error handling if needed

Quick Reference

Log Commands

docker compose logs odoo              # All logs
docker compose logs -f odoo           # Follow
docker compose logs --tail=100 odoo   # Last 100

Shell Access

docker compose exec odoo odoo shell -d DATABASE
docker compose exec db psql -U odoo -d DATABASE

Debug Print

print(f"DEBUG: {variable}")
_logger.debug(f"DEBUG: {variable}")
import pdb; pdb.set_trace()

Check Record

record.read()           # All fields
record.exists()         # Check exists
record._fields          # Field definitions
record._name            # Model name