|
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 |