Real-Time Log Watcher for Custom Addons
When developing custom addons in Odoo, logging is one of the simplest and most useful ways to understand what your code is actually doing. However, constantly searching through the main Odoo log can become inconvenient, especially when you are working on a specific customization and want to see only the messages generated by your own code.
In this tutorial, we will create a small Odoo custom addon with its own dedicated development log, development.log. We will add a simple logger, create a test button that generates messages at regular intervals, and use Linux's tail -f command to watch those messages appear in real time.
The goal is not to build a complicated logging framework. Instead, we will create a small and practical setup that can be reused while developing and debugging custom Odoo addons.
This video will help you understand, try, and manage the process step by step. The full code is provided below, so feel free to copy and paste it for your own use.
Create a Real-Time Log Watcher for Custom Addons
Follow the tutorial below to create a realtime log watcher for your Odoo custom addons.
sudo touch /var/log/odoo/development.log
sudo chown odoo:odoo /var/log/odoo/development.log
sudo chmod 640 /var/log/odoo/development.log
sudo systemctl restart odoo
cd /opt/odoo/odoo-server/custom_addons
sudo mkdir -p custom_logger/models
sudo mkdir -p custom_logger/views
sudo mkdir -p custom_logger/security
#see structure
tree custom_logger
#create __manifest__.py
sudo nano custom_logger/__manifest__.py
#create __init__.py
sudo nano custom_logger/__init__.py
#create ./models/__init__.py
sudo nano custom_logger/models/__init__.py
#create ./models/custom_logger.py
sudo nano custom_logger/models/custom_logger.py
#create ./views/custom_logger_views.xml
sudo nano custom_logger/views/custom_logger_views.xml
#create ./security/ir.model.access.csv
sudo nano custom_logger/security/ir.model.access.csv
#restart odoo
systemctl restart odoo
#./custom_logger/__manifest__.py
{
'name': 'Custom Logger',
'version': '18.0.1.0.0',
'category': 'Tools',
'depends': [
'base',
],
'data': [
'security/ir.model.access.csv',
'views/custom_logger_views.xml',
],
'installable': True,
'application': True,
}
#./custom_logger/__init__.py
from . import models
#./models/__init__.py
from . import custom_logger
#./models/custom_logger.py
import logging
import time
from logging.handlers import RotatingFileHandler
from odoo import models
# ============================================================
# Normal Odoo logger
# ============================================================
_logger = logging.getLogger(__name__)
# ============================================================
# Dedicated Development Logger
# ============================================================
DEVELOPMENT_LOG_FILE = '/var/log/odoo/development.log'
development_logger = logging.getLogger('odoo.development')
development_logger.setLevel(logging.INFO)
development_logger.propagate = False
if not development_logger.handlers:
handler = RotatingFileHandler(
DEVELOPMENT_LOG_FILE,
maxBytes=5 * 1024 * 1024,
backupCount=3,
)
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(name)s - %(message)s'
)
handler.setFormatter(formatter)
development_logger.addHandler(handler)
# ============================================================
# Test Model
# ============================================================
class CustomLogger(models.Model):
_name = 'custom.logger'
_description = 'Custom Logger'
def action_custom_logger(self):
development_logger.info(
'================================================'
)
development_logger.info(
'Development custom logger started.'
)
for number in range(1, 11):
development_logger.info(
'TEST MESSAGE - number: %s',
number,
)
time.sleep(2)
development_logger.info(
'Development custom logger finished.'
)
development_logger.info(
'================================================'
)
return True
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ===================================================== -->
<!-- Custom Logger Form -->
<!-- ===================================================== -->
<record id="view_custom_logger_form" model="ir.ui.view">
<field name="name">custom.logger.form</field>
<field name="model">custom.logger</field>
<field name="arch" type="xml">
<form string="Custom Logger">
<sheet>
<div class="oe_title">
<h1>Custom Logger</h1>
</div>
<button
name="action_custom_logger"
type="object"
string="Run Custom Logger"
class="btn-primary"
/>
</sheet>
</form>
</field>
</record>
<!-- ===================================================== -->
<!-- Window Action -->
<!-- ===================================================== -->
<record id="action_custom_logger" model="ir.actions.act_window">
<field name="name">Custom Logger</field>
<field name="res_model">custom.logger</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_custom_logger_form"/>
</record>
<!-- ===================================================== -->
<!-- Application Menu -->
<!-- ===================================================== -->
<menuitem
id="menu_custom_logger"
name="Custom Logger"
action="action_custom_logger"
sequence="10"
/>
</odoo>
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_custom_logger_user,access.custom.logger.user,model_custom_logger,,1,1,1,1
#command to run the tail
sudo tail -f /var/log/odoo/development.log
#command to clear and run
clear && sudo truncate -s 0 /var/log/odoo/development.log && sudo tail -f /var/log/odoo/development.log
#./models/custom_logger.py (improved)
...
import random
...
for number in range(1, 11):
random_number = random.randint(1, 100)
development_logger.info(
'TEST MESSAGE - number: %s of random %s',
number,
number_random,
)
time.sleep(2)
...
|
8 Comments
Georgia Reader Reply
Et rerum totam nisi. Molestiae vel quam dolorum vel voluptatem et et. Est ad aut sapiente quis molestiae est qui cum soluta. Vero aut rerum vel. Rerum quos laboriosam placeat ex qui. Sint qui facilis et.
Aron Alvarado Reply
Ipsam tempora sequi voluptatem quis sapiente non. Autem itaque eveniet saepe. Officiis illo ut beatae.
Lynda Small Reply
Enim ipsa eum fugiat fuga repellat. Commodi quo quo dicta. Est ullam aspernatur ut vitae quia mollitia id non. Qui ad quas nostrum rerum sed necessitatibus aut est. Eum officiis sed repellat maxime vero nisi natus. Amet nesciunt nesciunt qui illum omnis est et dolor recusandae. Recusandae sit ad aut impedit et. Ipsa labore dolor impedit et natus in porro aut. Magnam qui cum. Illo similique occaecati nihil modi eligendi. Pariatur distinctio labore omnis incidunt et illum. Expedita et dignissimos distinctio laborum minima fugiat. Libero corporis qui. Nam illo odio beatae enim ducimus. Harum reiciendis error dolorum non autem quisquam vero rerum neque.
Sianna Ramsay Reply
Et dignissimos impedit nulla et quo distinctio ex nemo. Omnis quia dolores cupiditate et. Ut unde qui eligendi sapiente omnis ullam. Placeat porro est commodi est officiis voluptas repellat quisquam possimus. Perferendis id consectetur necessitatibus.
Nolan Davidson Reply
Distinctio nesciunt rerum reprehenderit sed. Iste omnis eius repellendus quia nihil ut accusantium tempore. Nesciunt expedita id dolor exercitationem aspernatur aut quam ut. Voluptatem est accusamus iste at. Non aut et et esse qui sit modi neque. Exercitationem et eos aspernatur. Ea est consequuntur officia beatae ea aut eos soluta. Non qui dolorum voluptatibus et optio veniam. Quam officia sit nostrum dolorem.
Kay Duggan Reply
Dolorem atque aut. Omnis doloremque blanditiis quia eum porro quis ut velit tempore. Cumque sed quia ut maxime. Est ad aut cum. Ut exercitationem non in fugiat.