You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.7 KiB
103 lines
3.7 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2017 Camptocamp (http://www.camptocamp.com).
|
|
# @author Guewen Baconnier <guewen.baconnier@camptocamp.com>
|
|
# Copyright 2017 Akretion (http://www.akretion.com).
|
|
# @author Sébastien BEAU <sebastien.beau@akretion.com>
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
import logging
|
|
import os
|
|
import threading
|
|
import re
|
|
import json
|
|
|
|
from distutils.util import strtobool
|
|
from openerp.netsvc import ColoredFormatter
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
from pythonjsonlogger import jsonlogger
|
|
except ImportError:
|
|
jsonlogger = None # noqa
|
|
_logger.debug("Cannot 'import pythonjsonlogger'.")
|
|
|
|
|
|
def is_true(strval):
|
|
return bool(strtobool(strval or '0'.lower()))
|
|
|
|
|
|
# The following regex are use to extract information from native odoo log
|
|
# We struct is the following
|
|
# {name_of_the_log : [regex, formatter, extra_vals]}
|
|
# The extra_vals "dict" will be merged into the jsonlogger if the regex match
|
|
# In order to make understandable the regex please add an string example that
|
|
# match
|
|
REGEX = {
|
|
'openerp.addons.base.ir.ir_cron': [
|
|
# "cron.object.execute('db', 1, '*', u'base.action.rule', u'_check')"
|
|
(r"cron\.object\.execute\('.+'(?P<cron_model>[\w.]+).+"
|
|
r"'(?P<cron_method>[\w.]+)'\)",
|
|
{}, {'cron_running': True}),
|
|
|
|
# "0.016s (base.action.rule, _check)"
|
|
(r"(?P<cron_duration_second>[\d.]+)s \("
|
|
r"(?P<cron_model>[\w.]+), (?P<cron_method>[\w.]+)",
|
|
{'cron_duration_second': float},
|
|
{'cron_running': False, 'cron_status': 'succeeded'}),
|
|
|
|
# Call of self.pool.get('base.action.rule')._check(
|
|
# cr, uid, *u'()') failed in Job 43
|
|
(r"Call of self\.pool\.get\('(?P<cron_model>[\w.]+)'\)"
|
|
r".(?P<cron_method>[\w.]+)",
|
|
{}, {'cron_running': False, 'cron_status': 'failed'}),
|
|
],
|
|
}
|
|
|
|
|
|
class OdooJsonFormatter(jsonlogger.JsonFormatter):
|
|
|
|
def add_fields(self, log_record, record, message_dict):
|
|
record.pid = os.getpid()
|
|
record.dbname = getattr(threading.currentThread(), 'dbname', '?')
|
|
_super = super(OdooJsonFormatter, self)
|
|
res = _super.add_fields(log_record, record, message_dict)
|
|
if log_record['name'] in REGEX:
|
|
for regex, formatters, extra_vals in REGEX[log_record['name']]:
|
|
match = re.match(regex, log_record['message'])
|
|
if match:
|
|
vals = match.groupdict()
|
|
for key, func in formatters.items():
|
|
vals[key] = func(vals[key])
|
|
log_record.update(vals)
|
|
if extra_vals:
|
|
log_record.update(extra_vals)
|
|
return res
|
|
|
|
|
|
class OdooJsonDevFormatter(ColoredFormatter):
|
|
|
|
def format(self, record):
|
|
response = super(OdooJsonDevFormatter, self).format(record)
|
|
extra = {}
|
|
RESERVED_ATTRS = list(jsonlogger.RESERVED_ATTRS) + ["dbname", "pid"]
|
|
for key, value in record.__dict__.items():
|
|
if (key not in RESERVED_ATTRS and not
|
|
(hasattr(key, "startswith") and key.startswith('_'))):
|
|
extra[key] = value
|
|
if extra:
|
|
response += " extra: " + json.dumps(
|
|
extra, indent=4, sort_keys=True)
|
|
return response
|
|
|
|
|
|
if is_true(os.environ.get('ODOO_LOGGING_JSON')):
|
|
format = ('%(asctime)s %(pid)s %(levelname)s'
|
|
'%(dbname)s %(name)s: %(message)s')
|
|
formatter = OdooJsonFormatter(format)
|
|
logging.getLogger().handlers[0].formatter = formatter
|
|
elif is_true(os.environ.get('ODOO_LOGGING_JSON_DEV')):
|
|
format = ('%(asctime)s %(pid)s %(levelname)s'
|
|
'%(dbname)s %(name)s: %(message)s')
|
|
formatter = OdooJsonDevFormatter(format)
|
|
logging.getLogger().handlers[0].formatter = formatter
|