dts: add basic logging

The logging module provides loggers distinguished by two attributes,
a custom format and a verbosity switch. The loggers log to both console
and more verbosely to files.

Signed-off-by: Owen Hilyard <ohilyard@iol.unh.edu>
Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
This commit is contained in:
Juraj Linkeš 2022-11-04 11:05:18 +00:00 committed by Thomas Monjalon
parent 995fb3372e
commit 179d705936
3 changed files with 139 additions and 0 deletions

3
.gitignore vendored
View File

@ -36,6 +36,9 @@ TAGS
# ignore python bytecode files # ignore python bytecode files
*.pyc *.pyc
# DTS results
dts/output
# ignore default build directory, and directories from test-meson-builds.sh # ignore default build directory, and directories from test-meson-builds.sh
build build
build-* build-*

113
dts/framework/logger.py Normal file
View File

@ -0,0 +1,113 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
# Copyright(c) 2022 PANTHEON.tech s.r.o.
# Copyright(c) 2022 University of New Hampshire
"""
DTS logger module with several log level. DTS framework and TestSuite logs
are saved in different log files.
"""
import logging
import os.path
from typing import TypedDict
from .settings import SETTINGS
date_fmt = "%Y/%m/%d %H:%M:%S"
stream_fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
class LoggerDictType(TypedDict):
logger: "DTSLOG"
name: str
node: str
# List for saving all using loggers
Loggers: list[LoggerDictType] = []
class DTSLOG(logging.LoggerAdapter):
"""
DTS log class for framework and testsuite.
"""
logger: logging.Logger
node: str
sh: logging.StreamHandler
fh: logging.FileHandler
verbose_fh: logging.FileHandler
def __init__(self, logger: logging.Logger, node: str = "suite"):
self.logger = logger
# 1 means log everything, this will be used by file handlers if their level
# is not set
self.logger.setLevel(1)
self.node = node
# add handler to emit to stdout
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter(stream_fmt, date_fmt))
sh.setLevel(logging.INFO) # console handler default level
if SETTINGS.verbose is True:
sh.setLevel(logging.DEBUG)
self.logger.addHandler(sh)
self.sh = sh
logging_path_prefix = os.path.join(SETTINGS.output_dir, node)
fh = logging.FileHandler(f"{logging_path_prefix}.log")
fh.setFormatter(
logging.Formatter(
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt=date_fmt,
)
)
self.logger.addHandler(fh)
self.fh = fh
# This outputs EVERYTHING, intended for post-mortem debugging
# Also optimized for processing via AWK (awk -F '|' ...)
verbose_fh = logging.FileHandler(f"{logging_path_prefix}.verbose.log")
verbose_fh.setFormatter(
logging.Formatter(
fmt="%(asctime)s|%(name)s|%(levelname)s|%(pathname)s|%(lineno)d|"
"%(funcName)s|%(process)d|%(thread)d|%(threadName)s|%(message)s",
datefmt=date_fmt,
)
)
self.logger.addHandler(verbose_fh)
self.verbose_fh = verbose_fh
super(DTSLOG, self).__init__(self.logger, dict(node=self.node))
def logger_exit(self) -> None:
"""
Remove stream handler and logfile handler.
"""
for handler in (self.sh, self.fh, self.verbose_fh):
handler.flush()
self.logger.removeHandler(handler)
def getLogger(name: str, node: str = "suite") -> DTSLOG:
"""
Get logger handler and if there's no handler for specified Node will create one.
"""
global Loggers
# return saved logger
logger: LoggerDictType
for logger in Loggers:
if logger["name"] == name and logger["node"] == node:
return logger["logger"]
# return new logger
dts_logger: DTSLOG = DTSLOG(logging.getLogger(name), node)
Loggers.append({"logger": dts_logger, "name": name, "node": node})
return dts_logger

View File

@ -57,6 +57,8 @@ def __call__(
@dataclass(slots=True, frozen=True) @dataclass(slots=True, frozen=True)
class _Settings: class _Settings:
config_file_path: str config_file_path: str
output_dir: str
verbose: bool
def _get_parser() -> argparse.ArgumentParser: def _get_parser() -> argparse.ArgumentParser:
@ -71,6 +73,25 @@ def _get_parser() -> argparse.ArgumentParser:
"and targets.", "and targets.",
) )
parser.add_argument(
"--output-dir",
"--output",
action=_env_arg("DTS_OUTPUT_DIR"),
default="output",
required=False,
help="[DTS_OUTPUT_DIR] Output directory where dts logs and results are saved.",
)
parser.add_argument(
"-v",
"--verbose",
action=_env_arg("DTS_VERBOSE"),
default="N",
required=False,
help="[DTS_VERBOSE] Set to 'Y' to enable verbose output, logging all messages "
"to the console.",
)
return parser return parser
@ -78,6 +99,8 @@ def _get_settings() -> _Settings:
parsed_args = _get_parser().parse_args() parsed_args = _get_parser().parse_args()
return _Settings( return _Settings(
config_file_path=parsed_args.config_file, config_file_path=parsed_args.config_file,
output_dir=parsed_args.output_dir,
verbose=(parsed_args.verbose == "Y"),
) )