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.
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.
15 * ``log.screen``: Set this to True to have both "error" and "access" messages
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"
22 Many events are automatically logged; to log your own application events, call
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['/']``.
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
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).
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:
73 # Remove the default FileHandlers if present.
77 maxBytes = getattr(log, "rot_maxBytes", 10000000)
78 backupCount = getattr(log, "rot_backupCount", 1000)
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)
84 h.setFormatter(_cplogging.logfmt)
85 log.error_log.addHandler(h)
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)
91 h.setFormatter(_cplogging.logfmt)
92 log.access_log.addHandler(h)
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).
104 # Silence the no-handlers "warning" (stderr write!) in stdlib logging
105 logging
.Logger
.manager
.emittedNoHandlerWarning
= 1
106 logfmt
= logging
.Formatter("%(message)s")
111 from cherrypy
import _cperror
114 class LogManager(object):
115 """An object to assist both simple and advanced logging.
117 ``cherrypy.log`` is an instance of this class.
121 """The id() of the Application object which owns this log manager. If this
122 is a global log manager, appid is None."""
125 """The actual :class:`logging.Logger` instance for error messages."""
128 """The actual :class:`logging.Logger` instance for access messages."""
130 access_log_format
= \
131 '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
134 """The "top-level" logger name.
136 This string will be used as the first segment in the Logger names.
137 The default is "cherrypy", for example, in which case the Logger names
138 will be of the form::
140 cherrypy.error.<appid>
141 cherrypy.access.<appid>
144 def __init__(self
, appid
=None, logger_root
="cherrypy"):
145 self
.logger_root
= logger_root
148 self
.error_log
= logging
.getLogger("%s.error" % logger_root
)
149 self
.access_log
= logging
.getLogger("%s.access" % logger_root
)
151 self
.error_log
= logging
.getLogger("%s.error.%s" % (logger_root
, appid
))
152 self
.access_log
= logging
.getLogger("%s.access.%s" % (logger_root
, appid
))
153 self
.error_log
.setLevel(logging
.INFO
)
154 self
.access_log
.setLevel(logging
.INFO
)
155 cherrypy
.engine
.subscribe('graceful', self
.reopen_files
)
157 def reopen_files(self
):
158 """Close and reopen all file handlers."""
159 for log
in (self
.error_log
, self
.access_log
):
160 for h
in log
.handlers
:
161 if isinstance(h
, logging
.FileHandler
):
164 h
.stream
= open(h
.baseFilename
, h
.mode
)
167 def error(self
, msg
='', context
='', severity
=logging
.INFO
, traceback
=False):
168 """Write the given ``msg`` to the error log.
170 This is not just for errors! Applications may call this at any time
171 to log application-specific information.
173 If ``traceback`` is True, the traceback of the current exception
174 (if any) will be appended to ``msg``.
177 msg
+= _cperror
.format_exc()
178 self
.error_log
.log(severity
, ' '.join((self
.time(), context
, msg
)))
180 def __call__(self
, *args
, **kwargs
):
181 """An alias for ``error``."""
182 return self
.error(*args
, **kwargs
)
185 """Write to the access log (in Apache/NCSA Combined Log format).
187 See http://httpd.apache.org/docs/2.0/logs.html#combined for format
190 CherryPy calls this automatically for you. Note there are no arguments;
191 it collects the data itself from
192 :class:`cherrypy.request<cherrypy._cprequest.Request>`.
194 Like Apache started doing in 2.0.46, non-printable and other special
195 characters in %r (and we expand that to all parts) are escaped using
196 \\xhh sequences, where hh stands for the hexadecimal representation
197 of the raw byte. Exceptions from this rule are " and \\, which are
198 escaped by prepending a backslash, and all whitespace characters,
199 which are written in their C-style notation (\\n, \\t, etc).
201 request
= cherrypy
.serving
.request
202 remote
= request
.remote
203 response
= cherrypy
.serving
.response
204 outheaders
= response
.headers
205 inheaders
= request
.headers
206 if response
.output_status
is None:
209 status
= response
.output_status
.split(" ", 1)[0]
211 atoms
= {'h': remote
.name
or remote
.ip
,
213 'u': getattr(request
, "login", None) or "-",
215 'r': request
.request_line
,
217 'b': dict.get(outheaders
, 'Content-Length', '') or "-",
218 'f': dict.get(inheaders
, 'Referer', ''),
219 'a': dict.get(inheaders
, 'User-Agent', ''),
221 for k
, v
in atoms
.items():
222 if isinstance(v
, unicode):
224 elif not isinstance(v
, str):
226 # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
227 # and backslash for us. All we have to do is strip the quotes.
229 # Escape double-quote.
230 atoms
[k
] = v
.replace('"', '\\"')
233 self
.access_log
.log(logging
.INFO
, self
.access_log_format
% atoms
)
238 """Return now() in Apache Common Log Format (no timezone)."""
239 now
= datetime
.datetime
.now()
240 monthnames
= ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
241 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
242 month
= monthnames
[now
.month
- 1].capitalize()
243 return ('[%02d/%s/%04d:%02d:%02d:%02d]' %
244 (now
.day
, month
, now
.year
, now
.hour
, now
.minute
, now
.second
))
246 def _get_builtin_handler(self
, log
, key
):
247 for h
in log
.handlers
:
248 if getattr(h
, "_cpbuiltin", None) == key
:
252 # ------------------------- Screen handlers ------------------------- #
254 def _set_screen_handler(self
, log
, enable
, stream
=None):
255 h
= self
._get
_builtin
_handler
(log
, "screen")
260 h
= logging
.StreamHandler(stream
)
261 h
.setFormatter(logfmt
)
262 h
._cpbuiltin
= "screen"
265 log
.handlers
.remove(h
)
267 def _get_screen(self
):
268 h
= self
._get
_builtin
_handler
269 has_h
= h(self
.error_log
, "screen") or h(self
.access_log
, "screen")
272 def _set_screen(self
, newvalue
):
273 self
._set
_screen
_handler
(self
.error_log
, newvalue
, stream
=sys
.stderr
)
274 self
._set
_screen
_handler
(self
.access_log
, newvalue
, stream
=sys
.stdout
)
275 screen
= property(_get_screen
, _set_screen
,
276 doc
="""Turn stderr/stdout logging on or off.
278 If you set this to True, it'll add the appropriate StreamHandler for
279 you. If you set it to False, it will remove the handler.
282 # -------------------------- File handlers -------------------------- #
284 def _add_builtin_file_handler(self
, log
, fname
):
285 h
= logging
.FileHandler(fname
)
286 h
.setFormatter(logfmt
)
287 h
._cpbuiltin
= "file"
290 def _set_file_handler(self
, log
, filename
):
291 h
= self
._get
_builtin
_handler
(log
, "file")
294 if h
.baseFilename
!= os
.path
.abspath(filename
):
296 log
.handlers
.remove(h
)
297 self
._add
_builtin
_file
_handler
(log
, filename
)
299 self
._add
_builtin
_file
_handler
(log
, filename
)
303 log
.handlers
.remove(h
)
305 def _get_error_file(self
):
306 h
= self
._get
_builtin
_handler
(self
.error_log
, "file")
308 return h
.baseFilename
310 def _set_error_file(self
, newvalue
):
311 self
._set
_file
_handler
(self
.error_log
, newvalue
)
312 error_file
= property(_get_error_file
, _set_error_file
,
313 doc
="""The filename for self.error_log.
315 If you set this to a string, it'll add the appropriate FileHandler for
316 you. If you set it to ``None`` or ``''``, it will remove the handler.
319 def _get_access_file(self
):
320 h
= self
._get
_builtin
_handler
(self
.access_log
, "file")
322 return h
.baseFilename
324 def _set_access_file(self
, newvalue
):
325 self
._set
_file
_handler
(self
.access_log
, newvalue
)
326 access_file
= property(_get_access_file
, _set_access_file
,
327 doc
="""The filename for self.access_log.
329 If you set this to a string, it'll add the appropriate FileHandler for
330 you. If you set it to ``None`` or ``''``, it will remove the handler.
333 # ------------------------- WSGI handlers ------------------------- #
335 def _set_wsgi_handler(self
, log
, enable
):
336 h
= self
._get
_builtin
_handler
(log
, "wsgi")
339 h
= WSGIErrorHandler()
340 h
.setFormatter(logfmt
)
341 h
._cpbuiltin
= "wsgi"
344 log
.handlers
.remove(h
)
347 return bool(self
._get
_builtin
_handler
(self
.error_log
, "wsgi"))
349 def _set_wsgi(self
, newvalue
):
350 self
._set
_wsgi
_handler
(self
.error_log
, newvalue
)
351 wsgi
= property(_get_wsgi
, _set_wsgi
,
352 doc
="""Write errors to wsgi.errors.
354 If you set this to True, it'll add the appropriate
355 :class:`WSGIErrorHandler<cherrypy._cplogging.WSGIErrorHandler>` for you
356 (which writes errors to ``wsgi.errors``).
357 If you set it to False, it will remove the handler.
361 class WSGIErrorHandler(logging
.Handler
):
362 "A handler class which writes logging records to environ['wsgi.errors']."
365 """Flushes the stream."""
367 stream
= cherrypy
.serving
.request
.wsgi_environ
.get('wsgi.errors')
368 except (AttributeError, KeyError):
373 def emit(self
, record
):
376 stream
= cherrypy
.serving
.request
.wsgi_environ
.get('wsgi.errors')
377 except (AttributeError, KeyError):
381 msg
= self
.format(record
)
384 if not hasattr(types
, "UnicodeType"): #if no unicode support...
385 stream
.write(fs
% msg
)
388 stream
.write(fs
% msg
)
390 stream
.write(fs
% msg
.encode("UTF-8"))
393 self
.handleError(record
)