first commit
This commit is contained in:
6
smile_log/tools/__init__.py
Executable file
6
smile_log/tools/__init__.py
Executable file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (C) 2020 Smile (<http://www.smile.fr>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from .db_handler import *
|
||||
from .db_logger import *
|
||||
64
smile_log/tools/db_handler.py
Executable file
64
smile_log/tools/db_handler.py
Executable file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (C) 2020 Smile (<http://www.smile.fr>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import registry
|
||||
|
||||
|
||||
class SmileDBHandler(logging.Handler):
|
||||
|
||||
def __init__(self, level=logging.NOTSET):
|
||||
logging.Handler.__init__(self, level)
|
||||
self._dbname_to_cr = {}
|
||||
|
||||
def _get_cursor(self, dbname):
|
||||
cr = self._dbname_to_cr.get(dbname)
|
||||
if not cr or (cr and cr.closed):
|
||||
cr = registry(dbname).cursor()
|
||||
cr.autocommit(True)
|
||||
self._dbname_to_cr[dbname] = cr
|
||||
return cr
|
||||
|
||||
def emit(self, record):
|
||||
if not (record.args and isinstance(record.args, dict)):
|
||||
return False
|
||||
|
||||
dbname = record.args.get('dbname', '')
|
||||
cr = self._get_cursor(dbname)
|
||||
|
||||
res_id = record.args.get('res_id', 0)
|
||||
pid = record.args.get('pid', 0)
|
||||
uid = record.args.get('uid', 0)
|
||||
model_name = record.args.get('model_name', '')
|
||||
|
||||
request = """INSERT INTO smile_log
|
||||
(log_date, log_uid, model_name, res_id, pid, level, message)
|
||||
VALUES (now() at time zone 'UTC', %s, %s, %s, %s, %s, %s)"""
|
||||
params = (uid, model_name, res_id, pid, record.levelname, record.msg,)
|
||||
|
||||
try:
|
||||
cr.execute(request, params)
|
||||
except Exception:
|
||||
# retry
|
||||
cr = self._get_cursor(dbname)
|
||||
cr.execute(request, params)
|
||||
return True
|
||||
|
||||
def close(self):
|
||||
logging.Handler.close(self)
|
||||
for cr in self._dbname_to_cr.values():
|
||||
try:
|
||||
cr.execute(
|
||||
"INSERT INTO smile_log "
|
||||
"(log_date, log_uid, model_name, "
|
||||
"res_id, pid, level, message) "
|
||||
"VALUES (now() at time zone 'UTC', 0, '', "
|
||||
"0, 0, 'INFO', 'Odoo server stopped')")
|
||||
finally:
|
||||
cr.close()
|
||||
self._dbname_to_cr = {}
|
||||
|
||||
|
||||
logging.getLogger('smile_log').addHandler(SmileDBHandler())
|
||||
84
smile_log/tools/db_logger.py
Executable file
84
smile_log/tools/db_logger.py
Executable file
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (C) 2020 Smile (<http://www.smile.fr>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from odoo import registry
|
||||
|
||||
from .misc import add_timing, add_trace
|
||||
|
||||
if sys.version_info > (3,):
|
||||
long = int
|
||||
|
||||
|
||||
class SmileDBLogger:
|
||||
|
||||
def __init__(self, dbname, model_name, res_id, uid=0):
|
||||
assert isinstance(uid, (int, long)), 'uid should be an integer'
|
||||
self._logger = logging.getLogger('smile_log')
|
||||
|
||||
pid = 0
|
||||
|
||||
try:
|
||||
cr = registry(dbname).cursor()
|
||||
cr.autocommit(True)
|
||||
cr.execute(
|
||||
"select relname from pg_class "
|
||||
"where relname='smile_log_seq'")
|
||||
if not cr.rowcount:
|
||||
cr.execute("create sequence smile_log_seq")
|
||||
cr.execute("select nextval('smile_log_seq')")
|
||||
res = cr.fetchone()
|
||||
pid = res and res[0] or 0
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
self._logger_start = datetime.datetime.now()
|
||||
self._logger_args = {
|
||||
'dbname': dbname, 'model_name': model_name,
|
||||
'res_id': res_id, 'uid': uid, 'pid': pid}
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
return self._logger_args['pid']
|
||||
|
||||
def setLevel(self, level):
|
||||
self._logger.setLevel(level)
|
||||
|
||||
def getEffectiveLevel(self):
|
||||
return self._logger.getEffectiveLevel()
|
||||
|
||||
def debug(self, msg):
|
||||
self._logger.debug(msg, self._logger_args)
|
||||
|
||||
def info(self, msg):
|
||||
self._logger.info(msg, self._logger_args)
|
||||
|
||||
def warning(self, msg):
|
||||
self._logger.warning(msg, self._logger_args)
|
||||
|
||||
def log(self, msg):
|
||||
self._logger.log(msg, self._logger_args)
|
||||
|
||||
@add_trace
|
||||
def error(self, msg):
|
||||
self._logger.error(msg, self._logger_args)
|
||||
|
||||
@add_trace
|
||||
def critical(self, msg):
|
||||
self._logger.critical(msg, self._logger_args)
|
||||
|
||||
@add_trace
|
||||
def exception(self, msg):
|
||||
self._logger.exception(msg, self._logger_args)
|
||||
|
||||
@add_timing
|
||||
def time_info(self, msg):
|
||||
self._logger.info(msg, self._logger_args)
|
||||
|
||||
@add_timing
|
||||
def time_debug(self, msg):
|
||||
self._logger.debug(msg, self._logger_args)
|
||||
23
smile_log/tools/misc.py
Executable file
23
smile_log/tools/misc.py
Executable file
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (C) 2020 Smile (<http://www.smile.fr>)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import datetime
|
||||
import traceback
|
||||
|
||||
|
||||
def add_timing(original_method):
|
||||
def new_method(self, msg):
|
||||
delay = datetime.datetime.now() - self._logger_start
|
||||
msg += " after %sh %smin %ss" % tuple(str(delay).split(':'))
|
||||
return original_method(self, msg)
|
||||
return new_method
|
||||
|
||||
|
||||
def add_trace(original_method):
|
||||
def new_method(self, msg):
|
||||
stack = traceback.format_exc()
|
||||
stack = stack.replace('%', '%%')
|
||||
msg += '\n%s' % stack
|
||||
return original_method(self, msg)
|
||||
return new_method
|
||||
Reference in New Issue
Block a user