Volans has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/386399 )

Change subject: Logging: uniform loggers
......................................................................

Logging: uniform loggers

* Use proper hierarchical loggers across the project
* [Breaking API change] Remove optional parameter logger from all
  classes where it was accepted, the classes instantiate the proper
  logger based on the current module and class name.
* For classes inherited from a base abstract class, the logger is
  defined only in the base abstract class, with the name of the concrete
  class that is calling it.
* Changed CLI logging format to take advantage of the hirarchical
  logging.

Bug: T179002
Change-Id: I44eaed95ae4e7d12f16a03029e7c753bbefccb71
---
M cumin/backends/__init__.py
M cumin/backends/openstack.py
M cumin/backends/puppetdb.py
M cumin/cli.py
M cumin/query.py
M cumin/tests/unit/backends/external/__init__.py
M cumin/tests/unit/test_cli.py
M cumin/transport.py
M cumin/transports/__init__.py
M cumin/transports/clustershell.py
10 files changed, 46 insertions(+), 50 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/software/cumin 
refs/changes/99/386399/1

diff --git a/cumin/backends/__init__.py b/cumin/backends/__init__.py
index 19a6c0e..9961f1c 100644
--- a/cumin/backends/__init__.py
+++ b/cumin/backends/__init__.py
@@ -27,15 +27,14 @@
     """:py:class:`pyparsing.ParserElement`: derived classes must define their 
own pyparsing grammar and set this class
     attribute accordingly."""
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Query constructor.
 
         Arguments:
             config (dict): a dictionary with the parsed configuration file.
-            logger (logging.Logger, optional): an optional logger instance.
         """
         self.config = config
-        self.logger = logger or logging.getLogger(__name__)
+        self.logger = logging.getLogger('.'.join((self.__module__, 
self.__class__.__name__)))
         self.logger.trace('Backend {name} created with config: 
{config}'.format(
             name=type(self).__name__, config=config))
 
@@ -92,13 +91,13 @@
     operators.
     """
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Query aggregator constructor, initialize the stack.
 
         :Parameters:
             according to parent :py:meth:`cumin.backends.BaseQuery.__init__`.
         """
-        super(BaseQueryAggregator, self).__init__(config, logger=logger)
+        super(BaseQueryAggregator, self).__init__(config)
 
         self.stack = None
         self.stack_pointer = None
diff --git a/cumin/backends/openstack.py b/cumin/backends/openstack.py
index b2e1b71..56e0235 100644
--- a/cumin/backends/openstack.py
+++ b/cumin/backends/openstack.py
@@ -100,14 +100,14 @@
     grammar = grammar()
     """:py:class:`pyparsing.ParserElement`: load the grammar parser only once 
in a singleton-like way."""
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Override parent class constructor for specific setup.
 
         :Parameters:
             according to parent :py:meth:`cumin.backends.BaseQuery.__init__`.
 
         """
-        super(OpenStackQuery, self).__init__(config, logger=logger)
+        super(OpenStackQuery, self).__init__(config)
         self.openstack_config = self.config.get('openstack', {})
         self.search_project = None
         self.search_params = self._get_default_search_params()
diff --git a/cumin/backends/puppetdb.py b/cumin/backends/puppetdb.py
index 7d3168a..1e76a9b 100644
--- a/cumin/backends/puppetdb.py
+++ b/cumin/backends/puppetdb.py
@@ -140,14 +140,14 @@
     grammar = grammar()
     """:py:class:`pyparsing.ParserElement`: load the grammar parser only once 
in a singleton-like way."""
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Query constructor for the PuppetDB backend.
 
         :Parameters:
             according to parent :py:meth:`cumin.backends.BaseQuery.__init__`.
 
         """
-        super(PuppetDBQuery, self).__init__(config, logger=logger)
+        super(PuppetDBQuery, self).__init__(config)
         self.grouped_tokens = None
         self.current_group = self.grouped_tokens
         self._endpoint = None
diff --git a/cumin/cli.py b/cumin/cli.py
index 84f70e2..cb75b5d 100644
--- a/cumin/cli.py
+++ b/cumin/cli.py
@@ -188,19 +188,20 @@
     if not os.path.exists(file_path):
         os.makedirs(file_path, 0770)
 
-    log_formatter = logging.Formatter(
-        fmt='%(asctime)s [%(process)s] (%(levelname)s %(filename)s:%(lineno)s 
in %(funcName)s) %(message)s')
+    log_formatter = logging.Formatter(fmt='%(asctime)s [%(levelname)s 
%(process)s %(name)s.%(funcName)s] %(message)s')
     log_handler = RotatingFileHandler(filename, maxBytes=(5 * (1024**2)), 
backupCount=30)
     log_handler.setFormatter(log_formatter)
-    logger.addHandler(log_handler)
-    logger.raiseExceptions = False
+
+    root_logger = logging.getLogger()
+    root_logger.addHandler(log_handler)
+    root_logger.raiseExceptions = False
 
     if trace:
-        logger.setLevel(cumin.LOGGING_TRACE_LEVEL_NUMBER)
+        root_logger.setLevel(cumin.LOGGING_TRACE_LEVEL_NUMBER)
     elif debug:
-        logger.setLevel(logging.DEBUG)
+        root_logger.setLevel(logging.DEBUG)
     else:
-        logger.setLevel(logging.INFO)
+        root_logger.setLevel(logging.INFO)
 
 
 def sigint_handler(*args):  # pylint: disable=unused-argument
@@ -262,7 +263,7 @@
         args: ArgumentParser instance with parsed command line arguments
         config: a dictionary with the parsed configuration file
     """
-    hosts = query.Query(config, logger=logger).execute(args.hosts)
+    hosts = query.Query(config).execute(args.hosts)
 
     if not hosts:
         stderr('No hosts found that matches the query')
@@ -337,8 +338,8 @@
     if not hosts:
         return 0
 
-    target = transports.Target(hosts, batch_size=args.batch_size, 
batch_sleep=args.batch_sleep, logger=logger)
-    worker = transport.Transport.new(config, target, logger=logger)
+    target = transports.Target(hosts, batch_size=args.batch_size, 
batch_sleep=args.batch_sleep)
+    worker = transport.Transport.new(config, target)
 
     ok_codes = None
     if args.ignore_exit_codes:
diff --git a/cumin/query.py b/cumin/query.py
index 839dc42..8f005e0 100644
--- a/cumin/query.py
+++ b/cumin/query.py
@@ -20,17 +20,17 @@
         >>> import cumin
         >>> from cumin.query import Query
         >>> config = cumin.Config()
-        >>> hosts = Query(config, logger=logger).execute(query_string)
+        >>> hosts = Query(config).execute(query_string)
 
     """
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Query constructor, initialize the registered backends.
 
         :Parameters:
             according to parent 
:py:meth:`cumin.backends.BaseQueryAggregator.__init__`.
         """
-        super(Query, self).__init__(config, logger=logger)
+        super(Query, self).__init__(config)
         external = self.config.get('plugins', {}).get('backends', [])
         self.registered_backends = 
grammar.get_registered_backends(external=external)
         self.grammar = grammar.grammar(self.registered_backends.keys())
@@ -89,7 +89,7 @@
             raise InvalidQueryError("Default backend '{name}' is not 
registered: {backends}".format(
                 name=self.config['default_backend'], 
backends=self.registered_backends))
 
-        query = backend.cls(self.config, logger=self.logger)
+        query = backend.cls(self.config)
 
         return query.execute(query_string)
 
@@ -115,7 +115,7 @@
 
         if 'backend' in token_dict and 'query' in token_dict:
             element = self._get_stack_element()
-            query = 
self.registered_backends[token_dict['backend']].cls(self.config, 
logger=self.logger)
+            query = 
self.registered_backends[token_dict['backend']].cls(self.config)
             element['hosts'] = query.execute(token_dict['query'])
             if 'bool' in token_dict:
                 element['bool'] = token_dict['bool']
diff --git a/cumin/tests/unit/backends/external/__init__.py 
b/cumin/tests/unit/backends/external/__init__.py
index 2c2a8aa..374ded9 100644
--- a/cumin/tests/unit/backends/external/__init__.py
+++ b/cumin/tests/unit/backends/external/__init__.py
@@ -25,14 +25,14 @@
     grammar = grammar()
     """:py:class:`pyparsing.ParserElement`: load the grammar parser only once 
in a singleton-like way."""
 
-    def __init__(self, config, logger=None):
+    def __init__(self, config):
         """Query constructor for the test external backend.
 
         :Parameters:
             according to parent :py:meth:`cumin.backends.BaseQuery.__init__`.
 
         """
-        super(ExternalBackendQuery, self).__init__(config, logger=logger)
+        super(ExternalBackendQuery, self).__init__(config)
         self.hosts = NodeSet()
 
     def _execute(self):
diff --git a/cumin/tests/unit/test_cli.py b/cumin/tests/unit/test_cli.py
index b61d5ac..5ab1a76 100644
--- a/cumin/tests/unit/test_cli.py
+++ b/cumin/tests/unit/test_cli.py
@@ -70,21 +70,21 @@
 
 @mock.patch('cumin.cli.os')
 @mock.patch('cumin.cli.RotatingFileHandler')
[email protected]('cumin.cli.logger')
-def test_setup_logging(logging, file_handler, mocked_os):
[email protected]('cumin.cli.logging.getLogger')
+def test_setup_logging(mocked_get_logger, file_handler, mocked_os):
     """Calling setup_logging() should properly setup the logger."""
     mocked_os.path.exists.return_value = False
     cli.setup_logging('/path/to/filename')
-    logging.setLevel.assert_called_with(INFO)
+    assert mock.call().setLevel(INFO) in mocked_get_logger.mock_calls
     assert file_handler.called
 
     mocked_os.path.exists.return_value = True
     cli.setup_logging('filename', debug=True)
-    logging.setLevel.assert_called_with(DEBUG)
+    assert mock.call().setLevel(DEBUG) in mocked_get_logger.mock_calls
 
     mocked_os.path.exists.return_value = True
     cli.setup_logging('filename', trace=True)
-    logging.setLevel.assert_called_with(LOGGING_TRACE_LEVEL_NUMBER)
+    assert mock.call().setLevel(LOGGING_TRACE_LEVEL_NUMBER) in 
mocked_get_logger.mock_calls
 
 
 @mock.patch('cumin.cli.stderr')
@@ -206,7 +206,6 @@
     cli.run(args, config)
     assert transport.new.call_args[0][0] is config
     assert isinstance(transport.new.call_args[0][1], transports.Target)
-    assert transport.new.call_args[1]['logger'] is cli.logger
     assert stderr.called
 
 
diff --git a/cumin/transport.py b/cumin/transport.py
index bdca28a..25c268e 100644
--- a/cumin/transport.py
+++ b/cumin/transport.py
@@ -9,13 +9,12 @@
     """Transport factory class."""
 
     @staticmethod
-    def new(config, target, logger=None):
+    def new(config, target):
         """Create a transport worker class based on the configuration 
(`factory`).
 
         Arguments:
             config (dict): the configuration dictionary.
             target (cumin.transports.Target): a Target instance.
-            logger (logging.Logger, optional): an optional logger instance.
 
         Returns:
             BaseWorker: the created worker instance for the configured 
transport.
@@ -31,7 +30,7 @@
 
         try:
             module = 
importlib.import_module('cumin.transports.{transport}'.format(transport=config['transport']))
-            return module.worker_class(config, target, logger=logger)
+            return module.worker_class(config, target)
         except (AttributeError, ImportError) as e:
             e.message = "Unable to load worker class for transport 
'{transport}': {msg}".format(
                 transport=config['transport'], msg=repr(e))
diff --git a/cumin/transports/__init__.py b/cumin/transports/__init__.py
index 153f177..d046cc2 100644
--- a/cumin/transports/__init__.py
+++ b/cumin/transports/__init__.py
@@ -325,7 +325,7 @@
 class Target(object):
     """Targets management class."""
 
-    def __init__(self, hosts, batch_size=None, batch_sleep=None, logger=None):
+    def __init__(self, hosts, batch_size=None, batch_sleep=None):
         """Constructor, inizialize the Target with the list of hosts and 
additional parameters.
 
         Arguments:
@@ -337,13 +337,12 @@
                 It must be a positive integer or :py:data:`None` to unset it.
             batch_sleep (int, optional): sleep time in seconds between the end 
of execution of one host in the
                 batch and the start in the next host. It must be a positive 
float or None to unset it.
-            logger (logging.Logger, optional): a logger instance.
 
         Raises:
             cumin.transports.WorkerError: if the `hosts` parameter is invalid.
 
         """
-        self.logger = logger or logging.getLogger(__name__)
+        self.logger = logging.getLogger('.'.join((self.__module__, 
self.__class__.__name__)))
 
         if isinstance(hosts, NodeSet):
             self.hosts = hosts
@@ -411,17 +410,16 @@
 
     __metaclass__ = ABCMeta
 
-    def __init__(self, config, target, logger=None):
+    def __init__(self, config, target):
         """Worker constructor. Setup environment variables and initialize 
properties.
 
         Arguments:
             config (dict): a dictionary with the parsed configuration file.
             target (Target): a Target instance.
-            logger (logging.Logger, optional): an optional logger instance.
         """
         self.config = config
         self.target = target
-        self.logger = logger or logging.getLogger(__name__)
+        self.logger = logging.getLogger('.'.join((self.__module__, 
self.__class__.__name__)))
         self.logger.trace('Transport {name} created with config: 
{config}'.format(
             name=type(self).__name__, config=config))
 
diff --git a/cumin/transports/clustershell.py b/cumin/transports/clustershell.py
index d5ba468..36f5c8f 100644
--- a/cumin/transports/clustershell.py
+++ b/cumin/transports/clustershell.py
@@ -16,13 +16,13 @@
 class ClusterShellWorker(BaseWorker):
     """It provides a Cumin worker for SSH using the ClusterShell library."""
 
-    def __init__(self, config, target, logger=None):
+    def __init__(self, config, target):
         """Worker ClusterShell constructor.
 
         :Parameters:
             according to parent 
:py:meth:`cumin.transports.BaseWorker.__init__`.
         """
-        super(ClusterShellWorker, self).__init__(config, target, logger)
+        super(ClusterShellWorker, self).__init__(config, target)
         self.task = Task.task_self()  # Initialize a ClusterShell task
         self._handler_instance = None
 
@@ -55,7 +55,7 @@
         # Instantiate handler
         # Schedule only the first command for the first batch, the following 
ones must be handled by the EventHandler
         self._handler_instance = self.handler(  # pylint: disable=not-callable
-            self.target, self.commands, 
success_threshold=self.success_threshold, logger=self.logger)
+            self.target, self.commands, 
success_threshold=self.success_threshold)
 
         self.logger.info("Executing commands {commands} on '{num}' hosts: 
{hosts}".format(
             commands=self.commands, num=len(self.target.hosts), 
hosts=self.target.hosts))
@@ -146,7 +146,7 @@
     short_command_length = 35
     """:py:class:`int`: the length to which a command should be shortened in 
various outputs."""
 
-    def __init__(self, target, commands, success_threshold=1.0, logger=None, 
**kwargs):
+    def __init__(self, target, commands, success_threshold=1.0, **kwargs):
         """Event handler ClusterShell extension constructor.
 
         If subclasses defines a ``self.pbar_ko`` `tqdm` progress bar, it will 
be updated on timeout.
@@ -160,7 +160,7 @@
         """
         super(BaseEventHandler, self).__init__()
         self.success_threshold = success_threshold
-        self.logger = logger or logging.getLogger(__name__)
+        self.logger = logging.getLogger('.'.join((self.__module__, 
self.__class__.__name__)))
         self.target = target
         self.lock = threading.Lock()  # Used to update instance variables 
coherently from within callbacks
 
@@ -480,14 +480,14 @@
     enough nodes before proceeding with the next one.
     """
 
-    def __init__(self, target, commands, success_threshold=1.0, logger=None, 
**kwargs):
+    def __init__(self, target, commands, success_threshold=1.0, **kwargs):
         """Define a custom ClusterShell event handler to execute commands 
synchronously.
 
         :Parameters:
             according to parent :py:meth:`BaseEventHandler.__init__`.
         """
         super(SyncEventHandler, self).__init__(
-            target, commands, success_threshold=success_threshold, 
logger=logger, **kwargs)
+            target, commands, success_threshold=success_threshold, **kwargs)
         self.current_command_index = 0  # Global pointer for the current 
command in execution across all nodes
         self.start_command()
         self.aborted = False
@@ -706,14 +706,14 @@
     orchestration between the nodes.
     """
 
-    def __init__(self, target, commands, success_threshold=1.0, logger=None, 
**kwargs):
+    def __init__(self, target, commands, success_threshold=1.0, **kwargs):
         """Define a custom ClusterShell event handler to execute commands 
asynchronously between nodes.
 
         :Parameters:
             according to parent :py:meth:`BaseEventHandler.__init__`.
         """
         super(AsyncEventHandler, self).__init__(
-            target, commands, success_threshold=success_threshold, 
logger=logger, **kwargs)
+            target, commands, success_threshold=success_threshold, **kwargs)
 
         self.pbar_ok = tqdm(desc='PASS', total=self.counters['total'], 
leave=True, unit='hosts', dynamic_ncols=True,
                             bar_format=colorama.Fore.GREEN + self.bar_format, 
file=sys.stderr)

-- 
To view, visit https://gerrit.wikimedia.org/r/386399
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I44eaed95ae4e7d12f16a03029e7c753bbefccb71
Gerrit-PatchSet: 1
Gerrit-Project: operations/software/cumin
Gerrit-Branch: master
Gerrit-Owner: Volans <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to