Configuring Odoo Logging to Seq with pygelf
⚠️ Warning: tested on Odoo 11.0, 13.0, 15.0, 16.0, 17.0, and 18.0.
⚠️ Warning: Seq logging captures user data, starting the service may lock the users
and partners
tables, which can block or even crash updates. To prevent this, temporarily disable Seq logging during any update operations.
Install the pygelf package
⚠️ Warning: the latest release of pygelf is version 0.4.2, and it has not been updated since October 2021.
Install the pygelf
package using pip:
pip install pygelf
Update the Odoo configuration
In the __init__(self, fname=None)
method from the configmanager
class, add this line to introduce a new logging option for Seq::
group.add_option('--log-seq', dest='log_seq', help="Logging to seq")
When starting the Odoo server, you can specify the Seq server in one of two ways:
- Command Line:
./odoo-bin --log-seq=seq-server:port
- Configuration File:
Add the following to yourodoo.conf
file:log_seq = seq-server:port
Modify the Odoo Logger
Add Custom Logging Filters and Formatter. In the file odoo/netsvc.py
, include the following classes:
class uidFilterSeq(logging.Filter):
def filter(self, record):
if odoo and odoo.http and odoo.http.request:
if odoo.http.request.uid or odoo.http.request.session.uid:
if odoo.http.request.uid:
user_id = odoo.http.request.uid
elif odoo.http.request.session.uid:
user_id = odoo.http.request.session.uid
if odoo.http.request.uid or odoo.http.request.session.uid:
record.user_id = user_id
# res = get_partner_user(user_id)
# if res:
# record.user_partner = {'user_id': res['user_id'],
# 'user_login': res['user_login'],
# 'partner_id': res['partner_id'],
# 'partner_name': res['partner_name']}
if odoo.http.request.httprequest.cookies and 'visitor_uuid' in odoo.http.request.httprequest.cookies:
record.visitor_id = odoo.http.request.httprequest.cookies['visitor_uuid']
if odoo.http.request.httprequest.remote_addr:
record.client_address = odoo.http.request.httprequest.remote_addr
return True
class PerfFilterSeq(logging.Filter):
def filter(self, record):
if hasattr(threading.current_thread(), "query_count"):
query_count = threading.current_thread().query_count
query_time = threading.current_thread().query_time
perf_t0 = threading.current_thread().perf_t0
remaining_time = time.time() - perf_t0 - query_time
record.perf_info = {'query_count': "%d" % query_count,
'query_time': "%.3f" % query_time,
'remaining_time': "%.3f" % remaining_time}
delattr(threading.current_thread(), "query_count")
return True
class DBFormatterSeq(logging.Formatter):
def format(self, record):
record.pid = os.getpid()
return logging.Formatter.format(self, record)
Integrate the Seq Logging Handler. After the blocks handling tools.config['syslog']:
and elif tools.config['logfile']:
, add this snippet to configure Seq logging:
elif tools.cronfig['log_seq']:
loghost = tools.config['log_seq']
def record_factory_seq(*args, **kwargs):
record = old_factory(*args, **kwargs)
# We need to change the runbot to info level because seq don't have a runbot level
if record.levelno == logging.RUNBOT:
record.levelno = logging.INFO
return record
logging.setLogRecordFactory(record_factory_seq)
handler = GelfUdpHandler(
host=str(loghost.split(':')[0]), port=int(loghost.split(':')[1]),
include_extra_fields=True, debug=True
)
Then, between the block that checks for a POSIX system with a TTY stream and the else:
clause, insert the following code:
elif isinstance(handler, GelfUdpHandler):
formatter = DBFormatterSeq(format)
perf_filter = PerfFilterSeq()
handler.addFilter(uidFilterSeq())