Last active
January 31, 2024 09:33
-
-
Save Hansimov/649ad175156e59fc0cb598a5ff6b5c49 to your computer and use it in GitHub Desktop.
A script template of logger.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
import functools | |
import inspect | |
import logging | |
import os | |
import shutil | |
import subprocess | |
from termcolor import colored | |
def add_fillers(text, filler="=", fill_side="both"): | |
terminal_width = shutil.get_terminal_size().columns | |
text = text.strip() | |
text_width = len(text) | |
if text_width >= terminal_width: | |
return text | |
if fill_side[0].lower() == "b": | |
leading_fill_str = filler * ((terminal_width - text_width) // 2 - 1) + " " | |
trailing_fill_str = " " + filler * ( | |
terminal_width - text_width - len(leading_fill_str) - 1 | |
) | |
elif fill_side[0].lower() == "l": | |
leading_fill_str = filler * (terminal_width - text_width - 1) + " " | |
trailing_fill_str = "" | |
elif fill_side[0].lower() == "r": | |
leading_fill_str = "" | |
trailing_fill_str = " " + filler * (terminal_width - text_width - 1) | |
else: | |
raise ValueError("Invalid fill_side") | |
filled_str = f"{leading_fill_str}{text}{trailing_fill_str}" | |
return filled_str | |
class OSLogger(logging.Logger): | |
LOG_METHODS = { | |
"err": ("error", "red"), | |
"warn": ("warning", "light_red"), | |
"note": ("info", "light_magenta"), | |
"mesg": ("info", "light_cyan"), | |
"file": ("info", "light_blue"), | |
"line": ("info", "white"), | |
"success": ("info", "light_green"), | |
"fail": ("info", "light_red"), | |
"back": ("debug", "light_cyan"), | |
} | |
INDENT_METHODS = [ | |
"indent", | |
"set_indent", | |
"reset_indent", | |
"store_indent", | |
"restore_indent", | |
"log_indent", | |
] | |
LEVEL_METHODS = [ | |
"set_level", | |
"store_level", | |
"restore_level", | |
"quiet", | |
"enter_quiet", | |
"exit_quiet", | |
] | |
LEVEL_NAMES = { | |
"critical": logging.CRITICAL, | |
"error": logging.ERROR, | |
"warning": logging.WARNING, | |
"info": logging.INFO, | |
"debug": logging.DEBUG, | |
} | |
def __init__(self, name=None, prefix=False): | |
if not name: | |
frame = inspect.stack()[1] | |
module = inspect.getmodule(frame[0]) | |
name = module.__name__ | |
super().__init__(name) | |
self.setLevel(logging.INFO) | |
if prefix: | |
formatter_prefix = "[%(asctime)s] - [%(name)s] - [%(levelname)s]\n" | |
else: | |
formatter_prefix = "" | |
self.formatter = logging.Formatter(formatter_prefix + "%(message)s") | |
stream_handler = logging.StreamHandler() | |
stream_handler.setLevel(logging.INFO) | |
stream_handler.setFormatter(self.formatter) | |
self.addHandler(stream_handler) | |
self.log_indent = 0 | |
self.log_indents = [] | |
self.log_level = "info" | |
self.log_levels = [] | |
def indent(self, indent=2): | |
self.log_indent += indent | |
def set_indent(self, indent=2): | |
self.log_indent = indent | |
def reset_indent(self): | |
self.log_indent = 0 | |
def store_indent(self): | |
self.log_indents.append(self.log_indent) | |
def restore_indent(self): | |
self.log_indent = self.log_indents.pop(-1) | |
def set_level(self, level): | |
self.log_level = level | |
self.setLevel(self.LEVEL_NAMES[level]) | |
def store_level(self): | |
self.log_levels.append(self.log_level) | |
def restore_level(self): | |
self.log_level = self.log_levels.pop(-1) | |
self.set_level(self.log_level) | |
def quiet(self): | |
self.set_level("critical") | |
def enter_quiet(self, quiet=False): | |
if quiet: | |
self.store_level() | |
self.quiet() | |
def exit_quiet(self, quiet=False): | |
if quiet: | |
self.restore_level() | |
def log( | |
self, | |
level, | |
color, | |
msg, | |
indent=0, | |
fill=False, | |
fill_side="both", | |
end="\n", | |
*args, | |
**kwargs, | |
): | |
if type(msg) == str: | |
msg_str = msg | |
else: | |
msg_str = repr(msg) | |
quotes = ["'", '"'] | |
if msg_str[0] in quotes and msg_str[-1] in quotes: | |
msg_str = msg_str[1:-1] | |
indent_str = " " * (self.log_indent + indent) | |
indented_msg = "\n".join([indent_str + line for line in msg_str.split("\n")]) | |
if fill: | |
indented_msg = add_fillers(indented_msg, fill_side=fill_side) | |
handler = self.handlers[0] | |
handler.terminator = end | |
getattr(self, level)(colored(indented_msg, color), *args, **kwargs) | |
def route_log(self, method, msg, *args, **kwargs): | |
level, method = method | |
functools.partial(self.log, level, method, msg)(*args, **kwargs) | |
def err(self, msg: str = "", *args, **kwargs): | |
self.route_log(("error", "red"), msg, *args, **kwargs) | |
def warn(self, msg: str = "", *args, **kwargs): | |
self.route_log(("warning", "light_red"), msg, *args, **kwargs) | |
def note(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "light_magenta"), msg, *args, **kwargs) | |
def mesg(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "light_cyan"), msg, *args, **kwargs) | |
def file(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "light_blue"), msg, *args, **kwargs) | |
def line(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "white"), msg, *args, **kwargs) | |
def success(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "light_green"), msg, *args, **kwargs) | |
def fail(self, msg: str = "", *args, **kwargs): | |
self.route_log(("info", "light_red"), msg, *args, **kwargs) | |
def back(self, msg: str = "", *args, **kwargs): | |
self.route_log(("debug", "light_cyan"), msg, *args, **kwargs) | |
logger = OSLogger() | |
def shell_cmd(cmd, getoutput=False, showcmd=True, env=None): | |
if showcmd: | |
logger.info(colored(f"\n$ [{os.getcwd()}]", "light_blue")) | |
logger.info(colored(f" $ {cmd}\n", "light_cyan")) | |
if getoutput: | |
output = subprocess.getoutput(cmd, env=env) | |
return output | |
else: | |
subprocess.run(cmd, shell=True, env=env) | |
class Runtimer: | |
def __enter__(self): | |
self.t1, _ = self.start_time() | |
return self | |
def __exit__(self, exc_type, exc_value, traceback): | |
self.t2, _ = self.end_time() | |
self.elapsed_time(self.t2 - self.t1) | |
def start_time(self): | |
t1 = datetime.datetime.now() | |
self.logger_time("start", t1) | |
return t1, self.time2str(t1) | |
def end_time(self): | |
t2 = datetime.datetime.now() | |
self.logger_time("end", t2) | |
return t2, self.time2str(t2) | |
def elapsed_time(self, dt=None): | |
if dt is None: | |
dt = self.t2 - self.t1 | |
self.logger_time("elapsed", dt) | |
return dt, self.time2str(dt) | |
def logger_time(self, time_type, t): | |
time_types = { | |
"start": "Start", | |
"end": "End", | |
"elapsed": "Elapsed", | |
} | |
time_str = add_fillers( | |
colored( | |
f"{time_types[time_type]} time: [ {self.time2str(t)} ]", | |
"light_magenta", | |
), | |
fill_side="both", | |
) | |
logger.line(time_str) | |
# Convert time to string | |
def time2str(self, t): | |
datetime_str_format = "%Y-%m-%d %H:%M:%S" | |
if isinstance(t, datetime.datetime): | |
return t.strftime(datetime_str_format) | |
elif isinstance(t, datetime.timedelta): | |
hours = t.seconds // 3600 | |
hour_str = f"{hours} hr" if hours > 0 else "" | |
minutes = (t.seconds // 60) % 60 | |
minute_str = f"{minutes:>2} min" if minutes > 0 else "" | |
seconds = t.seconds % 60 | |
second_str = f"{seconds:>2} s" | |
time_str = " ".join([hour_str, minute_str, second_str]).strip() | |
return time_str | |
else: | |
return str(t) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment