From b704d0676ab2d623d2eeb1ed4dc1bcf2a2c4a5e2 Mon Sep 17 00:00:00 2001 From: Thomas Mangin Date: Mon, 20 Apr 2020 16:07:59 +0100 Subject: airbag: T2186: generic syslog --- python/vyos/airbag.py | 20 +++---- python/vyos/logger.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 python/vyos/logger.py diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py index b0565192d..a2e9de491 100644 --- a/python/vyos/airbag.py +++ b/python/vyos/airbag.py @@ -15,15 +15,13 @@ import os import sys -import logging -import logging.handlers from datetime import datetime from vyos import debug from vyos.config import Config from vyos.version import get_version from vyos.util import run - +from vyos.logger import syslog # we allow to disable the extra logging DISABLE = False @@ -62,7 +60,7 @@ def bug_report(dtype, value, trace): information = { 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'version': get_version(), - 'trace': format_exception(dtype, value, trace), + 'trace': '\n'.join(format_exception(dtype, value, trace)), 'instructions': COMMUNITY if 'rolling' in get_version() else SUPPORTED, } @@ -82,19 +80,14 @@ def intercepter(dtype, value, trace): pdb.pm() -def InterceptingLogger(address, _singleton=[False]): +def InterceptingLogger(_singleton=[False]): skip = _singleton.pop() _singleton.append(True) if skip: return - logger = logging.getLogger('VyOS') - logger.setLevel(logging.DEBUG) - handler = logging.handlers.SysLogHandler(address='/dev/log', facility='syslog') - logger.addHandler(handler) - # log to syslog any message sent to stderr - sys.stderr = _IO(sys.stderr, logger.critical) + sys.stderr = _IO(sys.stderr, syslog.critical) # lists as default arguments in function is normally dangerous @@ -124,14 +117,15 @@ except: # running testing so we are checking that we are on the router # as otherwise it prevents dpkg-buildpackage to work if get_version() and insession: - InterceptingLogger('/run/systemd/journal/dev-log') + InterceptingLogger() InterceptingException(intercepter) # Messages to print +# if the key before the value has not time, syslog takes that as the source of the message FAULT = """\ -Date: {date} +Fault Time: {date} VyOS image: {version} {trace} diff --git a/python/vyos/logger.py b/python/vyos/logger.py new file mode 100644 index 000000000..f7cc964d5 --- /dev/null +++ b/python/vyos/logger.py @@ -0,0 +1,143 @@ +# Copyright 2020 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +# A wrapper class around logging to make it easier to use + +# for a syslog logger: +# from vyos.logger import syslog +# syslog.critical('message') + +# for a stderr logger: +# from vyos.logger import stderr +# stderr.critical('message') + +# for a custom logger (syslog and file): +# from vyos.logger import getLogger +# combined = getLogger(__name__, syslog=True, stream=sys.stdout, filename='/tmp/test') +# combined.critical('message') + +import sys +import logging +import logging.handlers as handlers + +TIMED = '%(asctime)s: %(message)s' +SHORT = '%(filename)s: %(message)s' +CLEAR = '%(levelname) %(asctime)s %(filename)s: %(message)s' + +_levels = { + 'CRITICAL': logging.CRITICAL, + 'ERROR': logging.CRITICAL, + 'WARNING': logging.WARNING, + 'INFO': logging.INFO, + 'DEBUG': logging.DEBUG, + 'NOTSET': logging.NOTSET, +} + +# prevent recreation of already created logger +_created = {} + +def getLogger(name=None, **kwargs): + if name in _created: + if len(kwargs) == 0: + return _created[name] + raise ValueError('a logger with the name "{name} already exists') + + logger = logging.getLogger(name) + logger.setLevel(_levels[kwargs.get('level', 'DEBUG')]) + + if 'address' in kwargs or kwargs.get('syslog', False): + logger.addHandler(_syslog(**kwargs)) + if 'stream' in kwargs: + logger.addHandler(_stream(**kwargs)) + if 'filename' in kwargs: + logger.addHandler(_file(**kwargs)) + + _created[name] = logger + return logger + + +def _syslog(**kwargs): + formating = kwargs.get('format', SHORT) + handler = handlers.SysLogHandler( + address=kwargs.get('address', '/dev/log'), + facility=kwargs.get('facility', 'syslog'), + ) + handler.setFormatter(logging.Formatter(formating)) + return handler + + +def _stream(**kwargs): + formating = kwargs.get('format', CLEAR) + handler = logging.StreamHandler( + stream=kwargs.get('stream', sys.stderr), + ) + handler.setFormatter(logging.Formatter(formating)) + return handler + + +def _file(**kwargs): + formating = kwargs.get('format', CLEAR) + handler = handlers.RotatingFileHandler( + filename=kwargs.get('filename', 1048576), + maxBytes=kwargs.get('maxBytes', 1048576), + backupCount=kwargs.get('backupCount', 3), + ) + handler.setFormatter(logging.Formatter(formating)) + return handler + + +# exported pre-built logger, please keep in mind that the names +# must be unique otherwise the logger are shared + +# a logger for stderr +stderr = getLogger( + 'VyOS Syslog', + format=SHORT, + stream=sys.stderr, + address='/dev/log' +) + +# a logger to syslog +syslog = getLogger( + 'VyOS StdErr', + format='%(message)s', + address='/dev/log' +) + + +# testing +if __name__ == '__main__': + # from vyos.logger import getLogger + formating = '%(asctime)s (%(filename)s) %(levelname)s: %(message)s' + + # syslog logger + # syslog=True if no 'address' field is provided + syslog = getLogger(__name__ + '.1', syslog=True, format=formating) + syslog.info('syslog test') + + # steam logger + stream = getLogger(__name__ + '.2', stream=sys.stdout, level='ERROR') + stream.info('steam test') + + # file logger + filelog = getLogger(__name__ + '.3', filename='/tmp/test') + filelog.info('file test') + + # create a combined logger + getLogger('VyOS', syslog=True, stream=sys.stdout, filename='/tmp/test') + + # recover the created logger from name + combined = getLogger('VyOS') + combined.info('combined test') -- cgit v1.2.3