WebKitTools/Scripts/webkitpy/common/system/logutils.py
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
       
     2 #
       
     3 # Redistribution and use in source and binary forms, with or without
       
     4 # modification, are permitted provided that the following conditions
       
     5 # are met:
       
     6 # 1.  Redistributions of source code must retain the above copyright
       
     7 #     notice, this list of conditions and the following disclaimer.
       
     8 # 2.  Redistributions in binary form must reproduce the above copyright
       
     9 #     notice, this list of conditions and the following disclaimer in the
       
    10 #     documentation and/or other materials provided with the distribution.
       
    11 #
       
    12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
       
    13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
       
    16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
       
    17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
       
    18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
       
    19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
       
    20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    22 
       
    23 """Supports webkitpy logging."""
       
    24 
       
    25 # FIXME: Move this file to webkitpy/python24 since logging needs to
       
    26 #        be configured prior to running version-checking code.
       
    27 
       
    28 import logging
       
    29 import os
       
    30 import sys
       
    31 
       
    32 import webkitpy
       
    33 
       
    34 
       
    35 _log = logging.getLogger(__name__)
       
    36 
       
    37 # We set these directory paths lazily in get_logger() below.
       
    38 _scripts_dir = ""
       
    39 """The normalized, absolute path to the ...Scripts directory."""
       
    40 
       
    41 _webkitpy_dir = ""
       
    42 """The normalized, absolute path to the ...Scripts/webkitpy directory."""
       
    43 
       
    44 
       
    45 def _normalize_path(path):
       
    46     """Return the given path normalized.
       
    47 
       
    48     Converts a path to an absolute path, removes any trailing slashes,
       
    49     removes any extension, and lower-cases it.
       
    50 
       
    51     """
       
    52     path = os.path.abspath(path)
       
    53     path = os.path.normpath(path)
       
    54     path = os.path.splitext(path)[0]  # Remove the extension, if any.
       
    55     path = path.lower()
       
    56 
       
    57     return path
       
    58 
       
    59 
       
    60 # Observe that the implementation of this function does not require
       
    61 # the use of any hard-coded strings like "webkitpy", etc.
       
    62 #
       
    63 # The main benefit this function has over using--
       
    64 #
       
    65 # _log = logging.getLogger(__name__)
       
    66 #
       
    67 # is that get_logger() returns the same value even if __name__ is
       
    68 # "__main__" -- i.e. even if the module is the script being executed
       
    69 # from the command-line.
       
    70 def get_logger(path):
       
    71     """Return a logging.logger for the given path.
       
    72 
       
    73     Returns:
       
    74       A logger whose name is the name of the module corresponding to
       
    75       the given path.  If the module is in webkitpy, the name is
       
    76       the fully-qualified dotted module name beginning with webkitpy....
       
    77       Otherwise, the name is the base name of the module (i.e. without
       
    78       any dotted module name prefix).
       
    79 
       
    80     Args:
       
    81       path: The path of the module.  Normally, this parameter should be
       
    82             the __file__ variable of the module.
       
    83 
       
    84     Sample usage:
       
    85 
       
    86       import webkitpy.common.system.logutils as logutils
       
    87 
       
    88       _log = logutils.get_logger(__file__)
       
    89 
       
    90     """
       
    91     # Since we assign to _scripts_dir and _webkitpy_dir in this function,
       
    92     # we need to declare them global.
       
    93     global _scripts_dir
       
    94     global _webkitpy_dir
       
    95 
       
    96     path = _normalize_path(path)
       
    97 
       
    98     # Lazily evaluate _webkitpy_dir and _scripts_dir.
       
    99     if not _scripts_dir:
       
   100         # The normalized, absolute path to ...Scripts/webkitpy/__init__.
       
   101         webkitpy_path = _normalize_path(webkitpy.__file__)
       
   102 
       
   103         _webkitpy_dir = os.path.split(webkitpy_path)[0]
       
   104         _scripts_dir = os.path.split(_webkitpy_dir)[0]
       
   105 
       
   106     if path.startswith(_webkitpy_dir):
       
   107         # Remove the initial Scripts directory portion, so the path
       
   108         # starts with /webkitpy, for example "/webkitpy/init/logutils".
       
   109         path = path[len(_scripts_dir):]
       
   110 
       
   111         parts = []
       
   112         while True:
       
   113             (path, tail) = os.path.split(path)
       
   114             if not tail:
       
   115                 break
       
   116             parts.insert(0, tail)
       
   117 
       
   118         logger_name = ".".join(parts)  # For example, webkitpy.common.system.logutils.
       
   119     else:
       
   120         # The path is outside of webkitpy.  Default to the basename
       
   121         # without the extension.
       
   122         basename = os.path.basename(path)
       
   123         logger_name = os.path.splitext(basename)[0]
       
   124 
       
   125     return logging.getLogger(logger_name)
       
   126 
       
   127 
       
   128 def _default_handlers(stream):
       
   129     """Return a list of the default logging handlers to use.
       
   130 
       
   131     Args:
       
   132       stream: See the configure_logging() docstring.
       
   133 
       
   134     """
       
   135     # Create the filter.
       
   136     def should_log(record):
       
   137         """Return whether a logging.LogRecord should be logged."""
       
   138         # FIXME: Enable the logging of autoinstall messages once
       
   139         #        autoinstall is adjusted.  Currently, autoinstall logs
       
   140         #        INFO messages when importing already-downloaded packages,
       
   141         #        which is too verbose.
       
   142         if record.name.startswith("webkitpy.thirdparty.autoinstall"):
       
   143             return False
       
   144         return True
       
   145 
       
   146     logging_filter = logging.Filter()
       
   147     logging_filter.filter = should_log
       
   148 
       
   149     # Create the handler.
       
   150     handler = logging.StreamHandler(stream)
       
   151     formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
       
   152     handler.setFormatter(formatter)
       
   153     handler.addFilter(logging_filter)
       
   154 
       
   155     return [handler]
       
   156 
       
   157 
       
   158 def configure_logging(logging_level=None, logger=None, stream=None,
       
   159                       handlers=None):
       
   160     """Configure logging for standard purposes.
       
   161 
       
   162     Returns:
       
   163       A list of references to the logging handlers added to the root
       
   164       logger.  This allows the caller to later remove the handlers
       
   165       using logger.removeHandler.  This is useful primarily during unit
       
   166       testing where the caller may want to configure logging temporarily
       
   167       and then undo the configuring.
       
   168 
       
   169     Args:
       
   170       logging_level: The minimum logging level to log.  Defaults to
       
   171                      logging.INFO.
       
   172       logger: A logging.logger instance to configure.  This parameter
       
   173               should be used only in unit tests.  Defaults to the
       
   174               root logger.
       
   175       stream: A file-like object to which to log used in creating the default
       
   176               handlers.  The stream must define an "encoding" data attribute,
       
   177               or else logging raises an error.  Defaults to sys.stderr.
       
   178       handlers: A list of logging.Handler instances to add to the logger
       
   179                 being configured.  If this parameter is provided, then the
       
   180                 stream parameter is not used.
       
   181 
       
   182     """
       
   183     # If the stream does not define an "encoding" data attribute, the
       
   184     # logging module can throw an error like the following:
       
   185     #
       
   186     # Traceback (most recent call last):
       
   187     #   File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
       
   188     #         lib/python2.6/logging/__init__.py", line 761, in emit
       
   189     #     self.stream.write(fs % msg.encode(self.stream.encoding))
       
   190     # LookupError: unknown encoding: unknown
       
   191     if logging_level is None:
       
   192         logging_level = logging.INFO
       
   193     if logger is None:
       
   194         logger = logging.getLogger()
       
   195     if stream is None:
       
   196         stream = sys.stderr
       
   197     if handlers is None:
       
   198         handlers = _default_handlers(stream)
       
   199 
       
   200     logger.setLevel(logging_level)
       
   201 
       
   202     for handler in handlers:
       
   203         logger.addHandler(handler)
       
   204 
       
   205     _log.debug("Debug logging enabled.")
       
   206 
       
   207     return handlers