Files
2026-07-13 12:37:18 +08:00

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()