Skip to content

Odoo Logging Configuration and Debugging Techniques

DeployMonkey Team · March 23, 2026 11 min read

Odoo's Logging System

Odoo uses Python's standard logging module with custom configuration. Understanding how to configure and use logging is essential for debugging during development and diagnosing issues in production.

Basic Logging in Modules

Every Odoo module should use a module-level logger:

import logging

_logger = logging.getLogger(__name__)

class MyModel(models.Model):
    _name = 'my.model'

    def process_records(self):
        _logger.info('Processing %d records', len(self))
        for record in self:
            try:
                record._do_work()
                _logger.debug('Processed record %s', record.name)
            except Exception as e:
                _logger.error(
                    'Failed to process record %s: %s',
                    record.name, e, exc_info=True)

The convention is to name the logger _logger (with underscore prefix) and initialize it with __name__, which resolves to the module path (e.g., odoo.addons.my_module.models.my_model).

Log Levels

Python logging levels from least to most severe:

LevelWhen to UseExample
DEBUGDetailed diagnostic infoVariable values, loop iterations
INFONormal operationsRecord processed, cron started
WARNINGUnexpected but handledDeprecated feature used, fallback applied
ERROROperation failedAPI call failed, invalid data
CRITICALSystem-level failureDatabase unavailable, disk full

Configuring Log Levels

Command Line

# Set global log level
odoo --log-level=debug

# Set per-handler log levels
odoo --log-handler=odoo.addons.my_module:DEBUG
odoo --log-handler=odoo.models:DEBUG
odoo --log-handler=werkzeug:WARNING

# Enable SQL logging
odoo --log-level=debug_sql

# Multiple handlers
odoo --log-handler=odoo.addons.my_module:DEBUG \
     --log-handler=odoo.http:INFO \
     --log-handler=werkzeug:WARNING

Configuration File (odoo.conf)

[options]
log_level = info
log_handler = :INFO,odoo.addons.my_module:DEBUG,werkzeug:WARNING
log_db = False
log_db_level = warning
logfile = /var/log/odoo/odoo-server.log
logrotate = True

Structured Logging Patterns

# Good: use format strings with %s (lazy evaluation)
_logger.info('Order %s confirmed for %s', order.name, partner.name)

# Bad: f-strings evaluate even if log level is disabled
_logger.debug(f'Order {order.name} confirmed')  # Always evaluates

# Good: include relevant context
_logger.error(
    'Payment failed for order %s (partner=%s, amount=%s): %s',
    order.name, order.partner_id.id, order.amount_total, error)

# Good: include traceback for errors
try:
    self._process()
except Exception:
    _logger.exception('Processing failed for %s', self.name)
    # .exception() auto-includes traceback

SQL Debugging

Enable SQL logging to see every database query:

# Show all SQL queries
odoo --log-level=debug_sql

# Or specific handler
odoo --log-handler=odoo.sql_db:DEBUG

This outputs every SQL query with execution time. Essential for finding N+1 query problems and optimizing performance.

Using pdb for Interactive Debugging

The Python debugger lets you pause execution and inspect state:

def action_process(self):
    import pdb; pdb.set_trace()  # execution stops here
    # Or in Python 3.7+:
    breakpoint()

    for record in self:
        record._do_work()

When execution hits the breakpoint, the terminal where Odoo is running becomes an interactive Python shell. Commands:

  • n — next line
  • s — step into function
  • c — continue execution
  • p variable — print variable value
  • l — show current code context
  • pp self.read() — pretty-print record data

Note: pdb only works when Odoo runs with --workers=0 (single-process mode). Multi-worker mode does not support interactive debugging.

Debugging ORM Queries

# Log the SQL generated by a search
self.env.cr.execute('EXPLAIN ANALYZE ' + query)

# Check what SQL the ORM generates
import logging
logging.getLogger('odoo.sql_db').setLevel(logging.DEBUG)
result = self.env['res.partner'].search(
    [('is_company', '=', True)])
logging.getLogger('odoo.sql_db').setLevel(logging.WARNING)

Request Tracing

Log HTTP request details for API debugging:

from odoo.http import request

_logger.info(
    'API request: %s %s from %s (uid=%s)',
    request.httprequest.method,
    request.httprequest.path,
    request.httprequest.remote_addr,
    request.uid)

Production Log Analysis

Common patterns for analyzing production logs:

# Find errors in the last hour
grep 'ERROR' /var/log/odoo/odoo-server.log | tail -50

# Find slow queries (over 100ms)
grep 'query.*[0-9]\{3,\}ms' /var/log/odoo/odoo-server.log

# Track a specific request
grep 'request_id_abc123' /var/log/odoo/odoo-server.log

# Monitor memory usage
grep 'VmRSS' /var/log/odoo/odoo-server.log

Log to Database

Odoo can log to a database table for analysis:

[options]
log_db = True
log_db_level = warning

Logs go to the ir_logging table, viewable in Settings > Technical > Database > Logging.

Custom Exception Handling

from odoo.exceptions import UserError, ValidationError

def action_validate(self):
    try:
        self._check_constraints()
    except ValidationError:
        _logger.warning(
            'Validation failed for %s', self.name,
            exc_info=True)
        raise  # re-raise to show user the error
    except Exception:
        _logger.exception(
            'Unexpected error validating %s', self.name)
        raise UserError(
            'An unexpected error occurred. '
            'Please contact support.')

Best Practices

  • Always use _logger with __name__, never print()
  • Use %s formatting (not f-strings) for lazy evaluation
  • Include record identifiers (name, id) in log messages for traceability
  • Use _logger.exception() in except blocks to auto-include tracebacks
  • Set production logs to INFO level, development to DEBUG
  • Rotate logs with logrotate=True or system logrotate