AzurLaneAutoScript/module/logger.py

348 lines
10 KiB
Python
Raw Normal View History

2020-03-29 01:22:46 +08:00
import datetime
import logging
import os
import sys
from collections import deque
2022-01-21 03:55:30 +08:00
from typing import Callable, List
import rich.box
from rich import print as pprint
from rich.console import Console, ConsoleOptions, ConsoleRenderable, NewLine
from rich.highlighter import RegexHighlighter
from rich.logging import RichHandler
from rich.rule import Rule
from rich.style import Style
from rich.theme import Theme
from rich.traceback import Traceback
2020-03-29 01:22:46 +08:00
def empty_function(*args, **kwargs):
pass
# cnocr will set root logger in cnocr.utils
# Delete logging.basicConfig to avoid logging the same message twice.
logging.basicConfig = empty_function
logging.raiseExceptions = True # Set True if wanna see encode errors on console
2022-01-21 03:55:30 +08:00
# Force rounded box
rich.box.ASCII = rich.box.ASCII2 = rich.box.ROUNDED
# Remove HTTP keywords (GET, POST etc.)
RichHandler.KEYWORDS = []
class RichFileHandler(RichHandler):
# Rename
pass
class RichRenderableHandler(RichHandler):
"""
Pass renderable into a function
"""
def __init__(self, *args, func: Callable[[ConsoleRenderable], None] = None, **kwargs):
super().__init__(*args, **kwargs)
self._func = func
def emit(self, record: logging.LogRecord) -> None:
message = self.format(record)
traceback = None
if (
self.rich_tracebacks
and record.exc_info
and record.exc_info != (None, None, None)
):
exc_type, exc_value, exc_traceback = record.exc_info
assert exc_type is not None
assert exc_value is not None
traceback = Traceback.from_exception(
exc_type,
exc_value,
exc_traceback,
width=self.tracebacks_width,
extra_lines=self.tracebacks_extra_lines,
theme=self.tracebacks_theme,
word_wrap=self.tracebacks_word_wrap,
show_locals=self.tracebacks_show_locals,
locals_max_length=self.locals_max_length,
locals_max_string=self.locals_max_string,
)
message = record.getMessage()
if self.formatter:
record.message = record.getMessage()
formatter = self.formatter
if hasattr(formatter, "usesTime") and formatter.usesTime():
record.asctime = formatter.formatTime(
record, formatter.datefmt)
message = formatter.formatMessage(record)
message_renderable = self.render_message(record, message)
log_renderable = self.render(
record=record, traceback=traceback, message_renderable=message_renderable
)
# Directly put renderable into function
self._func(log_renderable)
def handle(self, record: logging.LogRecord) -> bool:
if not self._func:
return True
super().handle(record)
class HTMLConsole(Console):
"""
Force full feature console
but not working lol :(
"""
@property
def options(self) -> ConsoleOptions:
return ConsoleOptions(
max_height=self.size.height,
size=self.size,
legacy_windows=False,
min_width=1,
max_width=self.width,
encoding='utf-8',
is_terminal=False,
)
class Highlighter(RegexHighlighter):
base_style = 'web.'
highlights = [
2022-01-21 17:30:34 +08:00
# (r'(?P<datetime>(\d{2}|\d{4})(?:\-)?([0]{1}\d{1}|[1]{1}[0-2]{1})'
# r'(?:\-)?([0-2]{1}\d{1}|[3]{1}[0-1]{1})(?:\s)?([0-1]{1}\d{1}|'
# r'[2]{1}[0-3]{1})(?::)?([0-5]{1}\d{1})(?::)?([0-5]{1}\d{1}).\d+\b)'),
(r'(?P<time>([0-1]{1}\d{1}|[2]{1}[0-3]{1})(?::)?'
r'([0-5]{1}\d{1})(?::)?([0-5]{1}\d{1})(.\d+\b))'),
2022-01-21 03:55:30 +08:00
r"(?P<brace>[\{\[\(\)\]\}])"
r"\b(?P<bool_true>True)\b|\b(?P<bool_false>False)\b|\b(?P<none>None)\b",
r"(?P<path>(([A-Za-z]\:)|.)?\B([\/\\][\w\.\-\_\+]+)*[\/\\])(?P<filename>[\w\.\-\_\+]*)?",
# r"(?<![\\\w])(?P<str>b?\'\'\'.*?(?<!\\)\'\'\'|b?\'.*?(?<!\\)\'|b?\"\"\".*?(?<!\\)\"\"\"|b?\".*?(?<!\\)\")",
]
WEB_THEME = Theme({
"web.brace": Style(bold=True),
"web.bool_true": Style(color="bright_green", italic=True),
"web.bool_false": Style(color="bright_red", italic=True),
"web.none": Style(color="magenta", italic=True),
"web.path": Style(color="magenta"),
"web.filename": Style(color="bright_magenta"),
"web.str": Style(color="green", italic=False, bold=False),
"web.time": Style(color="cyan"),
"rule.text": Style(bold=True),
})
# Logger init
2022-01-21 17:30:34 +08:00
logger_debug = True
logger = logging.getLogger('alas')
logger.setLevel(logging.DEBUG if logger_debug else logging.INFO)
2022-01-21 03:55:30 +08:00
file_formatter = logging.Formatter(
fmt='%(asctime)s.%(msecs)03d | %(levelname)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
console_formatter = logging.Formatter(
fmt='%(asctime)s.%(msecs)03d%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
2022-01-21 17:30:34 +08:00
web_formatter = logging.Formatter(
fmt='%(asctime)s.%(msecs)03d%(message)s', datefmt='%H:%M:%S')
2020-03-29 01:22:46 +08:00
# Add console logger
2022-01-21 03:55:30 +08:00
# console = logging.StreamHandler(stream=sys.stdout)
# console.setFormatter(formatter)
# console.flush = sys.stdout.flush
# logger.addHandler(console)
# Add rich console logger
stdout_console = console = Console()
console_hdlr = RichHandler(
show_path=False,
show_time=False,
rich_tracebacks=True,
tracebacks_show_locals=True,
tracebacks_extra_lines=3,
)
console_hdlr.setFormatter(console_formatter)
logger.addHandler(console_hdlr)
2020-03-29 01:22:46 +08:00
2020-12-16 18:04:01 +08:00
# Ensure running in Alas root folder
os.chdir(os.path.join(os.path.dirname(__file__), '../'))
# Add file logger
pyw_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
2022-01-21 03:55:30 +08:00
def _set_file_logger(name=pyw_name):
if '_' in name:
name = name.split('_', 1)[0]
log_file = f'./log/{datetime.date.today()}_{name}.txt'
try:
file = logging.FileHandler(log_file, encoding='utf-8')
except FileNotFoundError:
os.mkdir('./log')
file = logging.FileHandler(log_file, encoding='utf-8')
2022-01-21 03:55:30 +08:00
file.setFormatter(file_formatter)
2022-01-21 03:55:30 +08:00
logger.handlers = [h for h in logger.handlers if not isinstance(
h, (logging.FileHandler, RichFileHandler))]
logger.addHandler(file)
logger.log_file = log_file
2020-03-29 01:22:46 +08:00
2022-01-21 03:55:30 +08:00
def set_file_logger(name=pyw_name):
if '_' in name:
name = name.split('_', 1)[0]
log_file = f'./log/{datetime.date.today()}_{name}.txt'
try:
file = open(log_file, mode='a', encoding='utf-8')
except FileNotFoundError:
os.mkdir('./log')
file = open(log_file, mode='a', encoding='utf-8')
file_console = Console(
file=file,
no_color=True,
width=120,
)
hdlr = RichFileHandler(
console=file_console,
show_path=False,
show_time=False,
show_level=False,
rich_tracebacks=True,
tracebacks_show_locals=True,
tracebacks_extra_lines=3,
)
hdlr.setFormatter(file_formatter)
logger.handlers = [h for h in logger.handlers if not isinstance(
h, (logging.FileHandler, RichFileHandler))]
logger.addHandler(hdlr)
logger.log_file = log_file
def set_func_logger(func):
console = HTMLConsole(
force_terminal=False,
force_interactive=False,
width=80,
color_system='truecolor',
markup=False,
safe_box=False,
highlighter=Highlighter(),
theme=WEB_THEME
)
hdlr = RichRenderableHandler(
func=func,
console=console,
show_path=False,
show_time=False,
show_level=True,
rich_tracebacks=True,
tracebacks_show_locals=True,
tracebacks_extra_lines=3,
highlighter=Highlighter(),
)
2022-01-21 17:30:34 +08:00
hdlr.setFormatter(web_formatter)
2022-01-21 03:55:30 +08:00
logger.handlers = [h for h in logger.handlers if not isinstance(
h, RichRenderableHandler)]
logger.addHandler(hdlr)
def _get_renderables(
self: Console, *objects, sep=" ", end="\n", justify=None, emoji=None, markup=None, highlight=None,
) -> List[ConsoleRenderable]:
"""
Refer to rich.console.Console.print()
"""
if not objects:
objects = (NewLine(),)
render_hooks = self._render_hooks[:]
with self:
renderables = self._collect_renderables(
objects,
sep,
end,
justify=justify,
emoji=emoji,
markup=markup,
highlight=highlight,
)
for hook in render_hooks:
renderables = hook.process_renderables(renderables)
return renderables
def print(*objects: ConsoleRenderable, **kwargs):
for hdlr in logger.handlers:
if isinstance(hdlr, RichRenderableHandler):
for renderable in _get_renderables(hdlr.console, *objects, **kwargs):
hdlr._func(renderable)
elif isinstance(hdlr, RichHandler):
hdlr.console.print(*objects)
def rule(title="", *, characters="", style="rule.line", end="\n", align="center"):
rule = Rule(title=title, characters=characters,
style=style, end=end, align=align)
print(rule)
2020-03-29 01:22:46 +08:00
def hr(title, level=3):
title = str(title).upper()
if level == 1:
2022-01-21 04:59:50 +08:00
logger.rule(title, characters='')
2020-03-29 01:22:46 +08:00
if level == 2:
2022-01-21 04:59:50 +08:00
logger.rule(title, characters='')
2020-03-29 01:22:46 +08:00
if level == 3:
2022-01-21 03:55:30 +08:00
logger.info(f"[bold]<<< {title} >>>[/bold]", extra={"markup": True})
2020-03-29 01:22:46 +08:00
if level == 0:
2022-01-21 04:59:50 +08:00
logger.rule(characters='')
2022-01-21 03:55:30 +08:00
logger.rule(title, characters=' ')
2022-01-21 04:59:50 +08:00
logger.rule(characters='')
2020-03-29 01:22:46 +08:00
def attr(name, text):
logger.info('[%s] %s' % (str(name), str(text)))
2020-07-07 19:54:30 +08:00
def attr_align(name, text, front='', align=22):
2020-07-06 00:46:36 +08:00
name = str(name).rjust(align)
2020-07-07 19:54:30 +08:00
if front:
name = front + name[len(front):]
2020-07-06 00:46:36 +08:00
logger.info('%s: %s' % (name, str(text)))
2022-01-21 17:30:34 +08:00
def show():
logger.info('INFO')
logger.warning('WARNING')
logger.debug('DEBUG')
logger.error('ERROR')
logger.critical('CRITICAL')
logger.hr('hr0', 0)
logger.hr('hr1', 1)
logger.hr('hr2', 2)
logger.hr('hr3', 3)
logger.info(r'Brace { [ ( ) ] }')
logger.info(r'True, False, None')
logger.info(r'E:/path\\to/alas/alas.exe, /root/alas/, ./relative/path/log.txt')
local_var1 = 'This is local variable'
# Line before exception
raise Exception("Exception")
# Line below exception
2020-03-29 01:22:46 +08:00
logger.hr = hr
logger.attr = attr
2020-07-06 00:46:36 +08:00
logger.attr_align = attr_align
logger.set_file_logger = set_file_logger
2022-01-21 03:55:30 +08:00
logger.rule = rule
logger.print = print
logger.log_file: str
2020-03-29 01:22:46 +08:00
logger.set_file_logger()
2020-03-29 01:22:46 +08:00
logger.hr('Start', level=0)