forked from goodboy/tractor
1
0
Fork 0
tractor/tractor/log.py

122 lines
3.2 KiB
Python
Raw Normal View History

2018-07-05 23:51:32 +00:00
"""
Log like a forester!
"""
import sys
2018-07-05 23:51:32 +00:00
from functools import partial
import logging
2018-08-26 17:12:59 +00:00
import colorlog # type: ignore
2018-08-31 21:16:24 +00:00
from typing import Optional
from ._state import ActorContextInfo
2018-07-05 23:51:32 +00:00
_proj_name = 'tractor'
2020-12-25 20:28:32 +00:00
_default_loglevel = 'ERROR'
2018-07-05 23:51:32 +00:00
# Super sexy formatting thanks to ``colorlog``.
# (NOTE: we use the '{' format style)
# Here, `thin_white` is just the layperson's gray.
LOG_FORMAT = (
# "{bold_white}{log_color}{asctime}{reset}"
"{log_color}{asctime}{reset}"
" {bold_white}{thin_white}({reset}"
"{thin_white}{actor}, {process}, {task}){reset}{bold_white}{thin_white})"
2018-07-05 23:51:32 +00:00
" {reset}{log_color}[{reset}{bold_log_color}{levelname}{reset}{log_color}]"
" {log_color}{name}"
" {thin_white}{filename}{log_color}:{reset}{thin_white}{lineno}{log_color}"
" {reset}{bold_white}{thin_white}{message}"
)
DATE_FORMAT = '%b %d %H:%M:%S'
LEVELS = {
'GARBAGE': 1,
'TRANSPORT': 5,
'RUNTIME': 15,
'PDB': 500,
2018-07-05 23:51:32 +00:00
'QUIET': 1000,
}
STD_PALETTE = {
'CRITICAL': 'red',
'ERROR': 'red',
'PDB': 'white',
2018-07-05 23:51:32 +00:00
'WARNING': 'yellow',
'INFO': 'green',
'RUNTIME': 'white',
2018-07-05 23:51:32 +00:00
'DEBUG': 'white',
'TRANSPORT': 'cyan',
2018-07-05 23:51:32 +00:00
'GARBAGE': 'blue',
}
BOLD_PALETTE = {
'bold': {
level: f"bold_{color}" for level, color in STD_PALETTE.items()}
}
2019-12-10 03:10:15 +00:00
def get_logger(
name: str = None,
_root_name: str = _proj_name,
2019-12-10 03:10:15 +00:00
) -> logging.LoggerAdapter:
2018-07-05 23:51:32 +00:00
'''Return the package log or a sub-log for `name` if provided.
'''
log = rlog = logging.getLogger(_root_name)
2018-07-05 23:51:32 +00:00
if name and name != _proj_name:
# handling for modules that use ``get_logger(__name__)`` to
# avoid duplicate project-package token in msg output
rname, _, tail = name.partition('.')
if rname == _root_name:
name = tail
2018-07-05 23:51:32 +00:00
log = rlog.getChild(name)
log.level = rlog.level
# add our actor-task aware adapter which will dynamically look up
# the actor and task names at each log emit
2019-12-10 03:10:15 +00:00
logger = logging.LoggerAdapter(log, ActorContextInfo())
2018-07-05 23:51:32 +00:00
# additional levels
for name, val in LEVELS.items():
logging.addLevelName(val, name)
# ex. create ``logger.runtime()``
2019-12-10 03:10:15 +00:00
setattr(logger, name.lower(), partial(logger.log, val))
2018-07-05 23:51:32 +00:00
2019-12-10 03:10:15 +00:00
return logger
2018-07-05 23:51:32 +00:00
2019-12-10 03:10:15 +00:00
def get_console_log(
level: str = None,
**kwargs,
2019-12-10 03:10:15 +00:00
) -> logging.LoggerAdapter:
2018-07-05 23:51:32 +00:00
'''Get the package logger and enable a handler which writes to stderr.
Yeah yeah, i know we can use ``DictConfig``. You do it.
2018-07-05 23:51:32 +00:00
'''
log = get_logger(**kwargs) # our root logger
logger = log.logger
2018-07-05 23:51:32 +00:00
if not level:
2018-08-26 17:12:59 +00:00
return log
log.setLevel(level.upper() if not isinstance(level, int) else level)
if not any(
2018-11-09 06:52:57 +00:00
handler.stream == sys.stderr # type: ignore
for handler in logger.handlers if getattr(handler, 'stream', None)
):
handler = logging.StreamHandler()
formatter = colorlog.ColoredFormatter(
LOG_FORMAT,
datefmt=DATE_FORMAT,
log_colors=STD_PALETTE,
secondary_log_colors=BOLD_PALETTE,
style='{',
)
handler.setFormatter(formatter)
logger.addHandler(handler)
2018-07-05 23:51:32 +00:00
return log
2018-07-14 20:09:05 +00:00
2018-08-31 21:16:24 +00:00
def get_loglevel() -> Optional[str]:
2018-07-14 20:09:05 +00:00
return _default_loglevel