# -*- coding: utf-8 -*- from datetime import datetime from io import TextIOBase import logging from logging import FileHandler, Formatter, Handler, StreamHandler from pathlib import Path import sys import time from typing import Optional time_format = '%Y-%m-%d %H:%M:%S' formatter = Formatter( '[%(asctime)s] %(levelname)s (%(name)s/%(threadName)s) %(message)s', time_format ) def init_logger() -> None: _setup_root_logger(StreamHandler(sys.stdout), logging.INFO) logging.basicConfig() def _prepare_log_dir(path: Optional[str]) -> Path: if path is None: return Path() ret = Path(path) ret.mkdir(parents=True, exist_ok=True) return ret def _setup_root_logger(handler: Handler, level: int) -> None: _setup_logger('tadl', handler, level) def _setup_logger(name: str, handler: Handler, level: int) -> None: handler.setFormatter(formatter) logger = logging.getLogger(name) logger.addHandler(handler) logger.setLevel(level) logger.propagate = False class _LogFileWrapper(TextIOBase): # wrap the logger file so that anything written to it will automatically get formatted def __init__(self, log_file: TextIOBase): self.file: TextIOBase = log_file self.line_buffer: Optional[str] = None self.line_start_time: Optional[datetime] = None def write(self, s: str) -> int: cur_time = datetime.now() if self.line_buffer and (cur_time - self.line_start_time).total_seconds() > 0.1: self.flush() if self.line_buffer: self.line_buffer += s else: self.line_buffer = s self.line_start_time = cur_time if '\n' not in s: return len(s) time_str = cur_time.strftime(time_format) lines = self.line_buffer.split('\n') for line in lines[:-1]: self.file.write(f'[{time_str}] PRINT {line}\n') self.file.flush() self.line_buffer = lines[-1] self.line_start_time = cur_time return len(s) def flush(self) -> None: if self.line_buffer: time_str = self.line_start_time.strftime(time_format) self.file.write(f'[{time_str}] PRINT {self.line_buffer}\n') self.file.flush() self.line_buffer = None