[IMP] Add a new Logging Handler, where we will store in the database of OpenERP.

this database can be overrided via the --log-pgsql-database

bzr revid: stw@openerp.com-20140228161147-s9nnrfq2tc94vq5p
This commit is contained in:
Stephane Wirtel 2014-02-28 17:11:47 +01:00
parent 3f779a5cae
commit 047d071a48
5 changed files with 162 additions and 26 deletions

View File

@ -111,15 +111,9 @@ def dispatch_rpc(service_name, method, params):
log(rpc_request, logging.DEBUG, logline, replace_request_password(params), depth=1)
return result
except openerp.osv.orm.except_orm:
raise
except openerp.exceptions.AccessError:
raise
except openerp.exceptions.AccessDenied:
raise
except openerp.exceptions.Warning:
raise
except openerp.exceptions.RedirectWarning:
except (openerp.osv.orm.except_orm, openerp.exceptions.AccessError, \
openerp.exceptions.AccessDenied, openerp.exceptions.Warning, \
openerp.exceptions.RedirectWarning):
raise
except openerp.exceptions.DeferredException, e:
_logger.exception(openerp.tools.exception_to_unicode(e))

View File

@ -0,0 +1 @@
from handlers import PostgreSQLHandler

100
openerp/loggers/handlers.py Normal file
View File

@ -0,0 +1,100 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2014 OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import contextlib
import datetime
import logging
import threading
import psycopg2
from openerp import tools
# The PostgreSQL Handler for the logging module, will be used by OpenERP to store the logs
# in the database, --log-pgsql-database=YOUR_DBNAME
# By default the system will use the current database
class PostgreSQLHandler(logging.Handler):
@contextlib.contextmanager
def create_connection(self):
db_name = None
db_name_from_cli = tools.config['log_pgsql_database']
if not db_name_from_cli:
# If there is no database, and only in this case, we are going to use the database
# from the current thread and create a connection to this database.
current_thread = threading.current_thread()
db_name_from_thread = getattr(current_thread, 'dbname', None)
if isinstance(db_name_from_thread, basestring):
db_name = db_name_from_thread
else:
db_name = db_name_from_cli
if not db_name:
return
parameters = {
'user': tools.config['db_user'] or None,
'password': tools.config['db_password'] or None,
'host': tools.config['db_host'] or None,
'port': tools.config['db_port'] or None,
'database': db_name,
}
try:
connection = psycopg2.connect(**parameters)
if connection:
yield connection
except Exception, ex: # Use a specific exception
print ex
def emit(self, record):
# We use a context manager to be tolerant to the errors (error of connections,...)
with self.create_connection() as conn:
exception = False
if record.exc_info:
exception = record.exc_text
now = datetime.datetime.utcnow()
current_thread = threading.current_thread()
uid = getattr(current_thread, 'uid', False)
dbname = getattr(current_thread, 'dbname', False)
parameters = (
now, uid, now, uid, 'server', dbname, record.name,
logging.getLevelName(record.levelno), record.msg, exception,
record.filename, record.funcName, record.lineno
)
with conn.cursor() as cursor:
cursor.execute("""
INSERT INTO ir_logging(
create_date, create_uid, write_date, write_uid,
type, dbname, name, level, message, exception, path, func,
line
)
VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""",
parameters
)
conn.commit()

View File

@ -29,6 +29,7 @@ import threading
import tools
import openerp
import openerp.loggers
_logger = logging.getLogger(__name__)
@ -85,6 +86,19 @@ class ColoredFormatter(DBFormatter):
record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname)
return DBFormatter.format(self, record)
import platform
def is_posix_operating_system():
return os.name == 'posix'
def is_windows_operating_system():
return os.name == 'nt'
def is_linux_operating_system():
return is_posix_operating_system() and platform.system() == 'Linux'
def is_macosx_operating_system():
return is_posix_operating_system() and platform.system() == 'Darwin'
def init_logger():
from tools.translate import resetlocale
resetlocale()
@ -94,24 +108,32 @@ def init_logger():
if tools.config['syslog']:
# SysLog Handler
if os.name == 'nt':
if is_windows_operating_system():
handler = logging.handlers.NTEventLogHandler("%s %s" % (release.description, release.version))
else:
elif is_linux_operating_system():
handler = logging.handlers.SysLogHandler('/dev/log')
format = '%s %s' % (release.description, release.version) \
+ ':%(dbname)s:%(levelname)s:%(name)s:%(message)s'
elif is_macosx_operating_system(): # There is no /dev/log on OSX
handler = logging.handlers.SysLogHandler('/var/run/log')
else:
raise Exception("There is no syslog handler for this Operating System: %s", platform.system())
format = '%s %s' % (release.description, release.version) + ':%(dbname)s:%(levelname)s:%(name)s:%(message)s'
elif tools.config['logfile']:
# LogFile Handler
logf = tools.config['logfile']
try:
# We check we have the right location for the log files
dirname = os.path.dirname(logf)
if dirname and not os.path.isdir(dirname):
os.makedirs(dirname)
if tools.config['logrotate'] is not False:
handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30)
elif os.name == 'posix':
handler = logging.handlers.TimedRotatingFileHandler(filename=logf, when='D', interval=1, backupCount=30)
elif is_posix_operating_system():
handler = logging.handlers.WatchedFileHandler(logf)
else:
handler = logging.handlers.FileHandler(logf)
except Exception:
@ -125,12 +147,14 @@ def init_logger():
# behind Apache with mod_wsgi, handler.stream will have type mod_wsgi.Log,
# which has no fileno() method. (mod_wsgi.Log is what is being bound to
# sys.stderr when the logging.StreamHandler is being constructed above.)
if isinstance(handler, logging.StreamHandler) \
and hasattr(handler.stream, 'fileno') \
and os.isatty(handler.stream.fileno()):
def has_fileno(stream):
return hasattr(stream, 'fileno') and os.isatty(stream.fileno())
if isinstance(handler, logging.StreamHandler) and has_fileno(handler.stream):
formatter = ColoredFormatter(format)
else:
formatter = DBFormatter(format)
handler.setFormatter(formatter)
# Configure handlers
@ -141,7 +165,9 @@ def init_logger():
logging_configurations = DEFAULT_LOG_CONFIGURATION + pseudo_config + logconfig
for logconfig_item in logging_configurations:
loggername, level = logconfig_item.split(':')
level = getattr(logging, level, logging.INFO)
logger = logging.getLogger(loggername)
logger.handlers = []
logger.setLevel(level)
@ -149,6 +175,13 @@ def init_logger():
if loggername != '':
logger.propagate = False
# magic ;-)
# we manage the connection in the postgresqlhandler
postgresqlHandler = openerp.loggers.handlers.PostgreSQLHandler()
postgresqlHandler.setLevel(logging.WARNING)
logger = logging.getLogger()
logger.addHandler(postgresqlHandler)
for logconfig_item in logging_configurations:
_logger.debug('logger level set: "%s"', logconfig_item)

View File

@ -72,9 +72,10 @@ class configmanager(object):
}
# Not exposed in the configuration file.
self.blacklist_for_save = set(
['publisher_warranty_url', 'load_language', 'root_path',
'init', 'save', 'config', 'update', 'stop_after_init'])
self.blacklist_for_save = set([
'publisher_warranty_url', 'load_language', 'root_path',
'init', 'save', 'config', 'update', 'stop_after_init'
])
# dictionary mapping option destination (keys in self.options) to MyOptions.
self.casts = {}
@ -83,7 +84,10 @@ class configmanager(object):
self.config_file = fname
self.has_ssl = check_ssl()
self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')])
self._LOGLEVELS = dict([
(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x))
for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')
])
version = "%s %s" % (release.description, release.version)
self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
@ -176,12 +180,16 @@ class configmanager(object):
group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG')
group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.http:DEBUG')
group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
group.add_option('--log-pgsql-database', dest='log_pgsql_database', help="database where OpenERP will store the exceptions", my_default=False)
# For backward-compatibility, map the old log levels to something
# quite close.
levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
levels = [
'info', 'debug_rpc', 'warn', 'test', 'critical',
'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset'
]
group.add_option('--log-level', dest='log_level', type='choice',
choices=levels, my_default='info',
help='specify the level of the logging. Accepted values: %s (deprecated option).' % (levels,))
parser.add_option_group(group)