Skip to content

Commit

Permalink
Add docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
ayjayt committed Dec 18, 2024
1 parent e6a36b4 commit 478d48a
Showing 1 changed file with 50 additions and 3 deletions.
53 changes: 50 additions & 3 deletions logistro/custom_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@

## New Constants and Globals

#: A more verbose debug
DEBUG2 = 5
logging.addLevelName(DEBUG2, "DEBUG2")

pipe_attr_blacklist = ["filename", "funcName", "threadName"]
#: These are useless when logging external process output. See getPipeLogger()
pipe_attr_blacklist = ["filename", "funcName", "threadName", "taskName"]

# Our basic formatting list
output = {
"time": "%(asctime)s",
"name": "%(name)s",
Expand All @@ -24,21 +27,27 @@
"thread": "%(threadName)s",
"message": "%(message)s",
}
# async taskName not supported below 3.12

# A more readable, human readable string
date_string = "%a, %d-%b %H:%M:%S"

# async taskName not supported below 3.12, remove it
if bool(sys.version_info[:3] < (3, 12)):
del output["task"]

# make human output a little more readable
output_human = output.copy()
output_human["func"] += "()"

date_string = "%a, %d-%b %H:%M:%S"
# generate format string
human_formatter = logging.Formatter(
":".join(output_human.values()),
datefmt=date_string,
)
structured_formatter = logging.Formatter(json.dumps(output))


# We set this as the logging class just to add a debug1 and debug2 function
class LogistroLogger(logging.getLoggerClass()):
def debug1(self, msg, *args, **kwargs):
super().log(logging.DEBUG, msg, *args, stacklevel=2, **kwargs)
Expand All @@ -51,14 +60,26 @@ def debug2(self, msg, *args, **kwargs):


def set_human():
"""set_human() is the same as passing --logistro-human (default)"""
args.parsed.human = True

"""set_structured() is the same as passing --logistro-structured"""


def set_structured():
args.parsed.human = False


def coerce_logger(logger, formatter=None):
"""coerce_logger() will loop through all of a logger's formatters and set
either the formatter specified, or default to human/structured based on flags
or if a set_* function was called
:param logger: The logger to coerce
:param formatter: The logging.Formatter() objecto use- defaults to
human_formatter or structured_formatter
"""
if not formatter:
if args.parsed.human:
formatter = human_formatter
Expand All @@ -80,13 +101,21 @@ def wrapper(*args, **kwargs):

@run_once
def betterConfig(**kwargs):
"""betterConfig is a wrapper over logging.basicConfig which
provides our defaults (level set by --logistro-level and formatter
set to human/structured. Currently, it will overwrite any format
or datefmt arguments passed. It is only ever run once.
"""
if "level" not in kwargs:
kwargs["level"] = args.parsed.log.upper()
logging.basicConfig(**kwargs)
coerce_logger(logging.getLogger())


def getLogger(name=None):
"""getLogger() is a simple wrapper for logging.getLogger()
which calls betterConfig() first."""
betterConfig()
logger = logging.getLogger(name)
return logger
Expand All @@ -113,6 +142,24 @@ def getPipeLogger(
default_level=logging.DEBUG,
IFS="\n",
):
"""getPipeLogger() is a special getLogger which returns a pipe and logger.
It spins a separate thread that listens on the pipe and outputs everything
it reads to the logger named. It is used, for example, to redirect Popen()
stderr to the logs. You should close the pipe `os.close(pipe)` to be sure
when exiting your program- in case the other side hangs or freezes.
:param name: the name of the logger (ie logging.getLogger(name))
:param parser: an optional function which accepts a LogRecord (see logging
docs) as well as a dictionary of already stripped values.
The signature of parser should be: fn(record, old). It functions like
a logging.Filter, and can be used to parse the other processes output
if it's structured. The argument `old` is a dictionary of metadata
that we strip from the log record due to irrelevancy, see the
pipe_attr_blacklist list.
:param default_level: the default level to pipe these logs to. Can be modified
with the parser as described above.
:param IFS: (Internal Field Separate) The character used to delimit between
process output, defaults to "\n".
"""
IFS = IFS.encode("utf-8")
logger = getLogger(name)
logger.addFilter(PipeLoggerFilter(parser))
Expand Down

0 comments on commit 478d48a

Please sign in to comment.