# Copyright 2013 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import ConfigParser import inspect import logging import logging.config import os import re try: from oslo.config import cfg except ImportError: from oslo_config import cfg _DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" _DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" _loggers = {} def getLogger(name='unknown'): if len(_loggers) == 0: loaded = _load_log_config() getLogger.adapter = TestsAdapter if loaded else None if name not in _loggers: logger = logging.getLogger(name) if getLogger.adapter: _loggers[name] = getLogger.adapter(logger, name) else: _loggers[name] = logger return _loggers[name] def _load_log_config(): conf_dir = os.environ.get('FUEL_LOG_CONFIG_DIR', None) conf_file = os.environ.get('FUEL_LOG_CONFIG', None) if not conf_dir or not conf_file: return False log_config = os.path.join(conf_dir, conf_file) try: logging.config.fileConfig(log_config) except ConfigParser.Error as exc: raise cfg.ConfigFileParseError(log_config, str(exc)) return True class TestsAdapter(logging.LoggerAdapter): def __init__(self, logger, project_name): self.logger = logger self.project = project_name self.regexp = re.compile(r"test_\w+\.py") def __getattr__(self, key): return getattr(self.logger, key) def _get_test_name(self): frames = inspect.stack() for frame in frames: binary_name = frame[1] if self.regexp.search(binary_name) and 'self' in frame[0].f_locals: return frame[0].f_locals.get('self').id() elif frame[3] == '_run_cleanups': # NOTE(myamazaki): method calling addCleanup return frame[0].f_locals.get('self').case.id() elif frame[3] in ['setUpClass', 'tearDownClass']: # NOTE(myamazaki): setUpClass or tearDownClass return "%s.%s.%s" % (frame[0].f_locals['cls'].__module__, frame[0].f_locals['cls'].__name__, frame[3]) return None def process(self, msg, kwargs): if 'extra' not in kwargs: kwargs['extra'] = {} extra = kwargs['extra'] test_name = self._get_test_name() if test_name: extra.update({'testname': test_name}) extra['extra'] = extra.copy() return msg, kwargs class TestsFormatter(logging.Formatter): def __init__(self, fmt=None, datefmt=None): super(TestsFormatter, self).__init__() self.default_format = _DEFAULT_LOG_FORMAT self.testname_format =\ "%(asctime)s %(levelname)8s [%(testname)s] %(message)s" self.datefmt = _DEFAULT_LOG_DATE_FORMAT def format(self, record): extra = record.__dict__.get('extra', None) if extra and 'testname' in extra: self._fmt = self.testname_format else: self._fmt = self.default_format return logging.Formatter.format(self, record)