Package cherrypy :: Module _cplogging
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cplogging

  1  """CherryPy logging.""" 
  2   
  3  import datetime 
  4  import logging 
  5  # Silence the no-handlers "warning" (stderr write!) in stdlib logging 
  6  logging.Logger.manager.emittedNoHandlerWarning = 1 
  7  logfmt = logging.Formatter("%(message)s") 
  8  import os 
  9  import rfc822 
 10  import sys 
 11   
 12  import cherrypy 
 13  from cherrypy import _cperror 
 14   
 15   
16 -class LogManager(object):
17 18 appid = None 19 error_log = None 20 access_log = None 21 access_log_format = \ 22 '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' 23
24 - def __init__(self, appid=None, logger_root="cherrypy"):
25 self.logger_root = logger_root 26 self.appid = appid 27 if appid is None: 28 self.error_log = logging.getLogger("%s.error" % logger_root) 29 self.access_log = logging.getLogger("%s.access" % logger_root) 30 else: 31 self.error_log = logging.getLogger("%s.error.%s" % (logger_root, appid)) 32 self.access_log = logging.getLogger("%s.access.%s" % (logger_root, appid)) 33 self.error_log.setLevel(logging.DEBUG) 34 self.access_log.setLevel(logging.INFO) 35 cherrypy.engine.subscribe('graceful', self.reopen_files)
36
37 - def reopen_files(self):
38 """Close and reopen all file handlers.""" 39 for log in (self.error_log, self.access_log): 40 for h in log.handlers: 41 if isinstance(h, logging.FileHandler): 42 h.acquire() 43 h.stream.close() 44 h.stream = open(h.baseFilename, h.mode) 45 h.release()
46
47 - def error(self, msg='', context='', severity=logging.INFO, traceback=False):
48 """Write to the error log. 49 50 This is not just for errors! Applications may call this at any time 51 to log application-specific information. 52 """ 53 if traceback: 54 msg += _cperror.format_exc() 55 self.error_log.log(severity, ' '.join((self.time(), context, msg)))
56
57 - def __call__(self, *args, **kwargs):
58 """Write to the error log. 59 60 This is not just for errors! Applications may call this at any time 61 to log application-specific information. 62 """ 63 return self.error(*args, **kwargs)
64
65 - def access(self):
66 """Write to the access log (in Apache/NCSA Combined Log format). 67 68 Like Apache started doing in 2.0.46, non-printable and other special 69 characters in %r (and we expand that to all parts) are escaped using 70 \\xhh sequences, where hh stands for the hexadecimal representation 71 of the raw byte. Exceptions from this rule are " and \\, which are 72 escaped by prepending a backslash, and all whitespace characters, 73 which are written in their C-style notation (\\n, \\t, etc). 74 """ 75 request = cherrypy.request 76 remote = request.remote 77 response = cherrypy.response 78 outheaders = response.headers 79 inheaders = request.headers 80 81 atoms = {'h': remote.name or remote.ip, 82 'l': '-', 83 'u': getattr(request, "login", None) or "-", 84 't': self.time(), 85 'r': request.request_line, 86 's': response.status.split(" ", 1)[0], 87 'b': outheaders.get('Content-Length', '') or "-", 88 'f': inheaders.get('Referer', ''), 89 'a': inheaders.get('User-Agent', ''), 90 } 91 for k, v in atoms.items(): 92 if isinstance(v, unicode): 93 v = v.encode('utf8') 94 elif not isinstance(v, str): 95 v = str(v) 96 # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc 97 # and backslash for us. All we have to do is strip the quotes. 98 v = repr(v)[1:-1] 99 # Escape double-quote. 100 atoms[k] = v.replace('"', '\\"') 101 102 try: 103 self.access_log.log(logging.INFO, self.access_log_format % atoms) 104 except: 105 self(traceback=True)
106
107 - def time(self):
108 """Return now() in Apache Common Log Format (no timezone).""" 109 now = datetime.datetime.now() 110 month = rfc822._monthnames[now.month - 1].capitalize() 111 return ('[%02d/%s/%04d:%02d:%02d:%02d]' % 112 (now.day, month, now.year, now.hour, now.minute, now.second))
113
114 - def _get_builtin_handler(self, log, key):
115 for h in log.handlers: 116 if getattr(h, "_cpbuiltin", None) == key: 117 return h
118 119 120 # ------------------------- Screen handlers ------------------------- # 121
122 - def _set_screen_handler(self, log, enable, stream=None):
123 h = self._get_builtin_handler(log, "screen") 124 if enable: 125 if not h: 126 if stream is None: 127 stream=sys.stderr 128 h = logging.StreamHandler(stream) 129 h.setFormatter(logfmt) 130 h._cpbuiltin = "screen" 131 log.addHandler(h) 132 elif h: 133 log.handlers.remove(h)
134
135 - def _get_screen(self):
136 h = self._get_builtin_handler 137 has_h = h(self.error_log, "screen") or h(self.access_log, "screen") 138 return bool(has_h)
139
140 - def _set_screen(self, newvalue):
141 self._set_screen_handler(self.error_log, newvalue, stream=sys.stderr) 142 self._set_screen_handler(self.access_log, newvalue)
143 screen = property(_get_screen, _set_screen, 144 doc="If True, error and access will print to stderr.") 145 146 147 # -------------------------- File handlers -------------------------- # 148
149 - def _add_builtin_file_handler(self, log, fname):
150 h = logging.FileHandler(fname) 151 h.setFormatter(logfmt) 152 h._cpbuiltin = "file" 153 log.addHandler(h)
154
155 - def _set_file_handler(self, log, filename):
156 h = self._get_builtin_handler(log, "file") 157 if filename: 158 if h: 159 if h.baseFilename != os.path.abspath(filename): 160 h.close() 161 log.handlers.remove(h) 162 self._add_builtin_file_handler(log, filename) 163 else: 164 self._add_builtin_file_handler(log, filename) 165 else: 166 if h: 167 h.close() 168 log.handlers.remove(h)
169
170 - def _get_error_file(self):
171 h = self._get_builtin_handler(self.error_log, "file") 172 if h: 173 return h.baseFilename 174 return ''
175 - def _set_error_file(self, newvalue):
176 self._set_file_handler(self.error_log, newvalue)
177 error_file = property(_get_error_file, _set_error_file, 178 doc="The filename for self.error_log.") 179
180 - def _get_access_file(self):
181 h = self._get_builtin_handler(self.access_log, "file") 182 if h: 183 return h.baseFilename 184 return ''
185 - def _set_access_file(self, newvalue):
186 self._set_file_handler(self.access_log, newvalue)
187 access_file = property(_get_access_file, _set_access_file, 188 doc="The filename for self.access_log.") 189 190 191 # ------------------------- WSGI handlers ------------------------- # 192
193 - def _set_wsgi_handler(self, log, enable):
194 h = self._get_builtin_handler(log, "wsgi") 195 if enable: 196 if not h: 197 h = WSGIErrorHandler() 198 h.setFormatter(logfmt) 199 h._cpbuiltin = "wsgi" 200 log.addHandler(h) 201 elif h: 202 log.handlers.remove(h)
203
204 - def _get_wsgi(self):
205 return bool(self._get_builtin_handler(self.error_log, "wsgi"))
206
207 - def _set_wsgi(self, newvalue):
208 self._set_wsgi_handler(self.error_log, newvalue)
209 wsgi = property(_get_wsgi, _set_wsgi, 210 doc="If True, error messages will be sent to wsgi.errors.")
211 212
213 -class WSGIErrorHandler(logging.Handler):
214 "A handler class which writes logging records to environ['wsgi.errors']." 215
216 - def flush(self):
217 """Flushes the stream.""" 218 try: 219 stream = cherrypy.request.wsgi_environ.get('wsgi.errors') 220 except (AttributeError, KeyError): 221 pass 222 else: 223 stream.flush()
224
225 - def emit(self, record):
226 """Emit a record.""" 227 try: 228 stream = cherrypy.request.wsgi_environ.get('wsgi.errors') 229 except (AttributeError, KeyError): 230 pass 231 else: 232 try: 233 msg = self.format(record) 234 fs = "%s\n" 235 import types 236 if not hasattr(types, "UnicodeType"): #if no unicode support... 237 stream.write(fs % msg) 238 else: 239 try: 240 stream.write(fs % msg) 241 except UnicodeError: 242 stream.write(fs % msg.encode("UTF-8")) 243 self.flush() 244 except: 245 self.handleError(record)
246