diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-01-29 21:01:43 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-01-29 21:01:43 +0000 |
commit | e25a1852338bf604762562f73c00608f6eb6f65e (patch) | |
tree | 095cfb42237cdaf6ced8e0e4fb249d1e49277d2d | |
parent | 734e85fb16480a5eb811394216317bd6d02a9ab6 (diff) | |
parent | 2fbf50652664a67a21dbb98a5b5dde26875eb30d (diff) | |
download | oslo-incubator-e25a1852338bf604762562f73c00608f6eb6f65e.tar.gz |
Merge "Remove oslo.log code and clean up versionutils API"
-rw-r--r-- | openstack/common/eventlet_backdoor.py | 2 | ||||
-rw-r--r-- | openstack/common/fixture/__init__.py | 0 | ||||
-rw-r--r-- | openstack/common/fixture/logging.py | 34 | ||||
-rw-r--r-- | openstack/common/log.py | 718 | ||||
-rw-r--r-- | openstack/common/loopingcall.py | 2 | ||||
-rw-r--r-- | openstack/common/periodic_task.py | 2 | ||||
-rw-r--r-- | openstack/common/policy.py | 2 | ||||
-rw-r--r-- | openstack/common/quota.py | 6 | ||||
-rw-r--r-- | openstack/common/scheduler/base_filter.py | 2 | ||||
-rw-r--r-- | openstack/common/scheduler/filters/capabilities_filter.py | 3 | ||||
-rw-r--r-- | openstack/common/scheduler/filters/ignore_attempted_hosts_filter.py | 3 | ||||
-rw-r--r-- | openstack/common/service.py | 7 | ||||
-rw-r--r-- | openstack/common/systemd.py | 3 | ||||
-rw-r--r-- | openstack/common/threadgroup.py | 2 | ||||
-rw-r--r-- | openstack/common/versionutils.py | 56 | ||||
-rw-r--r-- | test-requirements.txt | 1 | ||||
-rw-r--r-- | tests/unit/fixture/test_logging.py | 28 | ||||
-rw-r--r-- | tests/unit/test_deprecated.py | 71 | ||||
-rw-r--r-- | tests/unit/test_log.py | 681 | ||||
-rw-r--r-- | tests/unit/test_service.py | 2 | ||||
-rw-r--r-- | tests/unit/test_versionutils.py | 76 | ||||
-rw-r--r-- | tox.ini | 2 |
22 files changed, 156 insertions, 1547 deletions
diff --git a/openstack/common/eventlet_backdoor.py b/openstack/common/eventlet_backdoor.py index 31d7f24c..091cc772 100644 --- a/openstack/common/eventlet_backdoor.py +++ b/openstack/common/eventlet_backdoor.py @@ -19,6 +19,7 @@ from __future__ import print_function import copy import errno import gc +import logging import os import pprint import socket @@ -30,7 +31,6 @@ import greenlet from oslo.config import cfg from openstack.common._i18n import _LI -from openstack.common import log as logging help_for_backdoor_port = ( "Acceptable values are 0, <port>, and <start>:<end>, where 0 results " diff --git a/openstack/common/fixture/__init__.py b/openstack/common/fixture/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/openstack/common/fixture/__init__.py +++ /dev/null diff --git a/openstack/common/fixture/logging.py b/openstack/common/fixture/logging.py deleted file mode 100644 index 3823a035..00000000 --- a/openstack/common/fixture/logging.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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 fixtures - - -def get_logging_handle_error_fixture(): - """returns a fixture to make logging raise formatting exceptions. - - Usage: - self.useFixture(logging.get_logging_handle_error_fixture()) - """ - return fixtures.MonkeyPatch('logging.Handler.handleError', - _handleError) - - -def _handleError(self, record): - """Monkey patch for logging.Handler.handleError. - - The default handleError just logs the error to stderr but we want - the option of actually raising an exception. - """ - raise diff --git a/openstack/common/log.py b/openstack/common/log.py deleted file mode 100644 index 0ed95e04..00000000 --- a/openstack/common/log.py +++ /dev/null @@ -1,718 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""OpenStack logging handler. - -This module adds to logging functionality by adding the option to specify -a context object when calling the various log methods. If the context object -is not specified, default formatting is used. Additionally, an instance uuid -may be passed as part of the log message, which is intended to make it easier -for admins to find messages related to a specific instance. - -It also allows setting of formatting information through conf. - -""" - -import copy -import inspect -import itertools -import logging -import logging.config -import logging.handlers -import os -import socket -import sys -import traceback - -from oslo.config import cfg -from oslo.utils import importutils -from oslo_serialization import jsonutils -import six -from six import moves - -_PY26 = sys.version_info[0:2] == (2, 6) - -from openstack.common._i18n import _ -from openstack.common import local - - -_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" - - -common_cli_opts = [ - cfg.BoolOpt('debug', - short='d', - default=False, - help='Print debugging output (set logging level to ' - 'DEBUG instead of default WARNING level).'), - cfg.BoolOpt('verbose', - short='v', - default=False, - help='Print more verbose output (set logging level to ' - 'INFO instead of default WARNING level).'), -] - -logging_cli_opts = [ - cfg.StrOpt('log-config-append', - metavar='PATH', - deprecated_name='log-config', - help='The name of a logging configuration file. This file ' - 'is appended to any existing logging configuration ' - 'files. For details about logging configuration files, ' - 'see the Python logging module documentation.'), - cfg.StrOpt('log-format', - metavar='FORMAT', - help='DEPRECATED. ' - 'A logging.Formatter log message format string which may ' - 'use any of the available logging.LogRecord attributes. ' - 'This option is deprecated. Please use ' - 'logging_context_format_string and ' - 'logging_default_format_string instead.'), - cfg.StrOpt('log-date-format', - default=_DEFAULT_LOG_DATE_FORMAT, - metavar='DATE_FORMAT', - help='Format string for %%(asctime)s in log records. ' - 'Default: %(default)s .'), - cfg.StrOpt('log-file', - metavar='PATH', - deprecated_name='logfile', - help='(Optional) Name of log file to output to. ' - 'If no default is set, logging will go to stdout.'), - cfg.StrOpt('log-dir', - deprecated_name='logdir', - help='(Optional) The base directory used for relative ' - '--log-file paths.'), - cfg.BoolOpt('use-syslog', - default=False, - help='Use syslog for logging. ' - 'Existing syslog format is DEPRECATED during I, ' - 'and will change in J to honor RFC5424.'), - cfg.BoolOpt('use-syslog-rfc-format', - # TODO(bogdando) remove or use True after existing - # syslog format deprecation in J - default=False, - help='(Optional) Enables or disables syslog rfc5424 format ' - 'for logging. If enabled, prefixes the MSG part of the ' - 'syslog message with APP-NAME (RFC5424). The ' - 'format without the APP-NAME is deprecated in I, ' - 'and will be removed in J.'), - cfg.StrOpt('syslog-log-facility', - default='LOG_USER', - help='Syslog facility to receive log lines.') -] - -generic_log_opts = [ - cfg.BoolOpt('use_stderr', - default=True, - help='Log output to standard error.') -] - -DEFAULT_LOG_LEVELS = ['amqp=WARN', 'amqplib=WARN', 'boto=WARN', - 'qpid=WARN', 'sqlalchemy=WARN', 'suds=INFO', - 'oslo.messaging=INFO', 'iso8601=WARN', - 'requests.packages.urllib3.connectionpool=WARN', - 'urllib3.connectionpool=WARN', 'websocket=WARN', - "keystonemiddleware=WARN", "routes.middleware=WARN", - "stevedore=WARN"] - -log_opts = [ - cfg.StrOpt('logging_context_format_string', - default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [%(request_id)s %(user_identity)s] ' - '%(instance)s%(message)s', - help='Format string to use for log messages with context.'), - cfg.StrOpt('logging_default_format_string', - default='%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [-] %(instance)s%(message)s', - help='Format string to use for log messages without context.'), - cfg.StrOpt('logging_debug_format_suffix', - default='%(funcName)s %(pathname)s:%(lineno)d', - help='Data to append to log format when level is DEBUG.'), - cfg.StrOpt('logging_exception_prefix', - default='%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s ' - '%(instance)s', - help='Prefix each line of exception output with this format.'), - cfg.ListOpt('default_log_levels', - default=DEFAULT_LOG_LEVELS, - help='List of logger=LEVEL pairs.'), - cfg.BoolOpt('publish_errors', - default=False, - help='Enables or disables publication of error events.'), - cfg.BoolOpt('fatal_deprecations', - default=False, - help='Enables or disables fatal status of deprecations.'), - - # NOTE(mikal): there are two options here because sometimes we are handed - # a full instance (and could include more information), and other times we - # are just handed a UUID for the instance. - cfg.StrOpt('instance_format', - default='[instance: %(uuid)s] ', - help='The format for an instance that is passed with the log ' - 'message.'), - cfg.StrOpt('instance_uuid_format', - default='[instance: %(uuid)s] ', - help='The format for an instance UUID that is passed with the ' - 'log message.'), -] - -CONF = cfg.CONF -CONF.register_cli_opts(common_cli_opts) -CONF.register_cli_opts(logging_cli_opts) -CONF.register_opts(generic_log_opts) -CONF.register_opts(log_opts) - - -def list_opts(): - """Entry point for oslo-config-generator.""" - return [(None, copy.deepcopy(common_cli_opts)), - (None, copy.deepcopy(logging_cli_opts)), - (None, copy.deepcopy(generic_log_opts)), - (None, copy.deepcopy(log_opts)), - ] - - -# our new audit level -# NOTE(jkoelker) Since we synthesized an audit level, make the logging -# module aware of it so it acts like other levels. -logging.AUDIT = logging.INFO + 1 -logging.addLevelName(logging.AUDIT, 'AUDIT') - - -try: - NullHandler = logging.NullHandler -except AttributeError: # NOTE(jkoelker) NullHandler added in Python 2.7 - class NullHandler(logging.Handler): - def handle(self, record): - pass - - def emit(self, record): - pass - - def createLock(self): - self.lock = None - - -def _dictify_context(context): - if context is None: - return None - if not isinstance(context, dict) and getattr(context, 'to_dict', None): - context = context.to_dict() - return context - - -def _get_binary_name(): - return os.path.basename(inspect.stack()[-1][1]) - - -def _get_log_file_path(binary=None): - logfile = CONF.log_file - logdir = CONF.log_dir - - if logfile and not logdir: - return logfile - - if logfile and logdir: - return os.path.join(logdir, logfile) - - if logdir: - binary = binary or _get_binary_name() - return '%s.log' % (os.path.join(logdir, binary),) - - return None - - -class BaseLoggerAdapter(logging.LoggerAdapter): - - def audit(self, msg, *args, **kwargs): - self.log(logging.AUDIT, msg, *args, **kwargs) - - def isEnabledFor(self, level): - if _PY26: - # This method was added in python 2.7 (and it does the exact - # same logic, so we need to do the exact same logic so that - # python 2.6 has this capability as well). - return self.logger.isEnabledFor(level) - else: - return super(BaseLoggerAdapter, self).isEnabledFor(level) - - -class LazyAdapter(BaseLoggerAdapter): - def __init__(self, name='unknown', version='unknown'): - self._logger = None - self.extra = {} - self.name = name - self.version = version - - @property - def logger(self): - if not self._logger: - self._logger = getLogger(self.name, self.version) - if six.PY3: - # In Python 3, the code fails because the 'manager' attribute - # cannot be found when using a LoggerAdapter as the - # underlying logger. Work around this issue. - self._logger.manager = self._logger.logger.manager - return self._logger - - -class ContextAdapter(BaseLoggerAdapter): - warn = logging.LoggerAdapter.warning - - def __init__(self, logger, project_name, version_string): - self.logger = logger - self.project = project_name - self.version = version_string - self._deprecated_messages_sent = dict() - - @property - def handlers(self): - return self.logger.handlers - - def deprecated(self, msg, *args, **kwargs): - """Call this method when a deprecated feature is used. - - If the system is configured for fatal deprecations then the message - is logged at the 'critical' level and :class:`DeprecatedConfig` will - be raised. - - Otherwise, the message will be logged (once) at the 'warn' level. - - :raises: :class:`DeprecatedConfig` if the system is configured for - fatal deprecations. - - """ - stdmsg = _("Deprecated: %s") % msg - if CONF.fatal_deprecations: - self.critical(stdmsg, *args, **kwargs) - raise DeprecatedConfig(msg=stdmsg) - - # Using a list because a tuple with dict can't be stored in a set. - sent_args = self._deprecated_messages_sent.setdefault(msg, list()) - - if args in sent_args: - # Already logged this message, so don't log it again. - return - - sent_args.append(args) - self.warn(stdmsg, *args, **kwargs) - - def process(self, msg, kwargs): - # NOTE(jecarey): If msg is not unicode, coerce it into unicode - # before it can get to the python logging and - # possibly cause string encoding trouble - if not isinstance(msg, six.text_type): - msg = six.text_type(msg) - - if 'extra' not in kwargs: - kwargs['extra'] = {} - extra = kwargs['extra'] - - context = kwargs.pop('context', None) - if not context: - context = getattr(local.store, 'context', None) - if context: - extra.update(_dictify_context(context)) - - instance = kwargs.pop('instance', None) - instance_uuid = (extra.get('instance_uuid') or - kwargs.pop('instance_uuid', None)) - instance_extra = '' - if instance: - instance_extra = CONF.instance_format % instance - elif instance_uuid: - instance_extra = (CONF.instance_uuid_format - % {'uuid': instance_uuid}) - extra['instance'] = instance_extra - - extra.setdefault('user_identity', kwargs.pop('user_identity', None)) - - extra['project'] = self.project - extra['version'] = self.version - extra['extra'] = extra.copy() - return msg, kwargs - - -class JSONFormatter(logging.Formatter): - def __init__(self, fmt=None, datefmt=None): - # NOTE(jkoelker) we ignore the fmt argument, but its still there - # since logging.config.fileConfig passes it. - self.datefmt = datefmt - - def formatException(self, ei, strip_newlines=True): - lines = traceback.format_exception(*ei) - if strip_newlines: - lines = [moves.filter( - lambda x: x, - line.rstrip().splitlines()) for line in lines] - lines = list(itertools.chain(*lines)) - return lines - - def format(self, record): - message = {'message': record.getMessage(), - 'asctime': self.formatTime(record, self.datefmt), - 'name': record.name, - 'msg': record.msg, - 'args': record.args, - 'levelname': record.levelname, - 'levelno': record.levelno, - 'pathname': record.pathname, - 'filename': record.filename, - 'module': record.module, - 'lineno': record.lineno, - 'funcname': record.funcName, - 'created': record.created, - 'msecs': record.msecs, - 'relative_created': record.relativeCreated, - 'thread': record.thread, - 'thread_name': record.threadName, - 'process_name': record.processName, - 'process': record.process, - 'traceback': None} - - if hasattr(record, 'extra'): - message['extra'] = record.extra - - if record.exc_info: - message['traceback'] = self.formatException(record.exc_info) - - return jsonutils.dumps(message) - - -def _create_logging_excepthook(product_name): - def logging_excepthook(exc_type, value, tb): - extra = {'exc_info': (exc_type, value, tb)} - getLogger(product_name).critical( - "".join(traceback.format_exception_only(exc_type, value)), - **extra) - return logging_excepthook - - -class LogConfigError(Exception): - - message = _('Error loading logging config %(log_config)s: %(err_msg)s') - - def __init__(self, log_config, err_msg): - self.log_config = log_config - self.err_msg = err_msg - - def __str__(self): - return self.message % dict(log_config=self.log_config, - err_msg=self.err_msg) - - -def _load_log_config(log_config_append): - try: - logging.config.fileConfig(log_config_append, - disable_existing_loggers=False) - except (moves.configparser.Error, KeyError) as exc: - raise LogConfigError(log_config_append, six.text_type(exc)) - - -def setup(product_name, version='unknown'): - """Setup logging.""" - if CONF.log_config_append: - _load_log_config(CONF.log_config_append) - else: - _setup_logging_from_conf(product_name, version) - sys.excepthook = _create_logging_excepthook(product_name) - - -def set_defaults(logging_context_format_string=None, - default_log_levels=None): - # Just in case the caller is not setting the - # default_log_level. This is insurance because - # we introduced the default_log_level parameter - # later in a backwards in-compatible change - if default_log_levels is not None: - cfg.set_defaults( - log_opts, - default_log_levels=default_log_levels) - if logging_context_format_string is not None: - cfg.set_defaults( - log_opts, - logging_context_format_string=logging_context_format_string) - - -def _find_facility_from_conf(): - facility_names = logging.handlers.SysLogHandler.facility_names - facility = getattr(logging.handlers.SysLogHandler, - CONF.syslog_log_facility, - None) - - if facility is None and CONF.syslog_log_facility in facility_names: - facility = facility_names.get(CONF.syslog_log_facility) - - if facility is None: - valid_facilities = facility_names.keys() - consts = ['LOG_AUTH', 'LOG_AUTHPRIV', 'LOG_CRON', 'LOG_DAEMON', - 'LOG_FTP', 'LOG_KERN', 'LOG_LPR', 'LOG_MAIL', 'LOG_NEWS', - 'LOG_AUTH', 'LOG_SYSLOG', 'LOG_USER', 'LOG_UUCP', - 'LOG_LOCAL0', 'LOG_LOCAL1', 'LOG_LOCAL2', 'LOG_LOCAL3', - 'LOG_LOCAL4', 'LOG_LOCAL5', 'LOG_LOCAL6', 'LOG_LOCAL7'] - valid_facilities.extend(consts) - raise TypeError(_('syslog facility must be one of: %s') % - ', '.join("'%s'" % fac - for fac in valid_facilities)) - - return facility - - -class RFCSysLogHandler(logging.handlers.SysLogHandler): - def __init__(self, *args, **kwargs): - self.binary_name = _get_binary_name() - # Do not use super() unless type(logging.handlers.SysLogHandler) - # is 'type' (Python 2.7). - # Use old style calls, if the type is 'classobj' (Python 2.6) - logging.handlers.SysLogHandler.__init__(self, *args, **kwargs) - - def format(self, record): - # Do not use super() unless type(logging.handlers.SysLogHandler) - # is 'type' (Python 2.7). - # Use old style calls, if the type is 'classobj' (Python 2.6) - msg = logging.handlers.SysLogHandler.format(self, record) - msg = self.binary_name + ' ' + msg - return msg - - -def _setup_logging_from_conf(project, version): - log_root = getLogger(None).logger - for handler in log_root.handlers: - log_root.removeHandler(handler) - - logpath = _get_log_file_path() - if logpath: - filelog = logging.handlers.WatchedFileHandler(logpath) - log_root.addHandler(filelog) - - if CONF.use_stderr: - streamlog = ColorHandler() - log_root.addHandler(streamlog) - - elif not logpath: - # pass sys.stdout as a positional argument - # python2.6 calls the argument strm, in 2.7 it's stream - streamlog = logging.StreamHandler(sys.stdout) - log_root.addHandler(streamlog) - - if CONF.publish_errors: - handler = importutils.import_object( - "oslo.messaging.notify.log_handler.PublishErrorsHandler", - logging.ERROR) - log_root.addHandler(handler) - - datefmt = CONF.log_date_format - for handler in log_root.handlers: - # NOTE(alaski): CONF.log_format overrides everything currently. This - # should be deprecated in favor of context aware formatting. - if CONF.log_format: - handler.setFormatter(logging.Formatter(fmt=CONF.log_format, - datefmt=datefmt)) - log_root.info('Deprecated: log_format is now deprecated and will ' - 'be removed in the next release') - else: - handler.setFormatter(ContextFormatter(project=project, - version=version, - datefmt=datefmt)) - - if CONF.debug: - log_root.setLevel(logging.DEBUG) - elif CONF.verbose: - log_root.setLevel(logging.INFO) - else: - log_root.setLevel(logging.WARNING) - - for pair in CONF.default_log_levels: - mod, _sep, level_name = pair.partition('=') - logger = logging.getLogger(mod) - # NOTE(AAzza) in python2.6 Logger.setLevel doesn't convert string name - # to integer code. - if sys.version_info < (2, 7): - level = logging.getLevelName(level_name) - logger.setLevel(level) - else: - logger.setLevel(level_name) - - if CONF.use_syslog: - try: - facility = _find_facility_from_conf() - # TODO(bogdando) use the format provided by RFCSysLogHandler - # after existing syslog format deprecation in J - if CONF.use_syslog_rfc_format: - syslog = RFCSysLogHandler(address='/dev/log', - facility=facility) - else: - syslog = logging.handlers.SysLogHandler(address='/dev/log', - facility=facility) - log_root.addHandler(syslog) - except socket.error: - log_root.error('Unable to add syslog handler. Verify that syslog ' - 'is running.') - - -_loggers = {} - - -def getLogger(name='unknown', version='unknown'): - if name not in _loggers: - _loggers[name] = ContextAdapter(logging.getLogger(name), - name, - version) - return _loggers[name] - - -def getLazyLogger(name='unknown', version='unknown'): - """Returns lazy logger. - - Creates a pass-through logger that does not create the real logger - until it is really needed and delegates all calls to the real logger - once it is created. - """ - return LazyAdapter(name, version) - - -class WritableLogger(object): - """A thin wrapper that responds to `write` and logs.""" - - def __init__(self, logger, level=logging.INFO): - self.logger = logger - self.level = level - - def write(self, msg): - self.logger.log(self.level, msg.rstrip()) - - -class ContextFormatter(logging.Formatter): - """A context.RequestContext aware formatter configured through flags. - - The flags used to set format strings are: logging_context_format_string - and logging_default_format_string. You can also specify - logging_debug_format_suffix to append extra formatting if the log level is - debug. - - For information about what variables are available for the formatter see: - http://docs.python.org/library/logging.html#formatter - - If available, uses the context value stored in TLS - local.store.context - - """ - - def __init__(self, *args, **kwargs): - """Initialize ContextFormatter instance - - Takes additional keyword arguments which can be used in the message - format string. - - :keyword project: project name - :type project: string - :keyword version: project version - :type version: string - - """ - - self.project = kwargs.pop('project', 'unknown') - self.version = kwargs.pop('version', 'unknown') - - logging.Formatter.__init__(self, *args, **kwargs) - - def format(self, record): - """Uses contextstring if request_id is set, otherwise default.""" - - # NOTE(jecarey): If msg is not unicode, coerce it into unicode - # before it can get to the python logging and - # possibly cause string encoding trouble - if not isinstance(record.msg, six.text_type): - record.msg = six.text_type(record.msg) - - # store project info - record.project = self.project - record.version = self.version - - # store request info - context = getattr(local.store, 'context', None) - if context: - d = _dictify_context(context) - for k, v in d.items(): - setattr(record, k, v) - - # NOTE(sdague): default the fancier formatting params - # to an empty string so we don't throw an exception if - # they get used - for key in ('instance', 'color', 'user_identity'): - if key not in record.__dict__: - record.__dict__[key] = '' - - if record.__dict__.get('request_id'): - fmt = CONF.logging_context_format_string - else: - fmt = CONF.logging_default_format_string - - if (record.levelno == logging.DEBUG and - CONF.logging_debug_format_suffix): - fmt += " " + CONF.logging_debug_format_suffix - - if sys.version_info < (3, 2): - self._fmt = fmt - else: - self._style = logging.PercentStyle(fmt) - self._fmt = self._style._fmt - # Cache this on the record, Logger will respect our formatted copy - if record.exc_info: - record.exc_text = self.formatException(record.exc_info, record) - return logging.Formatter.format(self, record) - - def formatException(self, exc_info, record=None): - """Format exception output with CONF.logging_exception_prefix.""" - if not record: - return logging.Formatter.formatException(self, exc_info) - - stringbuffer = moves.StringIO() - traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], - None, stringbuffer) - lines = stringbuffer.getvalue().split('\n') - stringbuffer.close() - - if CONF.logging_exception_prefix.find('%(asctime)') != -1: - record.asctime = self.formatTime(record, self.datefmt) - - formatted_lines = [] - for line in lines: - pl = CONF.logging_exception_prefix % record.__dict__ - fl = '%s%s' % (pl, line) - formatted_lines.append(fl) - return '\n'.join(formatted_lines) - - -class ColorHandler(logging.StreamHandler): - LEVEL_COLORS = { - logging.DEBUG: '\033[00;32m', # GREEN - logging.INFO: '\033[00;36m', # CYAN - logging.AUDIT: '\033[01;36m', # BOLD CYAN - logging.WARN: '\033[01;33m', # BOLD YELLOW - logging.ERROR: '\033[01;31m', # BOLD RED - logging.CRITICAL: '\033[01;31m', # BOLD RED - } - - def format(self, record): - record.color = self.LEVEL_COLORS[record.levelno] - return logging.StreamHandler.format(self, record) - - -class DeprecatedConfig(Exception): - message = _("Fatal call to deprecated config: %(msg)s") - - def __init__(self, msg): - super(Exception, self).__init__(self.message % dict(msg=msg)) diff --git a/openstack/common/loopingcall.py b/openstack/common/loopingcall.py index 2eaa51d6..ce115467 100644 --- a/openstack/common/loopingcall.py +++ b/openstack/common/loopingcall.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import sys import time @@ -22,7 +23,6 @@ from eventlet import event from eventlet import greenthread from openstack.common._i18n import _LE, _LW -from openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/openstack/common/periodic_task.py b/openstack/common/periodic_task.py index dbcad8a4..34a16383 100644 --- a/openstack/common/periodic_task.py +++ b/openstack/common/periodic_task.py @@ -12,6 +12,7 @@ # under the License. import copy +import logging import random import time @@ -19,7 +20,6 @@ from oslo.config import cfg import six from openstack.common._i18n import _, _LE, _LI -from openstack.common import log as logging periodic_opts = [ diff --git a/openstack/common/policy.py b/openstack/common/policy.py index 107a39cb..a33edfed 100644 --- a/openstack/common/policy.py +++ b/openstack/common/policy.py @@ -91,6 +91,7 @@ as it allows particular rules to be explicitly disabled. import abc import ast import copy +import logging import os import re @@ -102,7 +103,6 @@ import six.moves.urllib.request as urlrequest from openstack.common import fileutils from openstack.common._i18n import _, _LE, _LI -from openstack.common import log as logging policy_opts = [ diff --git a/openstack/common/quota.py b/openstack/common/quota.py index 86e3ca51..d4404662 100644 --- a/openstack/common/quota.py +++ b/openstack/common/quota.py @@ -18,6 +18,7 @@ import copy import datetime +import logging from oslo.config import cfg from oslo.utils import importutils @@ -25,7 +26,7 @@ from oslo.utils import timeutils import six from openstack.common._i18n import _, _LE -from openstack.common import log as logging +from openstack.common import versionutils LOG = logging.getLogger(__name__) @@ -193,7 +194,8 @@ class DbQuotaDriver(object): default_quotas = self.db.quota_class_get_default(context) for resource in resources.values(): if resource.name not in default_quotas: - LOG.deprecated( + versionutils.report_deprecated_feature( + LOG, "Default quota for resource: %(res)s is set " "by the default quota flag: quota_%(res)s, " "it is now deprecated. Please use the " diff --git a/openstack/common/scheduler/base_filter.py b/openstack/common/scheduler/base_filter.py index aee7c60a..193da286 100644 --- a/openstack/common/scheduler/base_filter.py +++ b/openstack/common/scheduler/base_filter.py @@ -16,9 +16,9 @@ """ Filter support """ +import logging from openstack.common._i18n import _LI -from openstack.common import log as logging from openstack.common.scheduler import base_handler LOG = logging.getLogger(__name__) diff --git a/openstack/common/scheduler/filters/capabilities_filter.py b/openstack/common/scheduler/filters/capabilities_filter.py index 011cc371..41df6fc3 100644 --- a/openstack/common/scheduler/filters/capabilities_filter.py +++ b/openstack/common/scheduler/filters/capabilities_filter.py @@ -13,9 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. +import logging + import six -from openstack.common import log as logging from openstack.common.scheduler import filters from openstack.common.scheduler.filters import extra_specs_ops diff --git a/openstack/common/scheduler/filters/ignore_attempted_hosts_filter.py b/openstack/common/scheduler/filters/ignore_attempted_hosts_filter.py index 0ac14d3f..a09a7e19 100644 --- a/openstack/common/scheduler/filters/ignore_attempted_hosts_filter.py +++ b/openstack/common/scheduler/filters/ignore_attempted_hosts_filter.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -from openstack.common import log as logging +import logging + from openstack.common.scheduler import filters LOG = logging.getLogger(__name__) diff --git a/openstack/common/service.py b/openstack/common/service.py index 579e5c95..c8d2d254 100644 --- a/openstack/common/service.py +++ b/openstack/common/service.py @@ -18,7 +18,7 @@ """Generic Node base class for all workers that run on hosts.""" import errno -import logging as std_logging +import logging import os import random import signal @@ -39,7 +39,6 @@ from oslo.config import cfg from openstack.common import eventlet_backdoor from openstack.common._i18n import _LE, _LI, _LW -from openstack.common import log as logging from openstack.common import systemd from openstack.common import threadgroup @@ -163,7 +162,7 @@ class ServiceLauncher(Launcher): signo = 0 LOG.debug('Full set of CONF:') - CONF.log_opt_values(LOG, std_logging.DEBUG) + CONF.log_opt_values(LOG, logging.DEBUG) try: if ready_callback: @@ -377,7 +376,7 @@ class ProcessLauncher(object): systemd.notify_once() LOG.debug('Full set of CONF:') - CONF.log_opt_values(LOG, std_logging.DEBUG) + CONF.log_opt_values(LOG, logging.DEBUG) try: while True: diff --git a/openstack/common/systemd.py b/openstack/common/systemd.py index bbb47dfe..36243b34 100644 --- a/openstack/common/systemd.py +++ b/openstack/common/systemd.py @@ -16,12 +16,11 @@ Helper module for systemd service readiness notification. """ +import logging import os import socket import sys -from openstack.common import log as logging - LOG = logging.getLogger(__name__) diff --git a/openstack/common/threadgroup.py b/openstack/common/threadgroup.py index 53940911..ef1207a8 100644 --- a/openstack/common/threadgroup.py +++ b/openstack/common/threadgroup.py @@ -11,12 +11,12 @@ # 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 logging import threading import eventlet from eventlet import greenpool -from openstack.common import log as logging from openstack.common import loopingcall diff --git a/openstack/common/versionutils.py b/openstack/common/versionutils.py index 13259a3b..2865b7db 100644 --- a/openstack/common/versionutils.py +++ b/openstack/common/versionutils.py @@ -19,15 +19,24 @@ Helpers for comparing version strings. import functools import inspect +import logging +from oslo.config import cfg import pkg_resources import six from openstack.common._i18n import _ -from openstack.common import log as logging LOG = logging.getLogger(__name__) +CONF = cfg.CONF + + +opts = [ + cfg.BoolOpt('fatal_deprecations', + default=False, + help='Enables or disables fatal status of deprecations.'), +] class deprecated(object): @@ -127,7 +136,7 @@ class deprecated(object): @six.wraps(func_or_cls) def wrapped(*args, **kwargs): - LOG.deprecated(msg, details) + report_deprecated_feature(LOG, msg, details) return func_or_cls(*args, **kwargs) return wrapped elif inspect.isclass(func_or_cls): @@ -139,7 +148,7 @@ class deprecated(object): # and added to the oslo-incubator requrements @functools.wraps(orig_init, assigned=('__name__', '__doc__')) def new_init(self, *args, **kwargs): - LOG.deprecated(msg, details) + report_deprecated_feature(LOG, msg, details) orig_init(self, *args, **kwargs) func_or_cls.__init__ = new_init return func_or_cls @@ -201,3 +210,44 @@ def is_compatible(requested_version, current_version, same_major=True): return False return current_parts >= requested_parts + + +# Track the messages we have sent already. See +# report_deprecated_feature(). +_deprecated_messages_sent = {} + + +def report_deprecated_feature(logger, msg, *args, **kwargs): + """Call this function when a deprecated feature is used. + + If the system is configured for fatal deprecations then the message + is logged at the 'critical' level and :class:`DeprecatedConfig` will + be raised. + + Otherwise, the message will be logged (once) at the 'warn' level. + + :raises: :class:`DeprecatedConfig` if the system is configured for + fatal deprecations. + """ + stdmsg = _("Deprecated: %s") % msg + CONF.register_opts(opts) + if CONF.fatal_deprecations: + logger.critical(stdmsg, *args, **kwargs) + raise DeprecatedConfig(msg=stdmsg) + + # Using a list because a tuple with dict can't be stored in a set. + sent_args = _deprecated_messages_sent.setdefault(msg, list()) + + if args in sent_args: + # Already logged this message, so don't log it again. + return + + sent_args.append(args) + logger.warn(stdmsg, *args, **kwargs) + + +class DeprecatedConfig(Exception): + message = _("Fatal call to deprecated config: %(msg)s") + + def __init__(self, msg): + super(Exception, self).__init__(self.message % dict(msg=msg)) diff --git a/test-requirements.txt b/test-requirements.txt index 640a5234..5392900e 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,6 @@ coverage>=3.6 discover -fixtures>=0.3.14 hacking>=0.10.0,<0.11 oslosphinx>=2.2.0 # Apache-2.0 oslotest>=1.2.0 # Apache-2.0 diff --git a/tests/unit/fixture/test_logging.py b/tests/unit/fixture/test_logging.py deleted file mode 100644 index 22dd72e2..00000000 --- a/tests/unit/fixture/test_logging.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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. - -from oslotest import base - -from openstack.common.fixture import logging as logging_fixture -from openstack.common import log as logging - -LOG = logging.getLogger(__name__) - - -class TestLoggingFixture(base.BaseTestCase): - def test_logging_handle_error(self): - LOG.info('pid of first child is %(foo)s', 1) - self.useFixture(logging_fixture.get_logging_handle_error_fixture()) - self.assertRaises(TypeError, - LOG.error, - 'pid of first child is %(foo)s', - 1) diff --git a/tests/unit/test_deprecated.py b/tests/unit/test_deprecated.py index a8767635..e29793d3 100644 --- a/tests/unit/test_deprecated.py +++ b/tests/unit/test_deprecated.py @@ -13,12 +13,15 @@ # License for the specific language governing permissions and limitations # under the License. +import logging + import mock from oslo.config import fixture as config from oslotest import base as test_base from oslotest import mockpatch -from openstack.common import log as logging +from openstack.common import versionutils + LOG = logging.getLogger(__name__) @@ -33,17 +36,21 @@ class DeprecatedConfigTestCase(test_base.BaseTestCase): crit_fixture = self.useFixture(mockpatch.PatchObject(LOG, 'critical')) self.crit_mock = crit_fixture.mock - self.config = self.useFixture(config.Config()).config + self.config_fixture = self.useFixture(config.Config()) + self.config_fixture.register_opts(versionutils.opts) + self.config = self.config_fixture.config def test_deprecated(self): - LOG.deprecated('test') + versionutils.report_deprecated_feature(LOG, 'test') self.warn_mock.assert_called_once_with('Deprecated: test') self.assertFalse(self.crit_mock.called) def test_deprecated_fatal(self): self.config(fatal_deprecations=True) - self.assertRaises(logging.DeprecatedConfig, - LOG.deprecated, "test2") + self.assertRaises(versionutils.DeprecatedConfig, + versionutils.report_deprecated_feature, + LOG, + "test2") self.crit_mock.assert_called_once_with('Deprecated: test2') self.assertFalse(self.warn_mock.called) @@ -51,9 +58,9 @@ class DeprecatedConfigTestCase(test_base.BaseTestCase): # If the same message is used multiple times then it's only logged # once. - LOG.deprecated('only once!') - LOG.deprecated('only once!') - LOG.deprecated('only once!') + versionutils.report_deprecated_feature(LOG, 'only once!') + versionutils.report_deprecated_feature(LOG, 'only once!') + versionutils.report_deprecated_feature(LOG, 'only once!') self.warn_mock.assert_called_once_with('Deprecated: only once!') @@ -62,12 +69,12 @@ class DeprecatedConfigTestCase(test_base.BaseTestCase): msg1 = 'tdlodm_message 1' msg2 = 'tdlodm_message 2' - LOG.deprecated(msg1) - LOG.deprecated(msg2) - LOG.deprecated(msg1) - LOG.deprecated(msg1) - LOG.deprecated(msg2) - LOG.deprecated(msg2) + versionutils.report_deprecated_feature(LOG, msg1) + versionutils.report_deprecated_feature(LOG, msg2) + versionutils.report_deprecated_feature(LOG, msg1) + versionutils.report_deprecated_feature(LOG, msg1) + versionutils.report_deprecated_feature(LOG, msg2) + versionutils.report_deprecated_feature(LOG, msg2) exp_calls = [ mock.call('Deprecated: tdlodm_message 1'), @@ -80,10 +87,10 @@ class DeprecatedConfigTestCase(test_base.BaseTestCase): # If the same message format with different arguments is used then each # set of message + argument is logged once (for a simple argument) - LOG.deprecated('only once! %s', 'arg1') - LOG.deprecated('only once! %s', 'arg1') - LOG.deprecated('only once! %s', 'arg2') - LOG.deprecated('only once! %s', 'arg2') + versionutils.report_deprecated_feature(LOG, 'only once! %s', 'arg1') + versionutils.report_deprecated_feature(LOG, 'only once! %s', 'arg1') + versionutils.report_deprecated_feature(LOG, 'only once! %s', 'arg2') + versionutils.report_deprecated_feature(LOG, 'only once! %s', 'arg2') exp_calls = [ mock.call('Deprecated: only once! %s', 'arg1'), @@ -105,15 +112,25 @@ class DeprecatedConfigTestCase(test_base.BaseTestCase): msg_fmt_2_arg_2 = 6, None, 'purple' msg_fmt_2_arg_3 = 6, None, 'something' # same first args. - LOG.deprecated(msg_fmt_1, msg_fmt_1_arg_1) - LOG.deprecated(msg_fmt_1, msg_fmt_1_arg_2) # logged: args different - LOG.deprecated(msg_fmt_1, msg_fmt_1_arg_1) # no log: same msg+args - - LOG.deprecated(msg_fmt_2, msg_fmt_2_arg_1) - LOG.deprecated(msg_fmt_2, *msg_fmt_2_arg_2) # logged: args different - LOG.deprecated(msg_fmt_2, *msg_fmt_2_arg_3) # logged: args different - LOG.deprecated(msg_fmt_2, *msg_fmt_2_arg_3) # no log: same msg+args - LOG.deprecated(msg_fmt_2, *msg_fmt_2_arg_2) # no log: same msg+args + versionutils.report_deprecated_feature(LOG, msg_fmt_1, msg_fmt_1_arg_1) + # logged: args different + versionutils.report_deprecated_feature(LOG, msg_fmt_1, msg_fmt_1_arg_2) + # no log: same msg+args + versionutils.report_deprecated_feature(LOG, msg_fmt_1, msg_fmt_1_arg_1) + + versionutils.report_deprecated_feature(LOG, msg_fmt_2, msg_fmt_2_arg_1) + # logged: args different + versionutils.report_deprecated_feature(LOG, msg_fmt_2, + *msg_fmt_2_arg_2) + # logged: args different + versionutils.report_deprecated_feature(LOG, msg_fmt_2, + *msg_fmt_2_arg_3) + # no log: same msg+args + versionutils.report_deprecated_feature(LOG, msg_fmt_2, + *msg_fmt_2_arg_3) + # no log: same msg+args + versionutils.report_deprecated_feature(LOG, msg_fmt_2, + *msg_fmt_2_arg_2) exp_calls = [ mock.call('Deprecated: %s' % msg_fmt_1, msg_fmt_1_arg_1), diff --git a/tests/unit/test_log.py b/tests/unit/test_log.py deleted file mode 100644 index d910d28e..00000000 --- a/tests/unit/test_log.py +++ /dev/null @@ -1,681 +0,0 @@ -# Copyright (c) 2011 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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 logging -import os -import sys -import tempfile - -import mock -from oslo.config import cfg -from oslo.config import fixture as config -from oslo_context import context -from oslo_serialization import jsonutils -from oslotest import base as test_base -import six - -from openstack.common import fileutils -from openstack.common import local -from openstack.common import log - - -def _fake_context(): - return context.RequestContext(1, 1) - - -class CommonLoggerTestsMixIn(object): - """These tests are shared between LoggerTestCase and - LazyLoggerTestCase. - """ - - def setUp(self): - super(CommonLoggerTestsMixIn, self).setUp() - self.config = self.useFixture(config.Config()).config - - # common context has different fields to the defaults in log.py - self.config(logging_context_format_string='%(asctime)s %(levelname)s ' - '%(name)s [%(request_id)s ' - '%(user)s %(tenant)s] ' - '%(message)s') - self.log = None - log._setup_logging_from_conf('test', 'test') - - def test_handlers_have_context_formatter(self): - formatters = [] - for h in self.log.logger.handlers: - f = h.formatter - if isinstance(f, log.ContextFormatter): - formatters.append(f) - self.assertTrue(formatters) - self.assertEqual(len(formatters), len(self.log.logger.handlers)) - - def test_handles_context_kwarg(self): - self.log.info("foo", context=_fake_context()) - self.assertTrue(True) # didn't raise exception - - def test_audit_handles_context_arg(self): - self.log.audit("foo", context=_fake_context()) - self.assertTrue(True) # didn't raise exception - - def test_will_be_verbose_if_verbose_flag_set(self): - self.config(verbose=True) - log.setup("test_is_verbose") - logger = logging.getLogger("test_is_verbose") - self.assertEqual(logging.INFO, logger.getEffectiveLevel()) - - def test_will_be_debug_if_debug_flag_set(self): - self.config(debug=True) - log.setup("test_is_debug") - logger = logging.getLogger("test_is_debug") - self.assertEqual(logging.DEBUG, logger.getEffectiveLevel()) - - def test_will_not_be_verbose_if_verbose_flag_not_set(self): - self.config(verbose=False) - log.setup("test_is_not_verbose") - logger = logging.getLogger("test_is_not_verbose") - self.assertEqual(logging.WARNING, logger.getEffectiveLevel()) - - def test_no_logging_via_module(self): - for func in ('critical', 'error', 'exception', 'warning', 'warn', - 'info', 'debug', 'log', 'audit'): - self.assertRaises(AttributeError, getattr, log, func) - - -class LoggerTestCase(CommonLoggerTestsMixIn, test_base.BaseTestCase): - def setUp(self): - super(LoggerTestCase, self).setUp() - self.log = log.getLogger(None) - - -class LazyLoggerTestCase(CommonLoggerTestsMixIn, test_base.BaseTestCase): - def setUp(self): - super(LazyLoggerTestCase, self).setUp() - self.log = log.getLazyLogger(None) - - -class LogTestBase(test_base.BaseTestCase): - """Base test class that provides some convenience functions.""" - def _add_handler_with_cleanup(self, log_instance, handler=None, - formatter=None): - """Add a log handler to a log instance. - - This function should be used to add handlers to loggers in test cases - instead of directly adding them to ensure that the handler is - correctly removed at the end of the test. Otherwise the handler may - be left on the logger and interfere with subsequent tests. - - :param log_instance: The log instance to which the handler will be - added. - :param handler: The handler class to be added. Must be the class - itself, not an instance. - :param formatter: The formatter class to set on the handler. Must be - the class itself, not an instance. - """ - self.stream = six.StringIO() - if handler is None: - handler = logging.StreamHandler - self.handler = handler(self.stream) - if formatter is None: - formatter = log.ContextFormatter - self.handler.setFormatter(formatter()) - log_instance.logger.addHandler(self.handler) - self.addCleanup(log_instance.logger.removeHandler, self.handler) - - def _set_log_level_with_cleanup(self, log_instance, level): - """Set the log level of a logger for the duration of a test. - - Use this function to set the log level of a logger and add the - necessary cleanup to reset it back to default at the end of the test. - - :param log_instance: The logger whose level will be changed. - :param level: The new log level to use. - """ - self.level = log_instance.logger.getEffectiveLevel() - log_instance.logger.setLevel(level) - self.addCleanup(log_instance.logger.setLevel, self.level) - - -class LogHandlerTestCase(test_base.BaseTestCase): - - def setUp(self): - super(LogHandlerTestCase, self).setUp() - self.config = self.useFixture(config.Config()).config - - def test_log_path_logdir(self): - self.config(log_dir='/some/path', log_file=None) - self.assertEqual(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - def test_log_path_logfile(self): - self.config(log_file='/some/path/foo-bar.log') - self.assertEqual(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - def test_log_path_none(self): - self.config(log_dir=None, log_file=None) - self.assertIsNone(log._get_log_file_path(binary='foo-bar')) - - def test_log_path_logfile_overrides_logdir(self): - self.config(log_dir='/some/other/path', - log_file='/some/path/foo-bar.log') - self.assertEqual(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - -class SysLogHandlersTestCase(test_base.BaseTestCase): - """Test for standard and RFC compliant Syslog handlers.""" - def setUp(self): - super(SysLogHandlersTestCase, self).setUp() - self.facility = logging.handlers.SysLogHandler.LOG_USER - self.rfclogger = log.RFCSysLogHandler(facility=self.facility) - self.rfclogger.binary_name = 'Foo_application' - self.logger = logging.handlers.SysLogHandler(facility=self.facility) - self.logger.binary_name = 'Foo_application' - - def test_rfc_format(self): - """Ensure syslog msg contains APP-NAME for RFC wrapped handler.""" - logrecord = logging.LogRecord('name', 'WARN', '/tmp', 1, - 'Message', None, None) - expected = logging.LogRecord('name', 'WARN', '/tmp', 1, - 'Foo_application Message', None, None) - self.assertEqual(self.rfclogger.format(logrecord), - expected.getMessage()) - - def test_standard_format(self): - """Ensure syslog msg isn't modified for standard handler.""" - logrecord = logging.LogRecord('name', 'WARN', '/tmp', 1, - 'Message', None, None) - expected = logrecord - self.assertEqual(self.logger.format(logrecord), - expected.getMessage()) - - -class LogLevelTestCase(test_base.BaseTestCase): - def setUp(self): - super(LogLevelTestCase, self).setUp() - self.CONF = self.useFixture(config.Config()).conf - levels = self.CONF.default_log_levels - levels.append("nova-test=AUDIT") - levels.append("nova-not-debug=WARN") - self.config = self.useFixture(config.Config()).config - self.config(default_log_levels=levels, - verbose=True) - log.setup('testing') - self.log = log.getLogger('nova-test') - self.log_no_debug = log.getLogger('nova-not-debug') - - def test_is_enabled_for(self): - self.assertTrue(self.log.isEnabledFor(logging.AUDIT)) - self.assertFalse(self.log_no_debug.isEnabledFor(logging.DEBUG)) - - def test_has_level_from_flags(self): - self.assertEqual(logging.AUDIT, self.log.logger.getEffectiveLevel()) - - def test_child_log_has_level_of_parent_flag(self): - l = log.getLogger('nova-test.foo') - self.assertEqual(logging.AUDIT, l.logger.getEffectiveLevel()) - - -class JSONFormatterTestCase(LogTestBase): - def setUp(self): - super(JSONFormatterTestCase, self).setUp() - self.log = log.getLogger('test-json') - self._add_handler_with_cleanup(self.log, formatter=log.JSONFormatter) - self._set_log_level_with_cleanup(self.log, logging.DEBUG) - - def test_json(self): - test_msg = 'This is a %(test)s line' - test_data = {'test': 'log'} - self.log.debug(test_msg, test_data) - - data = jsonutils.loads(self.stream.getvalue()) - self.assertTrue(data) - self.assertTrue('extra' in data) - self.assertEqual('test-json', data['name']) - - self.assertEqual(test_msg % test_data, data['message']) - self.assertEqual(test_msg, data['msg']) - self.assertEqual(test_data, data['args']) - - self.assertEqual('test_log.py', data['filename']) - self.assertEqual('test_json', data['funcname']) - - self.assertEqual('DEBUG', data['levelname']) - self.assertEqual(logging.DEBUG, data['levelno']) - self.assertFalse(data['traceback']) - - def test_json_exception(self): - test_msg = 'This is %s' - test_data = 'exceptional' - try: - raise Exception('This is exceptional') - except Exception: - self.log.exception(test_msg, test_data) - - data = jsonutils.loads(self.stream.getvalue()) - self.assertTrue(data) - self.assertTrue('extra' in data) - self.assertEqual('test-json', data['name']) - - self.assertEqual(test_msg % test_data, data['message']) - self.assertEqual(test_msg, data['msg']) - self.assertEqual([test_data], data['args']) - - self.assertEqual('ERROR', data['levelname']) - self.assertEqual(logging.ERROR, data['levelno']) - self.assertTrue(data['traceback']) - - -class ContextFormatterTestCase(LogTestBase): - def setUp(self): - super(ContextFormatterTestCase, self).setUp() - self.config = self.useFixture(config.Config()).config - self.config(logging_context_format_string="HAS CONTEXT " - "[%(request_id)s]: " - "%(message)s", - logging_default_format_string="NOCTXT: %(message)s", - logging_debug_format_suffix="--DBG") - self.log = log.getLogger('') # obtain root logger instead of 'unknown' - self._add_handler_with_cleanup(self.log) - self._set_log_level_with_cleanup(self.log, logging.DEBUG) - - def test_uncontextualized_log(self): - self.log.info("foo") - self.assertEqual("NOCTXT: foo\n", self.stream.getvalue()) - - def test_contextualized_log(self): - ctxt = _fake_context() - self.log.info("bar", context=ctxt) - expected = "HAS CONTEXT [%s]: bar\n" % ctxt.request_id - self.assertEqual(expected, self.stream.getvalue()) - - def test_context_is_taken_from_tls_variable(self): - ctxt = _fake_context() - local.store.context = ctxt - try: - self.log.info("bar") - expected = "HAS CONTEXT [%s]: bar\n" % ctxt.request_id - self.assertEqual(expected, self.stream.getvalue()) - finally: - del local.store.context - - def test_contextual_information_is_imparted_to_3rd_party_log_records(self): - ctxt = _fake_context() - local.store.context = ctxt - try: - sa_log = logging.getLogger('sqlalchemy.engine') - sa_log.setLevel(logging.INFO) - sa_log.info('emulate logging within sqlalchemy') - - expected = ("HAS CONTEXT [%s]: emulate logging within " - "sqlalchemy\n" % ctxt.request_id) - self.assertEqual(expected, self.stream.getvalue()) - finally: - del local.store.context - - # def test_message_logging_3rd_party_log_records(self): - # ctxt = _fake_context() - # local.store.context = ctxt - # local.store.context.request_id = six.text_type('99') - # try: - # sa_log = logging.getLogger('sqlalchemy.engine') - # sa_log.setLevel(logging.INFO) - # message = gettextutils.Message('test ' + six.unichr(128)) - # sa_log.info(message) - - # expected = ("HAS CONTEXT [%s]: %s\n" % (ctxt.request_id, - # six.text_type(message))) - # self.assertEqual(expected, self.stream.getvalue()) - # finally: - # del local.store.context - - def test_debugging_log(self): - self.log.debug("baz") - self.assertEqual("NOCTXT: baz --DBG\n", self.stream.getvalue()) - - # def test_message_logging(self): - # # NOTE(luisg): Logging message objects with unicode objects - # # may cause trouble by the logging mechanism trying to coerce - # # the Message object, with a wrong encoding. This test case - # # tests that problem does not occur. - # ctxt = _fake_context() - # ctxt.request_id = six.text_type('99') - # message = gettextutils.Message('test ' + six.unichr(128)) - # self.log.info(message, context=ctxt) - # expected = "HAS CONTEXT [%s]: %s\n" % (ctxt.request_id, - # six.text_type(message)) - # self.assertEqual(expected, self.stream.getvalue()) - - # def test_unicode_conversion_in_adapter(self): - # ctxt = _fake_context() - # ctxt.request_id = six.text_type('99') - # message = "Exception is (%s)" - # ex = Exception(gettextutils.Message('test' + six.unichr(128))) - # self.log.debug(message, ex, context=ctxt) - # message = six.text_type(message) % ex - # expected = "HAS CONTEXT [%s]: %s --DBG\n" % (ctxt.request_id, - # message) - # self.assertEqual(expected, self.stream.getvalue()) - - # def test_unicode_conversion_in_formatter(self): - # ctxt = _fake_context() - # local.store.context = ctxt - # ctxt.request_id = six.text_type('99') - # try: - # no_adapt_log = logging.getLogger('no_adapt') - # no_adapt_log.setLevel(logging.INFO) - # message = "Exception is (%s)" - # ex = Exception(gettextutils.Message('test' + six.unichr(128))) - # no_adapt_log.info(message, ex) - # message = six.text_type(message) % ex - # expected = "HAS CONTEXT [%s]: %s\n" % (ctxt.request_id, - # message) - # self.assertEqual(expected, self.stream.getvalue()) - # finally: - # del local.store.context - - -class ExceptionLoggingTestCase(LogTestBase): - """Test that Exceptions are logged.""" - - def test_excepthook_logs_exception(self): - product_name = 'somename' - exc_log = log.getLogger(product_name) - - self._add_handler_with_cleanup(exc_log) - excepthook = log._create_logging_excepthook(product_name) - - try: - raise Exception('Some error happened') - except Exception: - excepthook(*sys.exc_info()) - - expected_string = ("CRITICAL somename [-] " - "Exception: Some error happened") - self.assertTrue(expected_string in self.stream.getvalue(), - msg="Exception is not logged") - - def test_excepthook_installed(self): - log.setup("test_excepthook_installed") - self.assertTrue(sys.excepthook != sys.__excepthook__) - - -class FancyRecordTestCase(LogTestBase): - """Test how we handle fancy record keys that are not in the - base python logging. - """ - - def setUp(self): - super(FancyRecordTestCase, self).setUp() - self.config = self.useFixture(config.Config()).config - # NOTE(sdague): use the different formatters to demonstrate format - # string with valid fancy keys and without. Slightly hacky, but given - # the way log objects layer up seemed to be most concise approach - self.config(logging_context_format_string="%(color)s " - "[%(request_id)s]: " - "%(instance)s" - "%(message)s", - logging_default_format_string="%(missing)s: %(message)s") - self.colorlog = log.getLogger() - self._add_handler_with_cleanup(self.colorlog, log.ColorHandler) - self._set_log_level_with_cleanup(self.colorlog, logging.DEBUG) - - def test_unsupported_key_in_log_msg(self): - # NOTE(sdague): exception logging bypasses the main stream - # and goes to stderr. Suggests on a better way to do this are - # welcomed. - error = sys.stderr - sys.stderr = six.StringIO() - - self.colorlog.info("foo") - self.assertNotEqual(sys.stderr.getvalue().find("KeyError: 'missing'"), - -1) - - sys.stderr = error - - def _validate_keys(self, ctxt, keyed_log_string): - infocolor = '\033[00;36m' - warncolor = '\033[01;33m' - infoexpected = "%s %s info\n" % (infocolor, keyed_log_string) - warnexpected = "%s %s warn\n" % (warncolor, keyed_log_string) - - self.colorlog.info("info", context=ctxt) - self.assertEqual(infoexpected, self.stream.getvalue()) - - self.colorlog.warn("warn", context=ctxt) - self.assertEqual(infoexpected + warnexpected, self.stream.getvalue()) - - def test_fancy_key_in_log_msg(self): - ctxt = _fake_context() - self._validate_keys(ctxt, '[%s]:' % ctxt.request_id) - - -class DomainTestCase(LogTestBase): - def setUp(self): - super(DomainTestCase, self).setUp() - self.config = self.useFixture(config.Config()).config - self.config(logging_context_format_string="[%(request_id)s]: " - "%(user_identity)s " - "%(message)s") - self.mylog = log.getLogger() - self._add_handler_with_cleanup(self.mylog) - self._set_log_level_with_cleanup(self.mylog, logging.DEBUG) - - def _validate_keys(self, ctxt, keyed_log_string): - infoexpected = "%s info\n" % (keyed_log_string) - warnexpected = "%s warn\n" % (keyed_log_string) - - self.mylog.info("info", context=ctxt) - self.assertEqual(infoexpected, self.stream.getvalue()) - - self.mylog.warn("warn", context=ctxt) - self.assertEqual(infoexpected + warnexpected, self.stream.getvalue()) - - def test_domain_in_log_msg(self): - ctxt = _fake_context() - ctxt.domain = 'mydomain' - ctxt.project_domain = 'myprojectdomain' - ctxt.user_domain = 'myuserdomain' - user_identity = ctxt.to_dict()['user_identity'] - self.assertTrue(ctxt.domain in user_identity) - self.assertTrue(ctxt.project_domain in user_identity) - self.assertTrue(ctxt.user_domain in user_identity) - self._validate_keys(ctxt, ('[%s]: %s' % - (ctxt.request_id, user_identity))) - - -class SetDefaultsTestCase(test_base.BaseTestCase): - class TestConfigOpts(cfg.ConfigOpts): - def __call__(self, args=None): - return cfg.ConfigOpts.__call__(self, - args=args, - prog='test', - version='1.0', - usage='%(prog)s FOO BAR', - default_config_files=[]) - - def setUp(self): - super(SetDefaultsTestCase, self).setUp() - self.conf = self.TestConfigOpts() - self.conf.register_opts(log.log_opts) - - self._orig_defaults = dict([(o.dest, o.default) - for o in log.log_opts]) - self.addCleanup(self._restore_log_defaults) - - def _restore_log_defaults(self): - for opt in log.log_opts: - opt.default = self._orig_defaults[opt.dest] - - def test_default_log_level_to_none(self): - log.set_defaults(logging_context_format_string=None, - default_log_levels=None) - self.conf([]) - self.assertEqual(log.DEFAULT_LOG_LEVELS, self.conf.default_log_levels) - - def test_change_default(self): - my_default = '%(asctime)s %(levelname)s %(name)s [%(request_id)s '\ - '%(user_id)s %(project)s] %(instance)s'\ - '%(message)s' - log.set_defaults(logging_context_format_string=my_default) - self.conf([]) - self.assertEqual(self.conf.logging_context_format_string, my_default) - - def test_change_default_log_level(self): - log.set_defaults(default_log_levels=['foo=bar']) - self.conf([]) - self.assertEqual(['foo=bar'], self.conf.default_log_levels) - self.assertIsNotNone(self.conf.logging_context_format_string) - - -class LogConfigOptsTestCase(test_base.BaseTestCase): - - def setUp(self): - super(LogConfigOptsTestCase, self).setUp() - self.CONF = self.useFixture(config.Config()).conf - - def test_print_help(self): - f = six.StringIO() - self.CONF([]) - self.CONF.print_help(file=f) - self.assertTrue('debug' in f.getvalue()) - self.assertTrue('verbose' in f.getvalue()) - self.assertTrue('log-config' in f.getvalue()) - self.assertTrue('log-format' in f.getvalue()) - - def test_debug_verbose(self): - self.CONF(['--debug', '--verbose']) - - self.assertEqual(self.CONF.debug, True) - self.assertEqual(self.CONF.verbose, True) - - def test_logging_opts(self): - self.CONF([]) - - self.assertIsNone(self.CONF.log_config_append) - self.assertIsNone(self.CONF.log_file) - self.assertIsNone(self.CONF.log_dir) - self.assertIsNone(self.CONF.log_format) - - self.assertEqual(self.CONF.log_date_format, - log._DEFAULT_LOG_DATE_FORMAT) - - self.assertEqual(self.CONF.use_syslog, False) - self.assertEqual(self.CONF.use_syslog_rfc_format, False) - - def test_log_file(self): - log_file = '/some/path/foo-bar.log' - self.CONF(['--log-file', log_file]) - self.assertEqual(self.CONF.log_file, log_file) - - def test_log_dir_handlers(self): - log_dir = tempfile.mkdtemp() - self.CONF(['--log-dir', log_dir]) - self.CONF.set_default('use_stderr', False) - log._setup_logging_from_conf('test', 'test') - logger = log._loggers[None].logger - self.assertEqual(1, len(logger.handlers)) - self.assertIsInstance(logger.handlers[0], - logging.handlers.WatchedFileHandler) - - def test_logfile_deprecated(self): - logfile = '/some/other/path/foo-bar.log' - self.CONF(['--logfile', logfile]) - self.assertEqual(self.CONF.log_file, logfile) - - def test_log_dir(self): - log_dir = '/some/path/' - self.CONF(['--log-dir', log_dir]) - self.assertEqual(self.CONF.log_dir, log_dir) - - def test_logdir_deprecated(self): - logdir = '/some/other/path/' - self.CONF(['--logdir', logdir]) - self.assertEqual(self.CONF.log_dir, logdir) - - def test_log_format_overrides_formatter(self): - self.CONF(['--log-format', '[Any format]']) - log._setup_logging_from_conf('test', 'test') - logger = log._loggers[None].logger - for handler in logger.handlers: - formatter = handler.formatter - self.assertTrue(isinstance(formatter, logging.Formatter)) - - def test_default_formatter(self): - log._setup_logging_from_conf('test', 'test') - logger = log._loggers[None].logger - for handler in logger.handlers: - formatter = handler.formatter - self.assertTrue(isinstance(formatter, log.ContextFormatter)) - - -class LogConfigTestCase(test_base.BaseTestCase): - - minimal_config = b"""[loggers] -keys=root - -[formatters] -keys= - -[handlers] -keys= - -[logger_root] -handlers= -""" - - def setUp(self): - super(LogConfigTestCase, self).setUp() - self.config = self.useFixture(config.Config()).config - self.log_config_append = \ - fileutils.write_to_tempfile(content=self.minimal_config, - prefix='logging', - suffix='.conf' - ) - - def test_log_config_append_ok(self): - self.config(log_config_append=self.log_config_append) - log.setup('test_log_config_append') - - def test_log_config_append_not_exist(self): - os.remove(self.log_config_append) - self.config(log_config_append=self.log_config_append) - self.assertRaises(log.LogConfigError, log.setup, - 'test_log_config_append') - - def test_log_config_append_invalid(self): - self.log_config_append = \ - fileutils.write_to_tempfile(content=self.minimal_config[5:], - prefix='logging', - suffix='.conf' - ) - self.config(log_config_append=self.log_config_append) - self.assertRaises(log.LogConfigError, log.setup, - 'test_log_config_append') - - def test_log_config_append_unreadable(self): - os.chmod(self.log_config_append, 0) - self.config(log_config_append=self.log_config_append) - self.assertRaises(log.LogConfigError, log.setup, - 'test_log_config_append') - - def test_log_config_append_disable_existing_loggers(self): - self.config(log_config_append=self.log_config_append) - with mock.patch('logging.config.fileConfig') as fileConfig: - log.setup('test_log_config_append') - - fileConfig.assert_called_once_with(self.log_config_append, - disable_existing_loggers=False) diff --git a/tests/unit/test_service.py b/tests/unit/test_service.py index 9c8f83b1..a81f5819 100644 --- a/tests/unit/test_service.py +++ b/tests/unit/test_service.py @@ -21,6 +21,7 @@ Unit Tests for service class from __future__ import print_function import errno +import logging import multiprocessing import os import signal @@ -37,7 +38,6 @@ from oslotest import base as test_base from oslotest import moxstubout from openstack.common import eventlet_backdoor -from openstack.common import log as logging from openstack.common import service diff --git a/tests/unit/test_versionutils.py b/tests/unit/test_versionutils.py index 00481690..4cb3aabb 100644 --- a/tests/unit/test_versionutils.py +++ b/tests/unit/test_versionutils.py @@ -21,7 +21,7 @@ from openstack.common import versionutils class DeprecatedTestCase(test_base.BaseTestCase): - def assert_deprecated(self, mock_log, no_removal=False, + def assert_deprecated(self, mock_reporter, no_removal=False, **expected_details): decorator = versionutils.deprecated if 'in_favor_of' in expected_details: @@ -38,10 +38,14 @@ class DeprecatedTestCase(test_base.BaseTestCase): expected_msg = getattr( decorator, '_deprecated_msg_with_no_alternative_no_removal') - mock_log.deprecated.assert_called_with(expected_msg, expected_details) + # The first argument is the logger, and we don't care about + # that, so ignore it with ANY. + mock_reporter.assert_called_with(mock.ANY, + expected_msg, + expected_details) - @mock.patch('openstack.common.versionutils.LOG', mock.Mock()) - def test_deprecating_a_function_returns_correct_value(self): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecating_a_function_returns_correct_value(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.ICEHOUSE) def do_outdated_stuff(data): @@ -52,8 +56,8 @@ class DeprecatedTestCase(test_base.BaseTestCase): self.assertThat(retval, matchers.Equals(expected_rv)) - @mock.patch('openstack.common.versionutils.LOG', mock.Mock()) - def test_deprecating_a_method_returns_correct_value(self): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecating_a_method_returns_correct_value(self, mock_reporter): class C(object): @versionutils.deprecated(as_of=versionutils.deprecated.ICEHOUSE) @@ -64,8 +68,8 @@ class DeprecatedTestCase(test_base.BaseTestCase): self.assertThat(retval, matchers.Equals((1, 'of anything'))) - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_unknown_future_release(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_unknown_future_release(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.BEXAR, in_favor_of='different_stuff()') @@ -74,14 +78,14 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='do_outdated_stuff()', in_favor_of='different_stuff()', as_of='Bexar', remove_in='D') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_known_future_release(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_known_future_release(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, in_favor_of='different_stuff()') @@ -90,14 +94,14 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='do_outdated_stuff()', in_favor_of='different_stuff()', as_of='Grizzly', remove_in='Icehouse') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_without_replacement(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_without_replacement(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY) def do_outdated_stuff(): @@ -105,13 +109,13 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='do_outdated_stuff()', as_of='Grizzly', remove_in='Icehouse') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_custom_what(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_custom_what(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, what='v2.0 API', @@ -121,14 +125,14 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='v2.0 API', in_favor_of='v3 API', as_of='Grizzly', remove_in='Icehouse') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_removed_next_release(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_removed_next_release(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, remove_in=1) @@ -137,13 +141,13 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='do_outdated_stuff()', as_of='Grizzly', remove_in='Havana') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_removed_plus_3(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_removed_plus_3(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, remove_in=+3) @@ -152,27 +156,27 @@ class DeprecatedTestCase(test_base.BaseTestCase): do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='do_outdated_stuff()', as_of='Grizzly', remove_in='Juno') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_removed_zero(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_removed_zero(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, remove_in=0) def do_outdated_stuff(): return do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, no_removal=True, what='do_outdated_stuff()', as_of='Grizzly', remove_in='Grizzly') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_with_removed_zero_and_alternative(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_with_removed_zero_and_alternative(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY, in_favor_of='different_stuff()', remove_in=0) @@ -180,15 +184,15 @@ class DeprecatedTestCase(test_base.BaseTestCase): return do_outdated_stuff() - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, no_removal=True, what='do_outdated_stuff()', as_of='Grizzly', in_favor_of='different_stuff()', remove_in='Grizzly') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_class_without_init(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_class_without_init(self, mock_reporter): @versionutils.deprecated(as_of=versionutils.deprecated.JUNO, remove_in=+1) @@ -197,13 +201,13 @@ class DeprecatedTestCase(test_base.BaseTestCase): obj = OutdatedClass() self.assertIsInstance(obj, OutdatedClass) - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='OutdatedClass()', as_of='Juno', remove_in='Kilo') - @mock.patch('openstack.common.versionutils.LOG') - def test_deprecated_class_with_init(self, mock_log): + @mock.patch('openstack.common.versionutils.report_deprecated_feature') + def test_deprecated_class_with_init(self, mock_reporter): mock_arguments = mock.MagicMock() args = (1, 5, 7) kwargs = {'first': 10, 'second': 20} @@ -223,7 +227,7 @@ class DeprecatedTestCase(test_base.BaseTestCase): self.assertEqual('It is __init__ method.', obj.__init__.__doc__) self.assertEqual(args, mock_arguments.args) self.assertEqual(kwargs, mock_arguments.kwargs) - self.assert_deprecated(mock_log, + self.assert_deprecated(mock_reporter, what='OutdatedClass()', as_of='Juno', remove_in='Kilo') @@ -64,7 +64,6 @@ commands = tests.unit.test_fileutils \ tests.unit.test_imageutils \ tests.unit.test_local \ - tests.unit.test_log \ tests.unit.test_memorycache \ tests.unit.test_periodic \ tests.unit.test_policy \ @@ -97,7 +96,6 @@ commands = tests.unit.test_fileutils \ tests.unit.test_imageutils \ tests.unit.test_local \ - tests.unit.test_log \ tests.unit.test_memorycache \ tests.unit.test_periodic \ tests.unit.test_policy \ |