183 lines
5.4 KiB
Python
183 lines
5.4 KiB
Python
# -*- coding:utf-8 -*-
|
|
# Author: hankcs
|
|
# Date: 2019-08-24 22:12
|
|
import datetime
|
|
import io
|
|
import logging
|
|
import os
|
|
import sys
|
|
from logging import LogRecord
|
|
|
|
import termcolor
|
|
|
|
from hanlp_common.constant import IPYTHON
|
|
|
|
|
|
class ColoredFormatter(logging.Formatter):
|
|
def __init__(self, fmt=None, datefmt=None, style='%', enable=True):
|
|
super().__init__(fmt, datefmt, style)
|
|
self.enable = enable
|
|
|
|
def formatMessage(self, record: LogRecord) -> str:
|
|
message = super().formatMessage(record)
|
|
if self.enable:
|
|
return color_format(message)
|
|
else:
|
|
return remove_color_tag(message)
|
|
|
|
|
|
def init_logger(name=None, root_dir=None, level=logging.INFO, mode='w',
|
|
fmt="%(asctime)s %(levelname)s %(message)s",
|
|
datefmt='%Y-%m-%d %H:%M:%S') -> logging.Logger:
|
|
if not name:
|
|
name = datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S")
|
|
rootLogger = logging.getLogger(os.path.join(root_dir, name) if root_dir else name)
|
|
rootLogger.propagate = False
|
|
|
|
consoleHandler = logging.StreamHandler(sys.stdout) # stderr will be rendered as red which is bad
|
|
consoleHandler.setFormatter(ColoredFormatter(fmt, datefmt=datefmt))
|
|
attached_to_std = False
|
|
for handler in rootLogger.handlers:
|
|
if isinstance(handler, logging.StreamHandler):
|
|
if handler.stream == sys.stderr or handler.stream == sys.stdout:
|
|
attached_to_std = True
|
|
break
|
|
if not attached_to_std:
|
|
rootLogger.addHandler(consoleHandler)
|
|
rootLogger.setLevel(level)
|
|
consoleHandler.setLevel(level)
|
|
|
|
if root_dir:
|
|
os.makedirs(root_dir, exist_ok=True)
|
|
log_path = "{0}/{1}.log".format(root_dir, name)
|
|
fileHandler = logging.FileHandler(log_path, mode=mode)
|
|
fileHandler.setFormatter(ColoredFormatter(fmt, datefmt=datefmt, enable=False))
|
|
rootLogger.addHandler(fileHandler)
|
|
fileHandler.setLevel(level)
|
|
|
|
return rootLogger
|
|
|
|
|
|
logger = init_logger(name='hanlp', level=os.environ.get('HANLP_LOG_LEVEL', 'INFO'))
|
|
|
|
|
|
def enable_debug(debug=True):
|
|
logger.setLevel(logging.DEBUG if debug else logging.ERROR)
|
|
|
|
|
|
class ErasablePrinter(object):
|
|
def __init__(self, out=sys.stderr):
|
|
self._last_print_width = 0
|
|
self.out = out
|
|
|
|
def erase(self):
|
|
if self._last_print_width:
|
|
if IPYTHON:
|
|
self.out.write("\r")
|
|
self.out.write(" " * self._last_print_width)
|
|
else:
|
|
self.out.write("\b" * self._last_print_width)
|
|
self.out.write(" " * self._last_print_width)
|
|
self.out.write("\b" * self._last_print_width)
|
|
self.out.write("\r") # \r is essential when multi-lines were printed
|
|
self._last_print_width = 0
|
|
|
|
def print(self, msg: str, color=True):
|
|
self.erase()
|
|
if color:
|
|
if IPYTHON:
|
|
msg, _len = color_format_len(msg)
|
|
_len = len(msg)
|
|
else:
|
|
msg, _len = color_format_len(msg)
|
|
self._last_print_width = _len
|
|
else:
|
|
self._last_print_width = len(msg)
|
|
self.out.write(msg)
|
|
self.out.flush()
|
|
|
|
|
|
_printer = ErasablePrinter()
|
|
|
|
|
|
def flash(line: str, color=True):
|
|
_printer.print(line, color)
|
|
|
|
|
|
def color_format(msg: str):
|
|
for tag in termcolor.COLORS, termcolor.HIGHLIGHTS, termcolor.ATTRIBUTES:
|
|
for c, v in tag.items():
|
|
start, end = f'[{c}]', f'[/{c}]'
|
|
msg = msg.replace(start, '\033[%dm' % v).replace(end, termcolor.RESET)
|
|
return msg
|
|
|
|
|
|
def remove_color_tag(msg: str):
|
|
for tag in termcolor.COLORS, termcolor.HIGHLIGHTS, termcolor.ATTRIBUTES:
|
|
for c, v in tag.items():
|
|
start, end = f'[{c}]', f'[/{c}]'
|
|
msg = msg.replace(start, '').replace(end, '')
|
|
return msg
|
|
|
|
|
|
def color_format_len(msg: str):
|
|
_len = len(msg)
|
|
for tag in termcolor.COLORS, termcolor.HIGHLIGHTS, termcolor.ATTRIBUTES:
|
|
for c, v in tag.items():
|
|
start, end = f'[{c}]', f'[/{c}]'
|
|
msg, delta = _replace_color_offset(msg, start, '\033[%dm' % v)
|
|
_len -= delta
|
|
msg, delta = _replace_color_offset(msg, end, termcolor.RESET)
|
|
_len -= delta
|
|
return msg, _len
|
|
|
|
|
|
def _replace_color_offset(msg: str, color: str, ctrl: str):
|
|
chunks = msg.split(color)
|
|
delta = (len(chunks) - 1) * len(color)
|
|
return ctrl.join(chunks), delta
|
|
|
|
|
|
def cprint(*args, file=None, **kwargs):
|
|
out = io.StringIO()
|
|
print(*args, file=out, **kwargs)
|
|
text = out.getvalue()
|
|
out.close()
|
|
c_text = color_format(text)
|
|
print(c_text, end='', file=file)
|
|
|
|
|
|
def main():
|
|
# cprint('[blink][yellow]...[/yellow][/blink]')
|
|
# show_colors_and_formats()
|
|
show_colors()
|
|
# print('previous', end='')
|
|
# for i in range(10):
|
|
# flash(f'[red]{i}[/red]')
|
|
|
|
|
|
def show_colors_and_formats():
|
|
msg = ''
|
|
for c in termcolor.COLORS.keys():
|
|
for h in termcolor.HIGHLIGHTS.keys():
|
|
for a in termcolor.ATTRIBUTES.keys():
|
|
msg += f'[{c}][{h}][{a}] {c}+{h}+{a} [/{a}][/{h}][/{c}]'
|
|
logger.info(msg)
|
|
|
|
|
|
def show_colors():
|
|
msg = ''
|
|
for c in termcolor.COLORS.keys():
|
|
cprint(f'[{c}]"{c}",[/{c}]')
|
|
|
|
|
|
# Generates tables for Doxygen flavored Markdown. See the Doxygen
|
|
# documentation for details:
|
|
# http://www.doxygen.nl/manual/markdown.html#md_tables
|
|
|
|
# Translation dictionaries for table alignment
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|