Package wtf :: Package app :: Package services :: Module log
[hide private]
[frames] | no frames]

Source Code for Module wtf.app.services.log

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006-2012 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 17  """ 
 18  Log service 
 19  =========== 
 20   
 21  The service provides a global log configuration. 
 22  """ 
 23  __author__ = u"Andr\xe9 Malo" 
 24  __docformat__ = "restructuredtext en" 
 25   
 26  import logging as _logging 
 27  import os as _os 
 28  import sys as _sys 
 29  import types as _types 
 30   
 31  from wtf import services as _services 
 32   
 33   
 34  BaseLogger = _logging.getLoggerClass() 
35 -class WtfLogger(BaseLogger):
36 """ 37 Improved logger class, which can wind up stack more than one frame 38 39 Unfortunately the logging code is not flexible enough to simply extend 40 the API, so we're actually copying the code with slight differences. 41 Always the same game :-( 42 """ 43
44 - def _log(self, level, msg, args, exc_info=None, stackwind=1):
45 """ 46 Low-level logging routine which creates a LogRecord and then calls 47 all the handlers of this logger to handle the record. 48 """ 49 # pylint: disable = W0212, C0103 50 51 if _logging._srcfile: 52 fn, lno, _ = self.findCaller(stackwind) 53 else: 54 fn, lno, _ = "(unknown file)", 0, "(unknown function)" 55 if exc_info: 56 if type(exc_info) != _types.TupleType: 57 exc_info = _sys.exc_info() 58 record = self.makeRecord( 59 self.name, level, fn, lno, msg, args, exc_info 60 ) 61 self.handle(record)
62
63 - def findCaller(self, stackwind=1):
64 """ 65 Find the stack frame of the caller so that we can note the source 66 file name, line number and function name. 67 """ 68 # pylint: disable = W0221, C0103 69 70 # + 2 = findCaller, _log 71 f, idx = _logging.currentframe(), max(1, stackwind) + 2 72 while idx: 73 if f.f_back is None: 74 break 75 f = f.f_back 76 idx -= 1 77 rv = "(unknown file)", 0, "(unknown function)" 78 while 1: 79 co = getattr(f, "f_code", None) 80 if co is None: 81 break 82 co = f.f_code 83 filename = _os.path.normcase(co.co_filename) 84 if filename == _logging._srcfile: # pylint: disable = W0212 85 f = f.f_back 86 continue 87 rv = (filename, f.f_lineno, co.co_name) 88 break 89 return rv
90
91 - def exception(self, msg, *args, **kwargs):
92 """ 93 Convenience method for logging an ERROR with exception information. 94 """ 95 kwargs['exc_info'] = 1 96 kwargs['stackwind'] = kwargs.get('stackwind', 1) + 1 97 self.error(msg, *args, **kwargs)
98 99 # Another glitch: The logger manager in the logging module doesn't seem 100 # to be able to simply take a logger instance from outside. Arrrrgh. 101 # 102 # The result is: the following two lines cannot be considered thread safe. 103 # However, since it's run during the startup phase, it shouldn't impose a 104 # problem. 105 _logging.setLoggerClass(WtfLogger) 106 _logging.getLogger('wtf') 107 108
109 -class LogService(object):
110 """ 111 Log service 112 113 The services provides a global interface to the logging facilities. 114 115 :See: `wtf.services.ServiceInterface` 116 117 :Groups: 118 - `Log levels`: `CRITICAL`, `FATAL`, `ERROR`, `WARNING`, `WARN`, `INFO` 119 `DEBUG` 120 - `Loggers`: `log`, `critical`, `fatal`, `error`, `exception`, `warning`, 121 `warn`, `info`, `debug` 122 123 :CVariables: 124 - `_DEFAULT_REC_FORMAT`: Default record format 125 - `_DEFAULT_TIME_FORMAT`: Default time format 126 - `_DEFAULT_LEVEL`: Default log level 127 - `CRITICAL`: CRITICAL log level 128 - `FATAL`: FATAL log level (== CRITICAL) 129 - `ERROR`: ERROR log level 130 - `WARNING`: WARNING log level 131 - `WARN`: WARN log level (== WARNING) 132 - `INFO`: INFO log level 133 - `DEBUG`: DEBUG log level 134 135 :IVariables: 136 - `log`: logger for all levels 137 - `critical`: critical logger 138 - `fatal`: fatal logger (== critical) 139 - `error`: error logger 140 - `exception`: error logger with exception 141 - `warning`: warning logger 142 - `warn`: warn logger (== warning) 143 - `info`: info logger 144 - `debug`: debug logger 145 146 :Types: 147 - `_DEFAULT_REC_FORMAT`: ``unicode`` 148 - `_DEFAULT_TIME_FORMAT`: ``None`` 149 - `_DEFAULT_LEVEL`: ``unicode`` 150 - `CRITICAL`: ``int`` 151 - `FATAL`: ``int`` 152 - `ERROR`: ``int`` 153 - `WARNING`: ``int`` 154 - `WARN`: ``int`` 155 - `INFO`: ``int`` 156 - `DEBUG`: ``int`` 157 - `log`: ``callable`` 158 - `critical`: ``callable`` 159 - `fatal`: ``callable`` 160 - `error`: ``callable`` 161 - `exception`: ``callable`` 162 - `warning`: ``callable`` 163 - `warn`: ``callable`` 164 - `info`: ``callable`` 165 - `debug`: ``callable`` 166 """ 167 __implements__ = [_services.ServiceInterface] 168 _DEFAULT_REC_FORMAT = \ 169 u'%(asctime)s %(levelname)s [%(filename)s:%(lineno)s] %(message)s' 170 _DEFAULT_TIME_FORMAT = None 171 _DEFAULT_LEVEL = u'WARN' 172 173 CRITICAL = _logging.CRITICAL 174 FATAL = _logging.FATAL 175 ERROR = _logging.ERROR 176 WARNING = _logging.WARNING 177 WARN = _logging.WARN 178 INFO = _logging.INFO 179 DEBUG = _logging.DEBUG 180
181 - def __init__(self, config, opts, args):
182 """ 183 Initialization 184 185 :See: `wtf.services.ServiceInterface.__init__` 186 """ 187 conf = 'log' in config and config.log or {}.get 188 rec_format = conf('record', self._DEFAULT_REC_FORMAT).encode('utf-8') 189 time_format = conf('time', self._DEFAULT_TIME_FORMAT) 190 loglevel = conf('level', self._DEFAULT_LEVEL).upper().encode('utf-8') 191 if time_format is not None: 192 time_format = time_format.encode('utf-8') 193 194 handler = _logging.StreamHandler(_sys.stderr) 195 handler.setFormatter(_logging.Formatter(rec_format, time_format)) 196 level = _logging.getLevelName(loglevel) 197 198 logger = _logging.getLogger('wtf') 199 logger.addHandler(handler) 200 logger.setLevel(level) 201 202 # populate symbols to service 203 methlist = ('debug', 'info', 'warning', 'warn', 'error', 204 'exception', 'critical', 'fatal', 'log') 205 for method in methlist: 206 setattr(self, method, getattr(logger, method))
207
208 - def shutdown(self):
209 """ :See: `wtf.services.ServiceInterface.shutdown` """ 210 pass
211
212 - def global_service(self):
213 """ :See: `wtf.services.ServiceInterface.global_service` """ 214 return 'wtf.log', self
215
216 - def middleware(self, func):
217 """ :See: `wtf.services.ServiceInterface.middleware` """ 218 return func
219
220 - def __call__(self, msg, level=ERROR, *args, **kwargs):
221 """ 222 Shortcut for log.log() with easier signature 223 224 :Parameters: 225 - `msg`: Log message 226 - `level`: Log level 227 - `args`: Additional arguments 228 - `kwargs`: Additional keyword arguments 229 230 :Types: 231 - `msg`: ``str`` 232 - `level`: ``int`` 233 - `args`: ``tuple`` 234 - `kwargs`: ``dict`` 235 236 :return: Whatever ``self.log()`` returns 237 :rtype: any 238 """ 239 kwargs['stackwind'] = kwargs.get('stackwind', 1) + 1 240 return self.log(level, msg, *args, **kwargs) # pylint: disable = E1101
241