From 796b51522c2cf68ea6b6a2944a1a2373d51d72cb Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Tue, 27 Nov 2012 10:27:00 +0100
Subject: [PATCH] Factorize logging setup in tools
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Most tools had their own β€œSetupLogging” function, but they were all
essentially the same. This patch adds a generic version to β€œutils.log”
and provides unit tests.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
---
 lib/tools/ensure_dirs.py          | 22 +---------
 lib/tools/prepare_node_join.py    | 22 +---------
 lib/utils/log.py                  | 46 +++++++++++++++++++++
 test/ganeti.utils.log_unittest.py | 68 +++++++++++++++++++++++++++++++
 tools/cluster-merge               | 24 +----------
 tools/move-instance               | 29 +------------
 tools/ovfconverter                | 28 ++-----------
 7 files changed, 122 insertions(+), 117 deletions(-)

diff --git a/lib/tools/ensure_dirs.py b/lib/tools/ensure_dirs.py
index afe4877cd..48a7b4415 100644
--- a/lib/tools/ensure_dirs.py
+++ b/lib/tools/ensure_dirs.py
@@ -201,26 +201,6 @@ def GetPaths():
   return paths
 
 
-def SetupLogging(opts):
-  """Configures the logging module.
-
-  """
-  formatter = logging.Formatter("%(asctime)s: %(message)s")
-
-  stderr_handler = logging.StreamHandler()
-  stderr_handler.setFormatter(formatter)
-  if opts.debug:
-    stderr_handler.setLevel(logging.NOTSET)
-  elif opts.verbose:
-    stderr_handler.setLevel(logging.INFO)
-  else:
-    stderr_handler.setLevel(logging.WARNING)
-
-  root_logger = logging.getLogger("")
-  root_logger.setLevel(logging.NOTSET)
-  root_logger.addHandler(stderr_handler)
-
-
 def ParseOptions():
   """Parses the options passed to the program.
 
@@ -246,7 +226,7 @@ def Main():
   """
   (opts, args) = ParseOptions()
 
-  SetupLogging(opts)
+  utils.SetupToolLogging(opts.debug, opts.verbose)
 
   if args:
     logging.error("No arguments are expected")
diff --git a/lib/tools/prepare_node_join.py b/lib/tools/prepare_node_join.py
index 37bd0bccc..deedd0b32 100644
--- a/lib/tools/prepare_node_join.py
+++ b/lib/tools/prepare_node_join.py
@@ -94,26 +94,6 @@ def VerifyOptions(parser, opts, args):
   return opts
 
 
-def SetupLogging(opts):
-  """Configures the logging module.
-
-  """
-  formatter = logging.Formatter("%(asctime)s: %(message)s")
-
-  stderr_handler = logging.StreamHandler()
-  stderr_handler.setFormatter(formatter)
-  if opts.debug:
-    stderr_handler.setLevel(logging.NOTSET)
-  elif opts.verbose:
-    stderr_handler.setLevel(logging.INFO)
-  else:
-    stderr_handler.setLevel(logging.WARNING)
-
-  root_logger = logging.getLogger("")
-  root_logger.setLevel(logging.NOTSET)
-  root_logger.addHandler(stderr_handler)
-
-
 def _VerifyCertificate(cert, _noded_cert_file=pathutils.NODED_CERT_FILE):
   """Verifies a certificate against the local node daemon certificate.
 
@@ -319,7 +299,7 @@ def Main():
   """
   opts = ParseOptions()
 
-  SetupLogging(opts)
+  utils.SetupToolLogging(opts.debug, opts.verbose)
 
   try:
     data = LoadData(sys.stdin.read())
diff --git a/lib/utils/log.py b/lib/utils/log.py
index 281f59045..2afbfd2f2 100644
--- a/lib/utils/log.py
+++ b/lib/utils/log.py
@@ -25,6 +25,7 @@
 import os.path
 import logging
 import logging.handlers
+from cStringIO import StringIO
 
 from ganeti import constants
 from ganeti import compat
@@ -269,3 +270,48 @@ def SetupLogging(logfile, program, debug=0, stderr_logging=False,
         raise
 
   return compat.partial(_ReopenLogFiles, reopen_handlers)
+
+
+def SetupToolLogging(debug, verbose, threadname=False,
+                     _root_logger=None, _stream=None):
+  """Configures the logging module for tools.
+
+  All log messages are sent to stderr.
+
+  @type debug: boolean
+  @param debug: Disable log message filtering
+  @type verbose: boolean
+  @param verbose: Enable verbose log messages
+  @type threadname: boolean
+  @param threadname: Whether to include thread name in output
+
+  """
+  if _root_logger is None:
+    root_logger = logging.getLogger("")
+  else:
+    root_logger = _root_logger
+
+  fmt = StringIO()
+  fmt.write("%(asctime)s:")
+
+  if threadname:
+    fmt.write(" %(threadName)s")
+
+  if debug or verbose:
+    fmt.write(" %(levelname)s")
+
+  fmt.write(" %(message)s")
+
+  formatter = logging.Formatter(fmt.getvalue())
+
+  stderr_handler = logging.StreamHandler(_stream)
+  stderr_handler.setFormatter(formatter)
+  if debug:
+    stderr_handler.setLevel(logging.NOTSET)
+  elif verbose:
+    stderr_handler.setLevel(logging.INFO)
+  else:
+    stderr_handler.setLevel(logging.WARNING)
+
+  root_logger.setLevel(logging.NOTSET)
+  root_logger.addHandler(stderr_handler)
diff --git a/test/ganeti.utils.log_unittest.py b/test/ganeti.utils.log_unittest.py
index 5ae860bbb..517694c72 100755
--- a/test/ganeti.utils.log_unittest.py
+++ b/test/ganeti.utils.log_unittest.py
@@ -26,9 +26,12 @@ import unittest
 import logging
 import tempfile
 import shutil
+import threading
+from cStringIO import StringIO
 
 from ganeti import constants
 from ganeti import errors
+from ganeti import compat
 from ganeti import utils
 
 import testutils
@@ -192,5 +195,70 @@ class TestSetupLogging(unittest.TestCase):
     self.assertTrue(utils.ReadFile(logfile2).endswith("This is a test\n"))
 
 
+class TestSetupToolLogging(unittest.TestCase):
+  def test(self):
+    error_name = logging.getLevelName(logging.ERROR)
+    warn_name = logging.getLevelName(logging.WARNING)
+    info_name = logging.getLevelName(logging.INFO)
+    debug_name = logging.getLevelName(logging.DEBUG)
+
+    for debug in [False, True]:
+      for verbose in [False, True]:
+        logger = logging.Logger("TestLogger")
+        buf = StringIO()
+
+        utils.SetupToolLogging(debug, verbose, _root_logger=logger, _stream=buf)
+
+        logger.error("level=error")
+        logger.warning("level=warning")
+        logger.info("level=info")
+        logger.debug("level=debug")
+
+        lines = buf.getvalue().splitlines()
+
+        self.assertTrue(compat.all(line.count(":") == 3 for line in lines))
+
+        messages = [line.split(":", 3)[-1].strip() for line in lines]
+
+        if debug:
+          self.assertEqual(messages, [
+            "%s level=error" % error_name,
+            "%s level=warning" % warn_name,
+            "%s level=info" % info_name,
+            "%s level=debug" % debug_name,
+            ])
+        elif verbose:
+          self.assertEqual(messages, [
+            "%s level=error" % error_name,
+            "%s level=warning" % warn_name,
+            "%s level=info" % info_name,
+            ])
+        else:
+          self.assertEqual(messages, [
+            "level=error",
+            "level=warning",
+            ])
+
+  def testThreadName(self):
+    thread_name = threading.currentThread().getName()
+
+    for enable_threadname in [False, True]:
+      logger = logging.Logger("TestLogger")
+      buf = StringIO()
+
+      utils.SetupToolLogging(True, True, threadname=enable_threadname,
+                             _root_logger=logger, _stream=buf)
+
+      logger.debug("test134042376")
+
+      lines = buf.getvalue().splitlines()
+      self.assertEqual(len(lines), 1)
+
+      if enable_threadname:
+        self.assertTrue((" %s " % thread_name) in lines[0])
+      else:
+        self.assertTrue(thread_name not in lines[0])
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()
diff --git a/tools/cluster-merge b/tools/cluster-merge
index d45d38148..a066335f4 100755
--- a/tools/cluster-merge
+++ b/tools/cluster-merge
@@ -781,28 +781,6 @@ class Merger(object):
     shutil.rmtree(self.work_dir)
 
 
-def SetupLogging(options):
-  """Setting up logging infrastructure.
-
-  @param options: Parsed command line options
-
-  """
-  formatter = logging.Formatter("%(asctime)s: %(levelname)s %(message)s")
-
-  stderr_handler = logging.StreamHandler()
-  stderr_handler.setFormatter(formatter)
-  if options.debug:
-    stderr_handler.setLevel(logging.NOTSET)
-  elif options.verbose:
-    stderr_handler.setLevel(logging.INFO)
-  else:
-    stderr_handler.setLevel(logging.WARNING)
-
-  root_logger = logging.getLogger("")
-  root_logger.setLevel(logging.NOTSET)
-  root_logger.addHandler(stderr_handler)
-
-
 def main():
   """Main routine.
 
@@ -821,7 +799,7 @@ def main():
 
   (options, args) = parser.parse_args()
 
-  SetupLogging(options)
+  utils.SetupToolLogging(options.debug, options.verbose)
 
   if not args:
     parser.error("No clusters specified")
diff --git a/tools/move-instance b/tools/move-instance
index 8490ecf9d..24bb49612 100755
--- a/tools/move-instance
+++ b/tools/move-instance
@@ -719,33 +719,6 @@ def CheckRapiSetup(rapi_factory):
   logging.info("Destination cluster RAPI version: %s", dest_client.GetVersion())
 
 
-def SetupLogging(options):
-  """Setting up logging infrastructure.
-
-  @param options: Parsed command line options
-
-  """
-  fmt = "%(asctime)s: %(threadName)s "
-  if options.debug or options.verbose:
-    fmt += "%(levelname)s "
-  fmt += "%(message)s"
-
-  formatter = logging.Formatter(fmt)
-
-  stderr_handler = logging.StreamHandler()
-  stderr_handler.setFormatter(formatter)
-  if options.debug:
-    stderr_handler.setLevel(logging.NOTSET)
-  elif options.verbose:
-    stderr_handler.setLevel(logging.INFO)
-  else:
-    stderr_handler.setLevel(logging.ERROR)
-
-  root_logger = logging.getLogger("")
-  root_logger.setLevel(logging.NOTSET)
-  root_logger.addHandler(stderr_handler)
-
-
 def ParseOptions():
   """Parses options passed to program.
 
@@ -845,7 +818,7 @@ def main():
   """
   (parser, options, args) = ParseOptions()
 
-  SetupLogging(options)
+  utils.SetupToolLogging(options.debug, options.verbose, threadname=True)
 
   (src_cluster_name, dest_cluster_name, instance_names) = \
     CheckOptions(parser, options, args)
diff --git a/tools/ovfconverter b/tools/ovfconverter
index 17024bf20..fd7aa4364 100755
--- a/tools/ovfconverter
+++ b/tools/ovfconverter
@@ -31,6 +31,7 @@ from ganeti import cli
 from ganeti import constants
 from ganeti import errors
 from ganeti import ovf
+from ganeti import utils
 
 
 IMPORT_MODE = "import"
@@ -161,35 +162,14 @@ def ParseOptions():
   return (mode, input_path, options)
 
 
-def SetupLogging(options):
-  """Setting up logging infrastructure.
-
-  @type options: optparse.Values
-  @param options: parsed command line options
-
-  """
-  formatter = logging.Formatter("%(asctime)s: %(levelname)s %(message)s")
-
-  stderr_handler = logging.StreamHandler()
-  stderr_handler.setFormatter(formatter)
-  if options.debug:
-    stderr_handler.setLevel(logging.NOTSET)
-  elif options.verbose:
-    stderr_handler.setLevel(logging.INFO)
-  else:
-    stderr_handler.setLevel(logging.WARNING)
-
-  root_logger = logging.getLogger("")
-  root_logger.setLevel(logging.NOTSET)
-  root_logger.addHandler(stderr_handler)
-
-
 def main():
   """Main routine.
 
   """
   (mode, input_path, options) = ParseOptions()
-  SetupLogging(options)
+
+  utils.SetupToolLogging(options.debug, options.verbose)
+
   logging.info("Chosen %s mode, reading the %s file", mode, input_path)
   assert mode in (IMPORT_MODE, EXPORT_MODE)
   converter = None
-- 
GitLab