#!/usr/bin/env python -tt # vim: ai ts=4 sts=4 et sw=4 # # Copyright (c) 2009, 2010, 2011 Intel, Inc. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; version 2 of the License # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA 02111-1307, USA. import os import sys import re import time __ALL__ = ['get_loglevel', 'set_loglevel', 'set_logfile', 'debug', 'verbose', 'info', 'warning', 'error', ] # COLORs in ANSI INFO_COLOR = 32 # green WARN_COLOR = 33 # yellow ERR_COLOR = 31 # red ASK_COLOR = 34 # blue NO_COLOR = 0 PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S) INTERACTIVE = True LOG_LEVEL = 1 LOG_LEVELS = { 'quiet': 0, 'normal': 1, 'verbose': 2, 'debug': 3, 'never': 4, } LOG_FILE_FP = None LOG_CONTENT = '' CATCHERR_BUFFILE_FD = -1 CATCHERR_BUFFILE_PATH = None CATCHERR_SAVED_2 = -1 def _general_print(head, color, msg=None, stream=None, level='normal'): global LOG_CONTENT if not stream: stream = sys.stdout if LOG_LEVELS[level] > LOG_LEVEL: # skip return errormsg = '' if CATCHERR_BUFFILE_FD > 0: size = os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_END) os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET) errormsg = os.read(CATCHERR_BUFFILE_FD, size) os.ftruncate(CATCHERR_BUFFILE_FD, 0) # append error msg to LOG if errormsg: LOG_CONTENT += errormsg # append normal msg to LOG save_msg = msg.strip() if msg else None if save_msg: timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime()) LOG_CONTENT += timestr + save_msg + '\n' if errormsg: _color_print('', NO_COLOR, errormsg, stream, level) _color_print(head, color, msg, stream, level) def _color_print(head, color, msg, stream, level): colored = True if color == NO_COLOR or \ not stream.isatty() or \ os.getenv('ANSI_COLORS_DISABLED') is not None: colored = False if head.startswith('\r'): # need not \n at last newline = False else: newline = True if colored: head = '\033[%dm%s:\033[0m ' %(color, head) if not newline: # ESC cmd to clear line head = '\033[2K' + head else: if head: head += ': ' if head.startswith('\r'): head = head.lstrip() newline = True if msg is not None: stream.write('%s%s' % (head, msg)) if newline: stream.write('\n') stream.flush() def _color_perror(head, color, msg, level='normal'): if CATCHERR_BUFFILE_FD > 0: _general_print(head, color, msg, sys.stdout, level) else: _general_print(head, color, msg, sys.stderr, level) def _split_msg(head, msg): if isinstance(msg, list): msg = '\n'.join(map(str, msg)) if msg.startswith('\n'): # means print \n at first msg = msg.lstrip() head = '\n' + head elif msg.startswith('\r'): # means print \r at first msg = msg.lstrip() head = '\r' + head match = PREFIX_RE.match(msg) if match: head += ' <%s>' % match.group(1) msg = match.group(2) return head, msg def get_loglevel(): return next((k for k, v in LOG_LEVELS.items() if v == LOG_LEVEL)) def set_loglevel(level): global LOG_LEVEL if level not in LOG_LEVELS: # no effect return LOG_LEVEL = LOG_LEVELS[level] def set_interactive(mode=True): global INTERACTIVE if mode: INTERACTIVE = True else: INTERACTIVE = False def log(msg=''): # log msg to LOG_CONTENT then save to logfile global LOG_CONTENT if msg: LOG_CONTENT += msg def info(msg): head, msg = _split_msg('Info', msg) _general_print(head, INFO_COLOR, msg) def verbose(msg): head, msg = _split_msg('Verbose', msg) _general_print(head, INFO_COLOR, msg, level='verbose') def warning(msg): head, msg = _split_msg('Warning', msg) _color_perror(head, WARN_COLOR, msg) def debug(msg): head, msg = _split_msg('Debug', msg) _color_perror(head, ERR_COLOR, msg, level='debug') def error(msg): head, msg = _split_msg('Error', msg) _color_perror(head, ERR_COLOR, msg) sys.exit(1) def set_logfile(fpath): global LOG_FILE_FP def _savelogf(): if LOG_FILE_FP: with open(LOG_FILE_FP, 'w') as log: log.write(LOG_CONTENT) if LOG_FILE_FP is not None: warning('duplicate log file configuration') LOG_FILE_FP = fpath import atexit atexit.register(_savelogf) def enable_logstderr(fpath): global CATCHERR_BUFFILE_FD global CATCHERR_BUFFILE_PATH global CATCHERR_SAVED_2 if os.path.exists(fpath): os.remove(fpath) CATCHERR_BUFFILE_PATH = fpath CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT) CATCHERR_SAVED_2 = os.dup(2) os.dup2(CATCHERR_BUFFILE_FD, 2) def disable_logstderr(): global CATCHERR_BUFFILE_FD global CATCHERR_BUFFILE_PATH global CATCHERR_SAVED_2 raw(msg=None) # flush message buffer and print it. os.dup2(CATCHERR_SAVED_2, 2) os.close(CATCHERR_SAVED_2) os.close(CATCHERR_BUFFILE_FD) os.unlink(CATCHERR_BUFFILE_PATH) CATCHERR_BUFFILE_FD = -1 CATCHERR_BUFFILE_PATH = None CATCHERR_SAVED_2 = -1