1
2
3
4
5
6
7
8
9 """Verbose output and debugging facility
10
11 Examples:
12 from verbosity import verbose, debug; debug.active = [1,2,3]; debug(1, "blah")
13
14 """
15
16 __docformat__ = 'restructuredtext'
17
18 from sys import stdout, stderr
19
20
21
22
23
24
25
26
27
28
30 """
31 Base class to provide logging
32 """
33
35 """Initialize the logger with a set of handlers to use for output
36
37 Each hanlder must have write() method implemented
38 """
39 if handlers == None:
40 handlers = [stdout]
41 self.__close_handlers = []
42 self.__handlers = []
43 self._setHandlers(handlers)
44 self.__lfprev = True
45 self.__crprev = 0
46
49
51 """Set list of handlers for the log.
52
53 A handler can be opened files, stdout, stderr, or a string, which
54 will be considered a filename to be opened for writing
55 """
56 handlers_ = []
57 self._closeOpenedHandlers()
58 for handler in handlers:
59 if isinstance(handler, basestring):
60 try:
61 handler = {'stdout' : stdout,
62 'stderr' : stderr}[handler.lower()]
63 except:
64 try:
65 handler = open(handler, 'w')
66 self.__close_handlers.append(handler)
67 except:
68 raise RuntimeError, \
69 "Cannot open file %s for writing by the logger" \
70 % handler
71 handlers_.append(handler)
72 self.__handlers = handlers_
73
75 """Close opened handlers (such as opened logfiles
76 """
77 for handler in self.__close_handlers:
78 handler.close()
79
81 """Return active handlers
82 """
83 return self.__handlers
84
85
86 - def __call__(self, msg, lf=True, cr=False, *args, **kwargs):
87 """
88 Write msg to each of the handlers.
89
90 It can append a newline (lf = Line Feed) or return
91 to the beginning before output and to take care about
92 cleaning previous message if present
93
94 it appends a newline (lf = Line Feed) since most commonly each
95 call is a separate message
96 """
97 if kwargs.has_key('msgargs'):
98 msg = msg % kwargs['msgargs']
99
100 if cr:
101 msg_ = ""
102 if self.__crprev > 0:
103
104 msg_ = "\r%s" % (" "*self.__crprev)
105 msg_ += "\r" + msg
106 self.__crprev = len(msg)
107 msg = msg_
108
109
110 lf = False
111 else:
112 self.__crprev += len(msg)
113
114 if lf:
115 msg = msg + "\n"
116 self.__crprev = 0
117
118 for handler in self.__handlers:
119 try:
120 handler.write(msg)
121 except:
122 print "Failed writing on handler %s" % handler
123 raise
124 try:
125 handler.flush()
126 except:
127
128 pass
129
130 self.__lfprev = lf
131
132 handlers = property(fget=_getHandlers, fset=_setHandlers)
133 lfprev = property(fget=lambda self:self.__lfprev)
134
135
136
138 """
139 Logger which prints based on level -- ie everything which is smaller
140 than specified level
141 """
142
143 - def __init__(self, level=0, indent=" ", *args, **kwargs):
144 """Define level logger.
145
146 It is defined by
147 `level`, int: to which messages are reported
148 `indent`, string: symbol used to indent
149 """
150 Logger.__init__(self, *args, **kwargs)
151 self.__level = level
152 self.__indent = indent
153 self._setLevel(level)
154 self._setIndent(indent)
155
157 """Set logging level
158 """
159 ilevel = int(level)
160 if ilevel < 0:
161 raise ValueError, \
162 "Negative verbosity levels (got %d) are not supported" \
163 % ilevel
164 self.__level = ilevel
165
166
168 """Either to indent the lines based on message log level"""
169 self.__indent = "%s" % indent
170
171
172 - def __call__(self, level, msg, *args, **kwargs):
173 """
174 Write msg and indent using self.indent it if it was requested
175
176 it appends a newline since most commonly each call is a separate
177 message
178 """
179 if level <= self.level:
180 if self.lfprev and self.indent:
181
182 msg = self.indent*level + msg
183 Logger.__call__(self, msg, *args, **kwargs)
184
185 level = property(fget=lambda self: self.__level, fset=_setLevel)
186 indent = property(fget=lambda self: self.__indent, fset=_setIndent)
187
188
190 """
191 Logger which prints a message for a given ID just once.
192
193 It could be used for one-time warning to don't overfill the output
194 with useless repeatative messages
195 """
196
198 """Define once logger.
199 """
200 Logger.__init__(self, *args, **kwargs)
201 self._known = {}
202
203
204 - def __call__(self, ident, msg, count=1, *args, **kwargs):
205 """
206 Write `msg` if `ident` occured less than `count` times by now.
207 """
208 if not self._known.has_key(ident):
209 self._known[ident] = 0
210
211 if count < 0 or self._known[ident] < count:
212 self._known[ident] += 1
213 Logger.__call__(self, msg, *args, **kwargs)
214
215
217 """
218 Logger which prints based on defined sets identified by Id.
219 """
220
221 - def __init__(self, register=None, active=None, printsetid=True,
222 *args, **kwargs):
233
234
236 """Set active logging set
237 """
238
239
240 from mvpa.base import verbose
241 self.__active = []
242 registered_keys = self.__registered.keys()
243 for item in list(set(active)):
244 if item == '':
245 continue
246 if isinstance(item, basestring):
247 if item in ['?', 'list', 'help']:
248 self.print_registered(detailed=(item != '?'))
249 raise SystemExit(0)
250 if item.upper() == "ALL":
251 verbose(2, "Enabling all registered debug handlers")
252 self.__active = registered_keys
253 break
254
255 regexp_str = "^%s$" % item
256 try:
257 regexp = re.compile(regexp_str)
258 except:
259 raise ValueError, \
260 "Unable to create regular expression out of %s" % item
261 matching_keys = filter(regexp.match, registered_keys)
262 toactivate = matching_keys
263 if len(toactivate) == 0:
264 ids = self.registered.keys()
265 ids.sort()
266 raise ValueError, \
267 "Unknown debug ID '%s' was asked to become active," \
268 " or regular expression '%s' did not get any match" \
269 " among known ids: %s" \
270 % (item, regexp_str, ids)
271 else:
272 toactivate = [item]
273
274
275 for item_ in toactivate:
276 if not (item_ in registered_keys):
277 raise ValueError, \
278 "Unknown debug ID %s was asked to become active" \
279 % item_
280 self.__active += toactivate
281
282 self.__active = list(set(self.__active))
283 self.__maxstrlength = max([len(str(x)) for x in self.__active] + [0])
284 if len(self.__active):
285 verbose(2, "Enabling debug handlers: %s" % `self.__active`)
286
287
289 """Either to print set Id at each line"""
290 self.__printsetid = printsetid
291
292
293 - def __call__(self, setid, msg, *args, **kwargs):
294 """
295 Write msg
296
297 It appends a newline since most commonly each call is a separate
298 message
299 """
300
301 if setid in self.__active:
302 if len(msg)>0 and self.__printsetid:
303 msg = "[%%-%ds] " % self.__maxstrlength % (setid) + msg
304 Logger.__call__(self, msg, *args, **kwargs)
305
306
307 - def register(self, setid, description):
308 """ "Register" a new setid with a given description for easy finding
309 """
310
311 if self.__registered.has_key(setid):
312 raise ValueError, \
313 "Setid %s is already known with description '%s'" % \
314 ( `setid`, self.__registered[setid] )
315 self.__registered[setid] = description
316
317
319 """Given a string listing registered(?) setids, make then active
320 """
321
322
323 self.active = value.split(",")
324
325
327 print "Registered debug entries: ",
328 kd = self.registered
329 rks = sorted(kd.keys())
330 maxl = max([len(k) for k in rks])
331 if not detailed:
332
333 print ', '.join(rks)
334 else:
335 print
336 for k in rks:
337 print '%%%ds %%s' % maxl % (k, kd[k])
338
339
340 printsetid = property(fget=lambda self: self.__printsetid, \
341 fset=_setPrintsetid)
342 active = property(fget=lambda self: self.__active, fset=_setActive)
343 registered = property(fget=lambda self: self.__registered)
344
345
346 if __debug__:
347
348 import os, re
349 import traceback
350 import time
351 from os import getpid
352 from os.path import basename, dirname
353
355 """Return stat information on current process.
356
357 Usually it is needed to know where the memory is gone, that is
358 why VmSize is the default for the field to spit out
359 TODO: Spit out multiple fields. Use some better way than parsing proc
360 """
361
362 fd = open('/proc/%d/status' % getpid())
363 lines = fd.readlines()
364 fd.close()
365 match = filter(lambda x:re.match('^%s:'%field, x), lines)[0].strip()
366 match = re.sub('[ \t]+', ' ', match)
367 return match
368
370 """Custom function to include directory name if filename is too common
371
372 Also strip .py at the end
373 """
374 base = basename(s).rstrip('py').rstrip('.')
375 if base in ['base', '__init__']:
376 base = basename(dirname(s)) + '.' + base
377 return base
378
381 """Initialize TrackBack metric
382
383 :Parameters:
384 collide : bool
385 if True then prefix common with previous invocation gets
386 replaced with ...
387 """
388 self.__prev = ""
389 self.__collide = collide
390
392 ftb = traceback.extract_stack(limit=100)[:-2]
393 entries = [[mbasename(x[0]), str(x[1])] for x in ftb]
394 entries = filter(lambda x:x[0] != 'unittest', entries)
395
396
397 entries_out = [entries[0]]
398 for entry in entries[1:]:
399 if entry[0] == entries_out[-1][0]:
400 entries_out[-1][1] += ',%s' % entry[1]
401 else:
402 entries_out.append(entry)
403 sftb = '>'.join(['%s:%s' % (mbasename(x[0]),
404 x[1]) for x in entries_out])
405 if self.__collide:
406
407 prev_next = sftb
408 common_prefix = os.path.commonprefix((self.__prev, sftb))
409 common_prefix2 = re.sub('>[^>]*$', '', common_prefix)
410
411 if common_prefix2 != "":
412 sftb = '...' + sftb[len(common_prefix2):]
413 self.__prev = prev_next
414
415 return sftb
416
417
419 """Simple helper class to provide relative time it took from previous
420 invocation"""
421
422 - def __init__(self, format="%3.3f sec"):
423 self.__prev = None
424 self.__format = format
425
427 dt = 0.0
428 ct = time.time()
429 if not self.__prev is None:
430 dt = ct - self.__prev
431 self.__prev = ct
432 return self.__format % dt
433
434
436 """
437 Logger for debugging purposes.
438
439 Expands SetLogger with ability to print some interesting information
440 (named Metric... XXX) about current process at each debug printout
441 """
442
443 _known_metrics = {
444 'vmem' : lambda : parseStatus(field='VmSize'),
445 'pid' : lambda : parseStatus(field='Pid'),
446 'asctime' : time.asctime,
447 'tb' : TraceBack(),
448 'tbc' : TraceBack(collide=True),
449 }
450
451
452 - def __init__(self, metrics=None, offsetbydepth=True, *args, **kwargs):
463
464
466 """Register some metric to report
467
468 func can be either a function call or a string which should
469 correspond to known metrics
470 """
471
472 if isinstance(func, basestring):
473 if func in ['all', 'ALL']:
474 func = self._known_metrics.keys()
475
476 if isinstance(func, basestring):
477 if DebugLogger._known_metrics.has_key(func):
478 func = DebugLogger._known_metrics[func]
479 else:
480 if func in ['?', 'list', 'help']:
481 print 'Known debug metrics: ', \
482 ', '.join(DebugLogger._known_metrics.keys())
483 raise SystemExit(0)
484 else:
485 raise ValueError, \
486 "Unknown name %s for metric in DebugLogger" % \
487 func + " Known metrics are " + \
488 `DebugLogger._known_metrics.keys()`
489 elif isinstance(func, list):
490 self.__metrics = []
491 for item in func:
492 self.registerMetric(item)
493 return
494
495 if not func in self.__metrics:
496 try:
497 from mvpa.base import debug
498 debug("DBG", "Registering metric %s" % func)
499 self.__metrics.append(func)
500 except:
501 pass
502
503
504 - def __call__(self, setid, msg, *args, **kwargs):
505
506 if not self.registered.has_key(setid):
507 raise ValueError, "Not registered debug ID %s" % setid
508
509 if not setid in self.active:
510
511
512 return
513
514 if len(msg) > 0:
515 msg_ = ' / '.join([str(x()) for x in self.__metrics])
516
517 if len(msg_)>0:
518 msg_ = "{%s}" % msg_
519
520
521 if self._offsetbydepth:
522 level = len(traceback.extract_stack())-2
523 else:
524 level = 1
525
526 if len(msg)>250 and 'DBG' in self.active and not setid.endswith('_TB'):
527 tb = traceback.extract_stack(limit=2)
528 msg += " !!!2LONG!!!. From %s" % str(tb[0])
529
530 msg = "DBG%s:%s%s" % (msg_, " "*level, msg)
531
532 SetLogger.__call__(self, setid, msg, *args, **kwargs)
533
534
536 self._offsetbydepth = b
537
538 offsetbydepth = property(fget=lambda x:x._offsetbydepth,
539 fset=_setOffsetByDepth)
540
541 metrics = property(fget=lambda x:x.__metrics,
542 fset=registerMetric)
543