Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python -tt |
| 2 | # vim: ai ts=4 sts=4 et sw=4 |
| 3 | # |
| 4 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. |
| 5 | # |
| 6 | # This program is free software; you can redistribute it and/or modify it |
| 7 | # under the terms of the GNU General Public License as published by the Free |
| 8 | # Software Foundation; version 2 of the License |
| 9 | # |
| 10 | # This program is distributed in the hope that it will be useful, but |
| 11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 13 | # for more details. |
| 14 | # |
| 15 | # You should have received a copy of the GNU General Public License along |
| 16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 |
| 17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 18 | |
| 19 | import os |
| 20 | import sys |
| 21 | import re |
| 22 | import time |
| 23 | |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 24 | __ALL__ = ['get_loglevel', |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 25 | 'set_loglevel', |
| 26 | 'set_logfile', |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 27 | 'debug', |
| 28 | 'verbose', |
| 29 | 'info', |
| 30 | 'warning', |
| 31 | 'error', |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 32 | ] |
| 33 | |
| 34 | # COLORs in ANSI |
| 35 | INFO_COLOR = 32 # green |
| 36 | WARN_COLOR = 33 # yellow |
| 37 | ERR_COLOR = 31 # red |
| 38 | ASK_COLOR = 34 # blue |
| 39 | NO_COLOR = 0 |
| 40 | |
| 41 | PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S) |
| 42 | |
| 43 | INTERACTIVE = True |
| 44 | |
| 45 | LOG_LEVEL = 1 |
| 46 | LOG_LEVELS = { |
| 47 | 'quiet': 0, |
| 48 | 'normal': 1, |
| 49 | 'verbose': 2, |
| 50 | 'debug': 3, |
| 51 | 'never': 4, |
| 52 | } |
| 53 | |
| 54 | LOG_FILE_FP = None |
| 55 | LOG_CONTENT = '' |
| 56 | CATCHERR_BUFFILE_FD = -1 |
| 57 | CATCHERR_BUFFILE_PATH = None |
| 58 | CATCHERR_SAVED_2 = -1 |
| 59 | |
| 60 | def _general_print(head, color, msg=None, stream=None, level='normal'): |
| 61 | global LOG_CONTENT |
| 62 | if not stream: |
| 63 | stream = sys.stdout |
| 64 | |
| 65 | if LOG_LEVELS[level] > LOG_LEVEL: |
| 66 | # skip |
| 67 | return |
| 68 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 69 | errormsg = '' |
| 70 | if CATCHERR_BUFFILE_FD > 0: |
| 71 | size = os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_END) |
| 72 | os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET) |
| 73 | errormsg = os.read(CATCHERR_BUFFILE_FD, size) |
| 74 | os.ftruncate(CATCHERR_BUFFILE_FD, 0) |
| 75 | |
| 76 | # append error msg to LOG |
| 77 | if errormsg: |
| 78 | LOG_CONTENT += errormsg |
| 79 | |
| 80 | # append normal msg to LOG |
| 81 | save_msg = msg.strip() if msg else None |
| 82 | if save_msg: |
| 83 | timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime()) |
| 84 | LOG_CONTENT += timestr + save_msg + '\n' |
| 85 | |
| 86 | if errormsg: |
| 87 | _color_print('', NO_COLOR, errormsg, stream, level) |
| 88 | |
| 89 | _color_print(head, color, msg, stream, level) |
| 90 | |
| 91 | def _color_print(head, color, msg, stream, level): |
| 92 | colored = True |
| 93 | if color == NO_COLOR or \ |
| 94 | not stream.isatty() or \ |
| 95 | os.getenv('ANSI_COLORS_DISABLED') is not None: |
| 96 | colored = False |
| 97 | |
| 98 | if head.startswith('\r'): |
| 99 | # need not \n at last |
| 100 | newline = False |
| 101 | else: |
| 102 | newline = True |
| 103 | |
| 104 | if colored: |
| 105 | head = '\033[%dm%s:\033[0m ' %(color, head) |
| 106 | if not newline: |
| 107 | # ESC cmd to clear line |
| 108 | head = '\033[2K' + head |
| 109 | else: |
| 110 | if head: |
| 111 | head += ': ' |
| 112 | if head.startswith('\r'): |
| 113 | head = head.lstrip() |
| 114 | newline = True |
| 115 | |
| 116 | if msg is not None: |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 117 | stream.write('%s%s' % (head, msg)) |
| 118 | if newline: |
| 119 | stream.write('\n') |
| 120 | |
| 121 | stream.flush() |
| 122 | |
| 123 | def _color_perror(head, color, msg, level='normal'): |
| 124 | if CATCHERR_BUFFILE_FD > 0: |
| 125 | _general_print(head, color, msg, sys.stdout, level) |
| 126 | else: |
| 127 | _general_print(head, color, msg, sys.stderr, level) |
| 128 | |
| 129 | def _split_msg(head, msg): |
| 130 | if isinstance(msg, list): |
| 131 | msg = '\n'.join(map(str, msg)) |
| 132 | |
| 133 | if msg.startswith('\n'): |
| 134 | # means print \n at first |
| 135 | msg = msg.lstrip() |
| 136 | head = '\n' + head |
| 137 | |
| 138 | elif msg.startswith('\r'): |
| 139 | # means print \r at first |
| 140 | msg = msg.lstrip() |
| 141 | head = '\r' + head |
| 142 | |
| 143 | match = PREFIX_RE.match(msg) |
| 144 | if match: |
| 145 | head += ' <%s>' % match.group(1) |
| 146 | msg = match.group(2) |
| 147 | |
| 148 | return head, msg |
| 149 | |
| 150 | def get_loglevel(): |
Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 151 | return next((k for k, v in LOG_LEVELS.items() if v == LOG_LEVEL)) |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 152 | |
| 153 | def set_loglevel(level): |
| 154 | global LOG_LEVEL |
| 155 | if level not in LOG_LEVELS: |
| 156 | # no effect |
| 157 | return |
| 158 | |
| 159 | LOG_LEVEL = LOG_LEVELS[level] |
| 160 | |
| 161 | def set_interactive(mode=True): |
| 162 | global INTERACTIVE |
| 163 | if mode: |
| 164 | INTERACTIVE = True |
| 165 | else: |
| 166 | INTERACTIVE = False |
| 167 | |
| 168 | def log(msg=''): |
| 169 | # log msg to LOG_CONTENT then save to logfile |
| 170 | global LOG_CONTENT |
| 171 | if msg: |
| 172 | LOG_CONTENT += msg |
| 173 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 174 | def info(msg): |
| 175 | head, msg = _split_msg('Info', msg) |
| 176 | _general_print(head, INFO_COLOR, msg) |
| 177 | |
| 178 | def verbose(msg): |
| 179 | head, msg = _split_msg('Verbose', msg) |
| 180 | _general_print(head, INFO_COLOR, msg, level='verbose') |
| 181 | |
| 182 | def warning(msg): |
| 183 | head, msg = _split_msg('Warning', msg) |
| 184 | _color_perror(head, WARN_COLOR, msg) |
| 185 | |
| 186 | def debug(msg): |
| 187 | head, msg = _split_msg('Debug', msg) |
| 188 | _color_perror(head, ERR_COLOR, msg, level='debug') |
| 189 | |
| 190 | def error(msg): |
| 191 | head, msg = _split_msg('Error', msg) |
| 192 | _color_perror(head, ERR_COLOR, msg) |
| 193 | sys.exit(1) |
| 194 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 195 | def set_logfile(fpath): |
| 196 | global LOG_FILE_FP |
| 197 | |
| 198 | def _savelogf(): |
| 199 | if LOG_FILE_FP: |
| 200 | with open(LOG_FILE_FP, 'w') as log: |
| 201 | log.write(LOG_CONTENT) |
| 202 | |
| 203 | if LOG_FILE_FP is not None: |
| 204 | warning('duplicate log file configuration') |
| 205 | |
| 206 | LOG_FILE_FP = fpath |
| 207 | |
| 208 | import atexit |
| 209 | atexit.register(_savelogf) |
| 210 | |
| 211 | def enable_logstderr(fpath): |
| 212 | global CATCHERR_BUFFILE_FD |
| 213 | global CATCHERR_BUFFILE_PATH |
| 214 | global CATCHERR_SAVED_2 |
| 215 | |
| 216 | if os.path.exists(fpath): |
| 217 | os.remove(fpath) |
| 218 | CATCHERR_BUFFILE_PATH = fpath |
| 219 | CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT) |
| 220 | CATCHERR_SAVED_2 = os.dup(2) |
| 221 | os.dup2(CATCHERR_BUFFILE_FD, 2) |
| 222 | |
| 223 | def disable_logstderr(): |
| 224 | global CATCHERR_BUFFILE_FD |
| 225 | global CATCHERR_BUFFILE_PATH |
| 226 | global CATCHERR_SAVED_2 |
| 227 | |
| 228 | raw(msg=None) # flush message buffer and print it. |
| 229 | os.dup2(CATCHERR_SAVED_2, 2) |
| 230 | os.close(CATCHERR_SAVED_2) |
| 231 | os.close(CATCHERR_BUFFILE_FD) |
| 232 | os.unlink(CATCHERR_BUFFILE_PATH) |
| 233 | CATCHERR_BUFFILE_FD = -1 |
| 234 | CATCHERR_BUFFILE_PATH = None |
| 235 | CATCHERR_SAVED_2 = -1 |