1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2002-2008 Hewlett-Packard Development Company, L.P.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # Authors: Doug Deprenger, Don Welch
24 import thread # TODO: Use threading instead (thread deprecated in Python 3.0)
32 identity = string.maketrans('','')
33 unprintable = identity.translate(identity, string.printable)
36 return s.translate(identity, unprintable)
38 DEFAULT_LOG_LEVEL = 'info'
54 logging_levels = {'none' : LOG_LEVEL_NONE,
55 'fata' : LOG_LEVEL_FATAL,
56 'fatal' : LOG_LEVEL_FATAL,
57 'erro' : LOG_LEVEL_ERROR,
58 'error' : LOG_LEVEL_ERROR,
59 'warn' : LOG_LEVEL_WARN,
60 'info' : LOG_LEVEL_INFO,
61 'debug' : LOG_LEVEL_DEBUG,
62 'dbg' : LOG_LEVEL_DEBUG,
63 'debug2' : LOG_LEVEL_DEBUG2,
64 'dbg2' : LOG_LEVEL_DEBUG2,
65 'debug3' : LOG_LEVEL_DEBUG3,
66 'dbg3' : LOG_LEVEL_DEBUG3,
73 LOG_TO_CONSOLE_AND_FILE = 3
76 # Copied from Gentoo Portage output.py
77 # Copyright 1998-2003 Daniel Robbins, Gentoo Technologies, Inc.
78 # Distributed under the GNU Public License v2
80 codes["reset"]="\x1b[0m"
81 codes["bold"]="\x1b[01m"
83 codes["teal"]="\x1b[36;06m"
84 codes["turquoise"]="\x1b[36;01m"
86 codes["fuscia"]="\x1b[35;01m"
87 codes["purple"]="\x1b[35;06m"
89 codes["blue"]="\x1b[34;01m"
90 codes["darkblue"]="\x1b[34;06m"
92 codes["green"]="\x1b[32;01m"
93 codes["darkgreen"]="\x1b[32;06m"
95 codes["yellow"]="\x1b[33;01m"
96 codes["brown"]="\x1b[33;06m"
98 codes["red"]="\x1b[31;01m"
99 codes["darkred"]="\x1b[31;06m"
102 def __init__(self, module='', level=LOG_LEVEL_INFO, where=LOG_TO_CONSOLE_AND_FILE,
103 log_datetime=False, log_file=None):
106 self._log_file = log_file
107 self._log_file_f = None
108 self._log_datetime = log_datetime
109 self._lock = thread.allocate_lock()
111 self.pid = os.getpid()
113 self.set_level(level)
116 def set_level(self, level):
117 if isinstance(level, str):
118 level = level.lower()
119 if level in Logger.logging_levels.keys():
120 self._level = Logger.logging_levels.get(level, Logger.LOG_LEVEL_INFO)
123 self.error("Invalid logging level: %s" % level)
126 elif isinstance(level, int):
127 if Logger.LOG_LEVEL_DEBUG3 <= level <= Logger.LOG_LEVEL_FATAL:
130 self._level = Logger.LOG_LEVEL_ERROR
131 self.error("Invalid logging level: %d" % level)
138 def set_module(self, module):
140 self.pid = os.getpid()
143 def no_formatting(self):
147 def set_logfile(self, log_file):
148 self._log_file = log_file
150 self._log_file_f = file(self._log_file, 'w')
152 self._log_file = None
153 self._log_file_f = None
154 self._where = Logger.LOG_TO_SCREEN
157 def get_logfile(self):
158 return self._log_file
161 def set_where(self, where):
170 return self._level <= Logger.LOG_LEVEL_DEBUG3
172 level = property(get_level, set_level)
175 def log(self, message, level, newline=True):
176 if self._level <= level:
177 if self._where in (Logger.LOG_TO_CONSOLE, Logger.LOG_TO_CONSOLE_AND_FILE):
180 if level >= Logger.LOG_LEVEL_WARN:
187 except UnicodeEncodeError:
188 out.write(message.encode("utf-8"))
198 def log_to_file(self, message):
199 if self._log_file_f is not None:
202 self._log_file_f.write(message.replace('\x1b', ''))
203 self._log_file_f.write('\n')
209 def stderr(self, message):
212 sys.stderr.write("%s: %s\n" % (self.module, message))
217 def debug(self, message):
218 if self._level <= Logger.LOG_LEVEL_DEBUG:
219 txt = "%s[%d]: debug: %s" % (self.module, self.pid, message)
220 self.log(self.color(txt, 'blue'), Logger.LOG_LEVEL_DEBUG)
222 if self._log_file is not None and \
223 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
224 self.log_to_file(txt)
228 def debug2(self, message):
229 if self._level <= Logger.LOG_LEVEL_DEBUG2:
230 txt = "%s[%d]: debug2: %s" % (self.module, self.pid, message)
231 self.log(self.color(txt, 'blue'), Logger.LOG_LEVEL_DEBUG2)
233 if self._log_file is not None and \
234 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
235 self.log_to_file(txt)
238 def debug3(self, message):
239 if self._level <= Logger.LOG_LEVEL_DEBUG3:
240 txt = "%s[%d]: debug3: %s" % (self.module, self.pid, message)
241 self.log(self.color(txt, 'blue'), Logger.LOG_LEVEL_DEBUG3)
243 if self._log_file is not None and \
244 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
245 self.log_to_file(txt)
249 def debug_block(self, title, block):
250 if self._level <= Logger.LOG_LEVEL_DEBUG:
251 line = "%s[%d]: debug: %s:" % (self.module, self.pid, title)
252 self.log(self.color(line, 'blue'), Logger.LOG_LEVEL_DEBUG)
253 self.log(self.color(block, 'blue'), Logger.LOG_LEVEL_DEBUG)
255 if self._log_file is not None and \
256 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
258 self.log_to_file(line % (self.module, self.pid, title))
259 self.log_to_file(block)
262 printable = """ !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ """
264 def log_data(self, data, width=16):
265 if self._level <= Logger.LOG_LEVEL_DEBUG:
267 index, line = 0, data[0:width]
269 txt = ' '.join(['%04x: ' % index, ' '.join(['%02x' % ord(d) for d in line]),
270 ' '*(width*3-3*len(line)), ''.join([('.', i)[i in Logger.printable] for i in line])])
272 self.log(self.color("%s[%d]: debug: %s" % (self.module, self.pid, txt), 'blue'),
273 Logger.LOG_LEVEL_DEBUG)
276 line = data[index:index+width]
278 self.log(self.color("%s[%d]: debug: %s" % (self.module, self.pid, "0000: (no data)"), 'blue'),
279 Logger.LOG_LEVEL_DEBUG)
282 def info(self, message=''):
283 if self._level <= Logger.LOG_LEVEL_INFO:
284 self.log(message, Logger.LOG_LEVEL_INFO)
286 if self._log_file is not None and \
287 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
288 self.log_to_file("%s[%d]: info: :%s" % (self.module, self.pid, message))
293 def warn(self, message):
294 if self._level <= Logger.LOG_LEVEL_WARN:
295 txt = "warning: %s" % message.encode('utf-8')
296 self.log(self.color(txt, 'fuscia'), Logger.LOG_LEVEL_WARN)
298 syslog.syslog(syslog.LOG_WARNING, "%s[%d]: %s" % (self.module, self.pid, txt))
300 if self._log_file is not None and \
301 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
302 self.log_to_file(txt)
307 def note(self, message):
308 if self._level <= Logger.LOG_LEVEL_WARN:
309 txt = "note: %s" % message
310 self.log(self.color(txt, 'green'), Logger.LOG_LEVEL_WARN)
312 if self._log_file is not None and \
313 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
314 self.log_to_file(txt)
319 def error(self, message):
320 if self._level <= Logger.LOG_LEVEL_ERROR:
321 txt = "error: %s" % message.encode("utf-8")
322 self.log(self.color(txt, 'red'), Logger.LOG_LEVEL_ERROR)
324 syslog.syslog(syslog.LOG_ALERT, "%s[%d]: %s" % (self.module, self.pid, txt))
326 if self._log_file is not None and \
327 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
328 self.log_to_file(txt)
331 def fatal(self, message):
332 if self._level <= Logger.LOG_LEVEL_FATAL:
333 txt = "fatal error: :%s" % self.module.encode('utf-8')
334 self.log(self.color(txt, 'red'), Logger.LOG_LEVEL_DEBUG)
336 syslog.syslog(syslog.LOG_ALERT, "%s[%d]: %s" % (self.module, self.pid, txt))
338 if self._log_file is not None and \
339 self._where in (Logger.LOG_TO_FILE, Logger.LOG_TO_CONSOLE_AND_FILE):
340 self.log_to_file(txt)
344 typ, value, tb = sys.exc_info()
345 body = "Traceback (innermost last):\n"
346 lst = traceback.format_tb(tb) + traceback.format_exception_only(typ, value)
347 body = body + "%-20s %s" % (''.join(lst[:-1]), lst[-1],)
351 def color(self, text, color):
353 return ''.join([Logger.codes.get(color, 'bold'), text, Logger.codes['reset']])
357 def bold(self, text):
358 return self.color(text, 'bold')
362 return self.color(text, 'red')
365 def green(self, text):
366 return self.color(text, 'green')
369 def purple(self, text):
370 return self.color(text, 'purple')
373 def yellow(self, text):
374 return self.color(text, 'yellow')
377 def darkgreen(self, text):
378 return self.color(text, 'darkgreen')
381 def blue(self, text):
382 return self.color(text, 'blue')
384 """Pretty print an XML document.
387 Copyright (c) 2008, Fredrik Ekholdt
390 Redistribution and use in source and binary forms, with or without
391 modification, are permitted provided that the following conditions are met:
393 * Redistributions of source code must retain the above copyright notice,
394 this list of conditions and the following disclaimer.
396 * Redistributions in binary form must reproduce the above copyright notice,
397 this list of conditions and the following disclaimer in the documentation
398 and/or other materials provided with the distribution.
400 * Neither the name of None nor the names of its contributors may be used to
401 endorse or promote products derived from this software without specific prior
404 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
405 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
406 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
407 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
408 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
409 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
410 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
411 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
412 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
413 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
414 POSSIBILITY OF SUCH DAMAGE."""
416 def _pprint_line(self, indent_level, line, width=100, level=LOG_LEVEL_DEBUG):
420 for l in range(indent_level):
422 number_chars = number_chars + 1
424 elem_start = re.findall("(\<\W{0,1}\w+) ?", line)[0]
425 elem_finished = re.findall("([?|\]\]]*\>)", line)[0]
427 attrs = re.findall("(\S*?\=\".*?\")", line)
428 #output.write(start + elem_start)
429 self.log(start+elem_start, level, False)
430 number_chars = len(start + elem_start)
432 if (attrs.index(attr) + 1) == len(attrs):
433 number_chars = number_chars + len(elem_finished)
435 if (number_chars + len(attr) + 1) > width:
437 self.log("\n", level, False)
438 #for i in range(len(start + elem_start) + 1):
440 #self.log(" ", level, False)
441 self.log(" "*(len(start + elem_start) + 1), level, False)
442 number_chars = len(start + elem_start) + 1
446 self.log(" ", level, False)
447 number_chars = number_chars + 1
449 self.log(attr, level, False)
450 number_chars = number_chars + len(attr)
451 #output.write(elem_finished + "\n")
452 self.log(elem_finished + "\n", level, False)
455 #give up pretty print this line
456 #output.write(start + line + "\n")
457 self.log(start + line + "\n", level, False)
460 def _pprint_elem_content(self, indent_level, line, level=LOG_LEVEL_DEBUG):
462 #for l in range(indent_level):
464 #self.log(" ", level, False)
465 self.log(" "*indent_level, level, False)
467 #output.write(line + "\n")
468 self.log(line + "\n", level, False)
471 def _get_next_elem(self, data):
472 start_pos = data.find("<")
473 end_pos = data.find(">") + 1
474 retval = data[start_pos:end_pos]
475 stopper = retval.rfind("/")
476 if stopper < retval.rfind("\""):
479 single = (stopper > -1 and ((retval.find(">") - stopper) < (stopper - retval.find("<"))))
481 ignore_excl = retval.find("<!") > -1
482 ignore_question = retval.find("<?") > -1
485 cdata = retval.find("<![CDATA[") > -1
487 end_pos = data.find("]]>")
489 end_pos = end_pos + len("]]>")
491 elif ignore_question:
492 end_pos = data.find("?>") + len("?>")
494 ignore = ignore_excl or ignore_question
496 no_indent = ignore or single
498 #print retval, end_pos, start_pos, stopper > -1, no_indent
505 def xml(self, xml, level=LOG_LEVEL_DEBUG, indent=4, width=80):
507 Use indent to select indentation level. Default is 4 """
510 start_pos, end_pos, is_stop, no_indent = self._get_next_elem(data)
511 while ((start_pos > -1 and end_pos > -1)):
512 self._pprint_elem_content(indent_level, data[:start_pos].strip(), level=level)
513 data = data[start_pos:]
514 if is_stop and not no_indent:
515 indent_level = indent_level - indent
517 self._pprint_line(indent_level,
518 data[:end_pos - start_pos],
519 width=width, level=level)
521 data = data[end_pos - start_pos:]
522 if not is_stop and not no_indent :
523 indent_level = indent_level + indent
528 start_pos, end_pos, is_stop, no_indent = self._get_next_elem(data)
530 # END: Pretty print an XML document.
533 def pprint(self, data):
534 self.info(pprint.pformat(data))