Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(351)

Side by Side Diff: third_party/cherrypy/_cplogging.py

Issue 9368042: Add CherryPy to third_party. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/cherrypy/_cperror.py ('k') | third_party/cherrypy/_cpmodpy.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 """
2 Simple config
3 =============
4
5 Although CherryPy uses the :mod:`Python logging module <logging>`, it does so
6 behind the scenes so that simple logging is simple, but complicated logging
7 is still possible. "Simple" logging means that you can log to the screen
8 (i.e. console/stdout) or to a file, and that you can easily have separate
9 error and access log files.
10
11 Here are the simplified logging settings. You use these by adding lines to
12 your config file or dict. You should set these at either the global level or
13 per application (see next), but generally not both.
14
15 * ``log.screen``: Set this to True to have both "error" and "access" messages
16 printed to stdout.
17 * ``log.access_file``: Set this to an absolute filename where you want
18 "access" messages written.
19 * ``log.error_file``: Set this to an absolute filename where you want "error"
20 messages written.
21
22 Many events are automatically logged; to log your own application events, call
23 :func:`cherrypy.log`.
24
25 Architecture
26 ============
27
28 Separate scopes
29 ---------------
30
31 CherryPy provides log managers at both the global and application layers.
32 This means you can have one set of logging rules for your entire site,
33 and another set of rules specific to each application. The global log
34 manager is found at :func:`cherrypy.log`, and the log manager for each
35 application is found at :attr:`app.log<cherrypy._cptree.Application.log>`.
36 If you're inside a request, the latter is reachable from
37 ``cherrypy.request.app.log``; if you're outside a request, you'll have to obtain
38 a reference to the ``app``: either the return value of
39 :func:`tree.mount()<cherrypy._cptree.Tree.mount>` or, if you used
40 :func:`quickstart()<cherrypy.quickstart>` instead, via ``cherrypy.tree.apps['/'] ``.
41
42 By default, the global logs are named "cherrypy.error" and "cherrypy.access",
43 and the application logs are named "cherrypy.error.2378745" and
44 "cherrypy.access.2378745" (the number is the id of the Application object).
45 This means that the application logs "bubble up" to the site logs, so if your
46 application has no log handlers, the site-level handlers will still log the
47 messages.
48
49 Errors vs. Access
50 -----------------
51
52 Each log manager handles both "access" messages (one per HTTP request) and
53 "error" messages (everything else). Note that the "error" log is not just for
54 errors! The format of access messages is highly formalized, but the error log
55 isn't--it receives messages from a variety of sources (including full error
56 tracebacks, if enabled).
57
58
59 Custom Handlers
60 ===============
61
62 The simple settings above work by manipulating Python's standard :mod:`logging`
63 module. So when you need something more complex, the full power of the standard
64 module is yours to exploit. You can borrow or create custom handlers, formats,
65 filters, and much more. Here's an example that skips the standard FileHandler
66 and uses a RotatingFileHandler instead:
67
68 ::
69
70 #python
71 log = app.log
72
73 # Remove the default FileHandlers if present.
74 log.error_file = ""
75 log.access_file = ""
76
77 maxBytes = getattr(log, "rot_maxBytes", 10000000)
78 backupCount = getattr(log, "rot_backupCount", 1000)
79
80 # Make a new RotatingFileHandler for the error log.
81 fname = getattr(log, "rot_error_file", "error.log")
82 h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
83 h.setLevel(DEBUG)
84 h.setFormatter(_cplogging.logfmt)
85 log.error_log.addHandler(h)
86
87 # Make a new RotatingFileHandler for the access log.
88 fname = getattr(log, "rot_access_file", "access.log")
89 h = handlers.RotatingFileHandler(fname, 'a', maxBytes, backupCount)
90 h.setLevel(DEBUG)
91 h.setFormatter(_cplogging.logfmt)
92 log.access_log.addHandler(h)
93
94
95 The ``rot_*`` attributes are pulled straight from the application log object.
96 Since "log.*" config entries simply set attributes on the log object, you can
97 add custom attributes to your heart's content. Note that these handlers are
98 used ''instead'' of the default, simple handlers outlined above (so don't set
99 the "log.error_file" config entry, for example).
100 """
101
102 import datetime
103 import logging
104 # Silence the no-handlers "warning" (stderr write!) in stdlib logging
105 logging.Logger.manager.emittedNoHandlerWarning = 1
106 logfmt = logging.Formatter("%(message)s")
107 import os
108 import sys
109
110 import cherrypy
111 from cherrypy import _cperror
112 from cherrypy._cpcompat import ntob, py3k
113
114
115 class NullHandler(logging.Handler):
116 """A no-op logging handler to silence the logging.lastResort handler."""
117
118 def handle(self, record):
119 pass
120
121 def emit(self, record):
122 pass
123
124 def createLock(self):
125 self.lock = None
126
127
128 class LogManager(object):
129 """An object to assist both simple and advanced logging.
130
131 ``cherrypy.log`` is an instance of this class.
132 """
133
134 appid = None
135 """The id() of the Application object which owns this log manager. If this
136 is a global log manager, appid is None."""
137
138 error_log = None
139 """The actual :class:`logging.Logger` instance for error messages."""
140
141 access_log = None
142 """The actual :class:`logging.Logger` instance for access messages."""
143
144 if py3k:
145 access_log_format = \
146 '{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}"'
147 else:
148 access_log_format = \
149 '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
150
151 logger_root = None
152 """The "top-level" logger name.
153
154 This string will be used as the first segment in the Logger names.
155 The default is "cherrypy", for example, in which case the Logger names
156 will be of the form::
157
158 cherrypy.error.<appid>
159 cherrypy.access.<appid>
160 """
161
162 def __init__(self, appid=None, logger_root="cherrypy"):
163 self.logger_root = logger_root
164 self.appid = appid
165 if appid is None:
166 self.error_log = logging.getLogger("%s.error" % logger_root)
167 self.access_log = logging.getLogger("%s.access" % logger_root)
168 else:
169 self.error_log = logging.getLogger("%s.error.%s" % (logger_root, app id))
170 self.access_log = logging.getLogger("%s.access.%s" % (logger_root, a ppid))
171 self.error_log.setLevel(logging.INFO)
172 self.access_log.setLevel(logging.INFO)
173
174 # Silence the no-handlers "warning" (stderr write!) in stdlib logging
175 self.error_log.addHandler(NullHandler())
176 self.access_log.addHandler(NullHandler())
177
178 cherrypy.engine.subscribe('graceful', self.reopen_files)
179
180 def reopen_files(self):
181 """Close and reopen all file handlers."""
182 for log in (self.error_log, self.access_log):
183 for h in log.handlers:
184 if isinstance(h, logging.FileHandler):
185 h.acquire()
186 h.stream.close()
187 h.stream = open(h.baseFilename, h.mode)
188 h.release()
189
190 def error(self, msg='', context='', severity=logging.INFO, traceback=False):
191 """Write the given ``msg`` to the error log.
192
193 This is not just for errors! Applications may call this at any time
194 to log application-specific information.
195
196 If ``traceback`` is True, the traceback of the current exception
197 (if any) will be appended to ``msg``.
198 """
199 if traceback:
200 msg += _cperror.format_exc()
201 self.error_log.log(severity, ' '.join((self.time(), context, msg)))
202
203 def __call__(self, *args, **kwargs):
204 """An alias for ``error``."""
205 return self.error(*args, **kwargs)
206
207 def access(self):
208 """Write to the access log (in Apache/NCSA Combined Log format).
209
210 See http://httpd.apache.org/docs/2.0/logs.html#combined for format
211 details.
212
213 CherryPy calls this automatically for you. Note there are no arguments;
214 it collects the data itself from
215 :class:`cherrypy.request<cherrypy._cprequest.Request>`.
216
217 Like Apache started doing in 2.0.46, non-printable and other special
218 characters in %r (and we expand that to all parts) are escaped using
219 \\xhh sequences, where hh stands for the hexadecimal representation
220 of the raw byte. Exceptions from this rule are " and \\, which are
221 escaped by prepending a backslash, and all whitespace characters,
222 which are written in their C-style notation (\\n, \\t, etc).
223 """
224 request = cherrypy.serving.request
225 remote = request.remote
226 response = cherrypy.serving.response
227 outheaders = response.headers
228 inheaders = request.headers
229 if response.output_status is None:
230 status = "-"
231 else:
232 status = response.output_status.split(ntob(" "), 1)[0]
233 if py3k:
234 status = status.decode('ISO-8859-1')
235
236 atoms = {'h': remote.name or remote.ip,
237 'l': '-',
238 'u': getattr(request, "login", None) or "-",
239 't': self.time(),
240 'r': request.request_line,
241 's': status,
242 'b': dict.get(outheaders, 'Content-Length', '') or "-",
243 'f': dict.get(inheaders, 'Referer', ''),
244 'a': dict.get(inheaders, 'User-Agent', ''),
245 }
246 if py3k:
247 for k, v in atoms.items():
248 if not isinstance(v, str):
249 v = str(v)
250 v = v.replace('"', '\\"').encode('utf8')
251 # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
252 # and backslash for us. All we have to do is strip the quotes.
253 v = repr(v)[2:-1]
254
255 # in python 3.0 the repr of bytes (as returned by encode)
256 # uses double \'s. But then the logger escapes them yet, again
257 # resulting in quadruple slashes. Remove the extra one here.
258 v = v.replace('\\\\', '\\')
259
260 # Escape double-quote.
261 atoms[k] = v
262
263 try:
264 self.access_log.log(logging.INFO, self.access_log_format.format( **atoms))
265 except:
266 self(traceback=True)
267 else:
268 for k, v in atoms.items():
269 if isinstance(v, unicode):
270 v = v.encode('utf8')
271 elif not isinstance(v, str):
272 v = str(v)
273 # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
274 # and backslash for us. All we have to do is strip the quotes.
275 v = repr(v)[1:-1]
276 # Escape double-quote.
277 atoms[k] = v.replace('"', '\\"')
278
279 try:
280 self.access_log.log(logging.INFO, self.access_log_format % atoms )
281 except:
282 self(traceback=True)
283
284 def time(self):
285 """Return now() in Apache Common Log Format (no timezone)."""
286 now = datetime.datetime.now()
287 monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
288 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
289 month = monthnames[now.month - 1].capitalize()
290 return ('[%02d/%s/%04d:%02d:%02d:%02d]' %
291 (now.day, month, now.year, now.hour, now.minute, now.second))
292
293 def _get_builtin_handler(self, log, key):
294 for h in log.handlers:
295 if getattr(h, "_cpbuiltin", None) == key:
296 return h
297
298
299 # ------------------------- Screen handlers ------------------------- #
300
301 def _set_screen_handler(self, log, enable, stream=None):
302 h = self._get_builtin_handler(log, "screen")
303 if enable:
304 if not h:
305 if stream is None:
306 stream=sys.stderr
307 h = logging.StreamHandler(stream)
308 h.setFormatter(logfmt)
309 h._cpbuiltin = "screen"
310 log.addHandler(h)
311 elif h:
312 log.handlers.remove(h)
313
314 def _get_screen(self):
315 h = self._get_builtin_handler
316 has_h = h(self.error_log, "screen") or h(self.access_log, "screen")
317 return bool(has_h)
318
319 def _set_screen(self, newvalue):
320 self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr)
321 self._set_screen_handler(self.access_log, newvalue, stream=sys.stdout)
322 screen = property(_get_screen, _set_screen,
323 doc="""Turn stderr/stdout logging on or off.
324
325 If you set this to True, it'll add the appropriate StreamHandler for
326 you. If you set it to False, it will remove the handler.
327 """)
328
329 # -------------------------- File handlers -------------------------- #
330
331 def _add_builtin_file_handler(self, log, fname):
332 h = logging.FileHandler(fname)
333 h.setFormatter(logfmt)
334 h._cpbuiltin = "file"
335 log.addHandler(h)
336
337 def _set_file_handler(self, log, filename):
338 h = self._get_builtin_handler(log, "file")
339 if filename:
340 if h:
341 if h.baseFilename != os.path.abspath(filename):
342 h.close()
343 log.handlers.remove(h)
344 self._add_builtin_file_handler(log, filename)
345 else:
346 self._add_builtin_file_handler(log, filename)
347 else:
348 if h:
349 h.close()
350 log.handlers.remove(h)
351
352 def _get_error_file(self):
353 h = self._get_builtin_handler(self.error_log, "file")
354 if h:
355 return h.baseFilename
356 return ''
357 def _set_error_file(self, newvalue):
358 self._set_file_handler(self.error_log, newvalue)
359 error_file = property(_get_error_file, _set_error_file,
360 doc="""The filename for self.error_log.
361
362 If you set this to a string, it'll add the appropriate FileHandler for
363 you. If you set it to ``None`` or ``''``, it will remove the handler.
364 """)
365
366 def _get_access_file(self):
367 h = self._get_builtin_handler(self.access_log, "file")
368 if h:
369 return h.baseFilename
370 return ''
371 def _set_access_file(self, newvalue):
372 self._set_file_handler(self.access_log, newvalue)
373 access_file = property(_get_access_file, _set_access_file,
374 doc="""The filename for self.access_log.
375
376 If you set this to a string, it'll add the appropriate FileHandler for
377 you. If you set it to ``None`` or ``''``, it will remove the handler.
378 """)
379
380 # ------------------------- WSGI handlers ------------------------- #
381
382 def _set_wsgi_handler(self, log, enable):
383 h = self._get_builtin_handler(log, "wsgi")
384 if enable:
385 if not h:
386 h = WSGIErrorHandler()
387 h.setFormatter(logfmt)
388 h._cpbuiltin = "wsgi"
389 log.addHandler(h)
390 elif h:
391 log.handlers.remove(h)
392
393 def _get_wsgi(self):
394 return bool(self._get_builtin_handler(self.error_log, "wsgi"))
395
396 def _set_wsgi(self, newvalue):
397 self._set_wsgi_handler(self.error_log, newvalue)
398 wsgi = property(_get_wsgi, _set_wsgi,
399 doc="""Write errors to wsgi.errors.
400
401 If you set this to True, it'll add the appropriate
402 :class:`WSGIErrorHandler<cherrypy._cplogging.WSGIErrorHandler>` for you
403 (which writes errors to ``wsgi.errors``).
404 If you set it to False, it will remove the handler.
405 """)
406
407
408 class WSGIErrorHandler(logging.Handler):
409 "A handler class which writes logging records to environ['wsgi.errors']."
410
411 def flush(self):
412 """Flushes the stream."""
413 try:
414 stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
415 except (AttributeError, KeyError):
416 pass
417 else:
418 stream.flush()
419
420 def emit(self, record):
421 """Emit a record."""
422 try:
423 stream = cherrypy.serving.request.wsgi_environ.get('wsgi.errors')
424 except (AttributeError, KeyError):
425 pass
426 else:
427 try:
428 msg = self.format(record)
429 fs = "%s\n"
430 import types
431 if not hasattr(types, "UnicodeType"): #if no unicode support...
432 stream.write(fs % msg)
433 else:
434 try:
435 stream.write(fs % msg)
436 except UnicodeError:
437 stream.write(fs % msg.encode("UTF-8"))
438 self.flush()
439 except:
440 self.handleError(record)
OLDNEW
« no previous file with comments | « third_party/cherrypy/_cperror.py ('k') | third_party/cherrypy/_cpmodpy.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698