WebKitTools/Scripts/webkitpy/common/system/logutils.py
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKitTools/Scripts/webkitpy/common/system/logutils.py	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,207 @@
+# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Supports webkitpy logging."""
+
+# FIXME: Move this file to webkitpy/python24 since logging needs to
+#        be configured prior to running version-checking code.
+
+import logging
+import os
+import sys
+
+import webkitpy
+
+
+_log = logging.getLogger(__name__)
+
+# We set these directory paths lazily in get_logger() below.
+_scripts_dir = ""
+"""The normalized, absolute path to the ...Scripts directory."""
+
+_webkitpy_dir = ""
+"""The normalized, absolute path to the ...Scripts/webkitpy directory."""
+
+
+def _normalize_path(path):
+    """Return the given path normalized.
+
+    Converts a path to an absolute path, removes any trailing slashes,
+    removes any extension, and lower-cases it.
+
+    """
+    path = os.path.abspath(path)
+    path = os.path.normpath(path)
+    path = os.path.splitext(path)[0]  # Remove the extension, if any.
+    path = path.lower()
+
+    return path
+
+
+# Observe that the implementation of this function does not require
+# the use of any hard-coded strings like "webkitpy", etc.
+#
+# The main benefit this function has over using--
+#
+# _log = logging.getLogger(__name__)
+#
+# is that get_logger() returns the same value even if __name__ is
+# "__main__" -- i.e. even if the module is the script being executed
+# from the command-line.
+def get_logger(path):
+    """Return a logging.logger for the given path.
+
+    Returns:
+      A logger whose name is the name of the module corresponding to
+      the given path.  If the module is in webkitpy, the name is
+      the fully-qualified dotted module name beginning with webkitpy....
+      Otherwise, the name is the base name of the module (i.e. without
+      any dotted module name prefix).
+
+    Args:
+      path: The path of the module.  Normally, this parameter should be
+            the __file__ variable of the module.
+
+    Sample usage:
+
+      import webkitpy.common.system.logutils as logutils
+
+      _log = logutils.get_logger(__file__)
+
+    """
+    # Since we assign to _scripts_dir and _webkitpy_dir in this function,
+    # we need to declare them global.
+    global _scripts_dir
+    global _webkitpy_dir
+
+    path = _normalize_path(path)
+
+    # Lazily evaluate _webkitpy_dir and _scripts_dir.
+    if not _scripts_dir:
+        # The normalized, absolute path to ...Scripts/webkitpy/__init__.
+        webkitpy_path = _normalize_path(webkitpy.__file__)
+
+        _webkitpy_dir = os.path.split(webkitpy_path)[0]
+        _scripts_dir = os.path.split(_webkitpy_dir)[0]
+
+    if path.startswith(_webkitpy_dir):
+        # Remove the initial Scripts directory portion, so the path
+        # starts with /webkitpy, for example "/webkitpy/init/logutils".
+        path = path[len(_scripts_dir):]
+
+        parts = []
+        while True:
+            (path, tail) = os.path.split(path)
+            if not tail:
+                break
+            parts.insert(0, tail)
+
+        logger_name = ".".join(parts)  # For example, webkitpy.common.system.logutils.
+    else:
+        # The path is outside of webkitpy.  Default to the basename
+        # without the extension.
+        basename = os.path.basename(path)
+        logger_name = os.path.splitext(basename)[0]
+
+    return logging.getLogger(logger_name)
+
+
+def _default_handlers(stream):
+    """Return a list of the default logging handlers to use.
+
+    Args:
+      stream: See the configure_logging() docstring.
+
+    """
+    # Create the filter.
+    def should_log(record):
+        """Return whether a logging.LogRecord should be logged."""
+        # FIXME: Enable the logging of autoinstall messages once
+        #        autoinstall is adjusted.  Currently, autoinstall logs
+        #        INFO messages when importing already-downloaded packages,
+        #        which is too verbose.
+        if record.name.startswith("webkitpy.thirdparty.autoinstall"):
+            return False
+        return True
+
+    logging_filter = logging.Filter()
+    logging_filter.filter = should_log
+
+    # Create the handler.
+    handler = logging.StreamHandler(stream)
+    formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
+    handler.setFormatter(formatter)
+    handler.addFilter(logging_filter)
+
+    return [handler]
+
+
+def configure_logging(logging_level=None, logger=None, stream=None,
+                      handlers=None):
+    """Configure logging for standard purposes.
+
+    Returns:
+      A list of references to the logging handlers added to the root
+      logger.  This allows the caller to later remove the handlers
+      using logger.removeHandler.  This is useful primarily during unit
+      testing where the caller may want to configure logging temporarily
+      and then undo the configuring.
+
+    Args:
+      logging_level: The minimum logging level to log.  Defaults to
+                     logging.INFO.
+      logger: A logging.logger instance to configure.  This parameter
+              should be used only in unit tests.  Defaults to the
+              root logger.
+      stream: A file-like object to which to log used in creating the default
+              handlers.  The stream must define an "encoding" data attribute,
+              or else logging raises an error.  Defaults to sys.stderr.
+      handlers: A list of logging.Handler instances to add to the logger
+                being configured.  If this parameter is provided, then the
+                stream parameter is not used.
+
+    """
+    # If the stream does not define an "encoding" data attribute, the
+    # logging module can throw an error like the following:
+    #
+    # Traceback (most recent call last):
+    #   File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
+    #         lib/python2.6/logging/__init__.py", line 761, in emit
+    #     self.stream.write(fs % msg.encode(self.stream.encoding))
+    # LookupError: unknown encoding: unknown
+    if logging_level is None:
+        logging_level = logging.INFO
+    if logger is None:
+        logger = logging.getLogger()
+    if stream is None:
+        stream = sys.stderr
+    if handlers is None:
+        handlers = _default_handlers(stream)
+
+    logger.setLevel(logging_level)
+
+    for handler in handlers:
+        logger.addHandler(handler)
+
+    _log.debug("Debug logging enabled.")
+
+    return handlers