Source code for pypushflow.logutils
import os
import sys
import warnings
import logging
import logging.handlers
from pathlib import Path
DEFAULT_FORMAT = "%(levelname)-8s %(asctime)s [%(threadName)s] %(message)s"
DEFAULT_LOGDIR = os.path.join(os.path.sep, "tmp_14_days")
[docs]
class PyPushflowLoggedObject:
"""This object owns a logger which adds workflow related context to the logs"""
def __init__(self, log_metadata=None, parent=None):
if log_metadata:
if "class" not in log_metadata:
log_metadata["class"] = type(self).__name__
else:
log_metadata = {"class": type(self).__name__}
if parent:
logger = parent.logger
else:
logger = logging.getLogger("pypushflow")
self.logger = _PypushflowLoggerAdapter(logger, log_metadata)
[docs]
def basicConfig(filename=None, logdir=None, user=None, initiator=None, **kwargs):
"""Basic configuration for the root logger. By default:
* set log level to INFO
* add STDOUT stream handler
Has no effect when there are already handlers unless `force=True`.
Note: pytest installs handlers.
"""
warnings.warn(
"logging configuration in pypushflow is deprecated", DeprecationWarning
)
kwargs.setdefault("format", DEFAULT_FORMAT)
kwargs.setdefault("level", logging.INFO)
kwargs.setdefault("stream", sys.stdout)
user = user or "unknown"
initiator = initiator or "unknown"
force = kwargs.setdefault("force", False)
logging.basicConfig(**kwargs)
logger = logging.getLogger()
for handler in logger.handlers:
if isinstance(handler, _PyPushflowLogFile):
logger.removeHandler(handler)
if (len(logger.handlers) == 0 or force) and filename:
if logdir is None:
logdir = DEFAULT_LOGDIR
filename = os.path.join(logdir, filename)
handler = _PyPushflowLogFile.factory(filename, user, initiator)
logger.addHandler(handler)
class _PypushflowLoggerAdapter(logging.LoggerAdapter):
"""Log adapter which adds workflow related context (if any) to the log messages"""
def process(self, msg, kwargs):
kwargs["extra"] = self.extra
if self.extra:
if "workflow" in self.extra:
ctx = "[%s]" % (self.extra["workflow"],)
elif "actor" in self.extra:
ctx = "[<%s> %s]" % (
self.extra["class"],
self.extra["actor"],
)
else:
ctx = None
if ctx:
msg = "%s %s" % (ctx, msg)
return msg, kwargs
class _PyPushflowLogFile(logging.handlers.RotatingFileHandler):
MAXBYTES = 10 << 20
BACKUPCOUNT = 10
SUFFIX = ".log"
@classmethod
def factory(cls, filename: str, user: str, initiator: str):
log_file_path = cls.__getLogFile(filename)
if log_file_path is None:
return None
handler = cls(log_file_path, maxBytes=cls.MAXBYTES, backupCount=cls.BACKUPCOUNT)
formatter = logging.Formatter(DEFAULT_FORMAT)
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)
return handler
@classmethod
def __getLogFile(cls, filename: str, user: str, initiator: str):
if not filename:
return None
filename = Path(filename).with_suffix(cls.SUFFIX)
directory = filename.parent / user / initiator
try:
directory.mkdir(mode=0o755, parents=True, exist_ok=True)
except Exception:
return None
return directory / filename.name