1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()
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
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
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
69
70
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:
85 f = f.f_back
86 continue
87 rv = (filename, f.f_lineno, co.co_name)
88 break
89 return rv
90
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
100
101
102
103
104
105 _logging.setLoggerClass(WtfLogger)
106 _logging.getLogger('wtf')
107
108
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
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
209 """ :See: `wtf.services.ServiceInterface.shutdown` """
210 pass
211
213 """ :See: `wtf.services.ServiceInterface.global_service` """
214 return 'wtf.log', self
215
217 """ :See: `wtf.services.ServiceInterface.middleware` """
218 return func
219
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)
241