From 3ad1feb54e489bffd2cb6ff2bf89a6669f1e4cdd Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Mon, 4 Feb 2013 13:42:25 +0100 Subject: new subpackage openlmi.common created for sharing code openlmi.common subpackage allows to share code accros various OpenLMI provider packages. Currently there is a cmpi_logging.py module. Resolves: #71 in openlmi trac -- Make common package under openlmi namespace for utilities shared accross python providers. --- src/python/openlmi/common/__init__.py | 24 ++++ src/python/openlmi/common/cmpi_logging.py | 203 ++++++++++++++++++++++++++++++ src/python/setup.py | 4 +- 3 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 src/python/openlmi/common/__init__.py create mode 100644 src/python/openlmi/common/cmpi_logging.py (limited to 'src/python') diff --git a/src/python/openlmi/common/__init__.py b/src/python/openlmi/common/__init__.py new file mode 100644 index 0000000..9acc604 --- /dev/null +++ b/src/python/openlmi/common/__init__.py @@ -0,0 +1,24 @@ +# Software Management Providers +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Michal Minar +# + +""" +Common utilities for OpenLMI python providers. +""" diff --git a/src/python/openlmi/common/cmpi_logging.py b/src/python/openlmi/common/cmpi_logging.py new file mode 100644 index 0000000..aa9cd71 --- /dev/null +++ b/src/python/openlmi/common/cmpi_logging.py @@ -0,0 +1,203 @@ +# -*- Coding:utf-8 -*- +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Jan Safranek + + +import logging +import inspect +import traceback + +TRACE_WARNING = logging.INFO - 1 +TRACE_INFO = logging.INFO - 2 +TRACE_VERBOSE = logging.DEBUG + +class CMPILogHandler(logging.Handler): + """ + A handler class, which sends log messages to CMPI log. + """ + + def __init__(self, cmpi_logger, *args, **kwargs): + self.cmpi_logger = cmpi_logger + super(CMPILogHandler, self).__init__(*args, **kwargs) + + def emit(self, record): + msg = self.format(record) + if record.levelno >= logging.ERROR: + self.cmpi_logger.log_error(msg) + elif record.levelno >= logging.WARNING: + self.cmpi_logger.log_warn(msg) + elif record.levelno >= logging.INFO: + self.cmpi_logger.log_info(msg) + elif record.levelno >= TRACE_WARNING: + self.cmpi_logger.trace_warn(record.filename, msg) + elif record.levelno >= TRACE_INFO: + self.cmpi_logger.trace_info(record.filename, msg) + elif record.levelno >= logging.DEBUG: + self.cmpi_logger.trace_verbose(record.filename, msg) + +class CMPILogger(logging.getLoggerClass()): + """ + A logger class, which adds trace_method level log methods. + """ + def trace_warn(self, msg, *args, **kwargs): + """ Log message with TRACE_WARNING severity. """ + self.log(TRACE_WARNING, msg, *args, **kwargs) + + def trace_info(self, msg, *args, **kwargs): + """ Log message with TRACE_INFO severity. """ + self.log(TRACE_INFO, msg, *args, **kwargs) + + def trace_verbose(self, msg, *args, **kwargs): + """ Log message with TRACE_VERBOSE severity. """ + self.log(TRACE_VERBOSE, msg, *args, **kwargs) + +logging.setLoggerClass(CMPILogger) + +def trace_method(func): + """ Decorator, trace entry and exit for a class method. """ + classname = inspect.getouterframes(inspect.currentframe())[1][3] + def helper_func(*args, **kwargs): + """ + Helper function, wrapping real function by trace_method decorator. + """ + logger.log(TRACE_VERBOSE, "Entering %s.%s", classname, func.__name__) + try: + ret = func(*args, **kwargs) + except Exception as exc: + if getattr(exc, "tb_printed", False) is False: + logger.exception("full traceback") + logger.log(TRACE_VERBOSE, "traceback: %s", + traceback.format_exc()) + exc.tb_printed = True + logger.log(TRACE_WARNING, "%s.%s threw exception %s", + classname, func.__name__, str(exc)) + raise + logger.log(TRACE_VERBOSE, "Exiting %s.%s", classname, func.__name__) + return ret + helper_func.__name__ = func.__name__ + helper_func.__doc__ = func.__doc__ + helper_func.__module__ = func.__module__ + return helper_func + +def trace_function(func): + """ Decorator, trace entry and exit for a function outside any class. """ + def helper_func(*args, **kwargs): + """ + Helper function, wrapping real function by trace_method decorator. + """ + logger.log(TRACE_VERBOSE, "Entering %s.%s", + func.__module__, func.__name__) + try: + ret = func(*args, **kwargs) + except Exception as exc: + if getattr(exc, "tb_printed", False) is False: + logger.exception("full traceback") + logger.log(TRACE_VERBOSE, "traceback: %s", + traceback.format_exc()) + exc.tb_printed = True + logger.log(TRACE_WARNING, "%s.%s threw exception %s", + func.__module__, func.__name__, str(exc)) + raise + logger.log(TRACE_VERBOSE, "Exiting %s", func.__name__) + return ret + helper_func.__name__ = func.__name__ + helper_func.__doc__ = func.__doc__ + helper_func.__module__ = func.__module__ + return helper_func + +class LogManager(object): + """ + Class, which takes care of CMPI logging. + There should be only one instance of this class and it should be + instantiated as soon as possible, even before reading a config. + The config file can be provided later by set_config call. + """ + FORMAT_STDERR = '%(levelname)s: %(message)s' + FORMAT_CMPI = '%(levelname)s: %(message)s' + + LOGGER_NAME = "openlmi.storage" + + def __init__(self, env): + """ + Initialize logging. + """ + formatter = logging.Formatter(self.FORMAT_CMPI) + + self.cmpi_handler = CMPILogHandler(env.get_logger()) + self.cmpi_handler.setLevel(logging.DEBUG) + self.cmpi_handler.setFormatter(formatter) + + self.logger = logging.getLogger(self.LOGGER_NAME) + self.logger.addHandler(self.cmpi_handler) + self.logger.setLevel(logging.DEBUG) + + self.stderr_handler = None + self.config = None + + global logger # IGNORE:W0603 + logger = self.logger + logger.info("CMPI log started") + + @trace_method + def set_config(self, config): + """ + Set a configuration of logging. It applies its setting immediately + and also subscribes for configuration changes. + """ + self.config = config + config.add_listener(self._config_changed) + # apply the config + self._config_changed(config) + + @trace_method + def _config_changed(self, config): + """ + Apply changed configuration, i.e. start/stop sending to stderr + and set appropriate log level. + """ + if config.tracing: + self.logger.setLevel(logging.DEBUG) + else: + self.logger.setLevel(logging.INFO) + if config.stderr: + # start sending to stderr + if not self.stderr_handler: + # create stderr handler + formatter = logging.Formatter(self.FORMAT_STDERR) + self.stderr_handler = logging.StreamHandler() + self.stderr_handler.setLevel(logging.DEBUG) + self.stderr_handler.setFormatter(formatter) + self.logger.addHandler(self.stderr_handler) + self.logger.info("Started logging to stderr.") + else: + # stop sending to stderr + if self.stderr_handler: + self.logger.info("Stopped logging to stderr.") + self.logger.removeHandler(self.stderr_handler) + self.stderr_handler = None + + def destroy(self): + if self.stderr_handler: + self.logger.removeHandler(self.stderr_handler) + self.stderr_handler = None + self.logger.removeHandler(self.cmpi_handler) + self.cmpi_handler = None + self.config.remove_listener(self._config_changed) + +logger = None diff --git a/src/python/setup.py b/src/python/setup.py index 8e72bfe..9d4ef7c 100644 --- a/src/python/setup.py +++ b/src/python/setup.py @@ -5,9 +5,9 @@ setup( author='Michal Minar', author_email='miminar@redhat.com', url='https://fedorahosted.org/openlmi/', - version='0.1', + version='0.2', namespace_packages = ['openlmi'], - packages = ['openlmi'], + packages = ['openlmi', 'openlmi.common'], classifiers=[ 'license :: osi approved :: gnu lesser general public license v2 or later (lgplv2+)', 'operating system :: posix :: linux', -- cgit