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