Add proper logging mechanism to application

This mechanism has custom coloring, support to dump in file and
on the commandline and support to turn either or both off. cli/cli.py
provies a function that can be called in each module to instantiate
a logger for that module.
This commit is contained in:
Dennis Potter 2021-05-10 00:59:52 +02:00
parent c32bfdd8c0
commit 27c4e9de3c
Signed by: Dennis
GPG Key ID: 186A8AD440942BAF
4 changed files with 107 additions and 15 deletions

View File

@ -1,29 +1,46 @@
import argparse import argparse
import os import os
import time
import logging
from itertools import chain from itertools import chain
logging_map = {
"DEBUG": logging.DEBUG,
"INFO": logging.INFO,
"WARNING": logging.WARNING,
"ERROR": logging.ERROR,
"CRITICAL": logging.CRITICAL,
"NONE": logging.NOTSET
}
class CliArguments(): class CliArguments():
def __init__(self): def __init__(self):
self.parser = argparse.ArgumentParser( self.parser = argparse.ArgumentParser(
description="SystemRDL 2 SystemVerilog compiler") description="SystemRDL 2 SystemVerilog compiler")
self.parser.add_argument( self.parser.add_argument(
"-v", "--stream_log_level",
"--verbosity", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'],
action="count", default='WARNING',
help="Increase output verbosity.") help="Set verbosity level of output to shell. When set to 'NONE',\
nothing will be printed to the shell. (default: %(default)s)")
self.parser.add_argument( self.parser.add_argument(
"-q", "--file_log_level",
"--quiet", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'NONE'],
action="store_true") default='INFO',
help="Set verbosity level of output to log-file. When set to 'NONE',\
nothing will be printed to the shell. (default: %(default)s)")
self.parser.add_argument( self.parser.add_argument(
"-o", "-o",
"--out_dir", "--out_dir",
type=str, type=str,
default="./srdl2sv_out",
help="Define output directory to dump files.\ help="Define output directory to dump files.\
If directory is non-existent, it will be created.") If directory is non-existent, it will be created.\
(default: %(default)s)")
self.parser.add_argument( self.parser.add_argument(
"-d", "-d",
@ -49,15 +66,29 @@ class CliArguments():
def get_config(self) -> dict(): def get_config(self) -> dict():
args = self.parser.parse_args() args = self.parser.parse_args()
# Create dictionary to save config in
config = dict() config = dict()
# Save input file and output directory to dump everything in
config['input_file'] = args.IN_RDL config['input_file'] = args.IN_RDL
config['verbosity'] = args.verbosity config['output_dir'] = args.out_dir
config['quiet'] = args.quiet
# Map logging level string to integers
config['stream_log_level'] = logging_map[args.stream_log_level]
config['file_log_level'] = logging_map[args.file_log_level]
# Determine paths to be passed to systemrdl-compiler to search
# for include files.
if args.recursive_search: if args.recursive_search:
config['search_paths'] = [x[0] for y in args.search_paths for x in os.walk(y)] config['search_paths'] = [x[0] for y in args.search_paths for x in os.walk(y)]
else: else:
config['search_paths'] = args.search_paths config['search_paths'] = args.search_paths
# Save timestamp, so that it can be used across the compiler
config['ts'] = time.localtime()
# Determine name of file to hold logs
ts = time.strftime('%Y%m%d_%H%M%S', config['ts'])
config['file_log_location'] = "srdl2sv_{}.log".format(ts)
return config return config

0
srdl2sv/log/__init__.py Normal file
View File

59
srdl2sv/log/log.py Normal file
View File

@ -0,0 +1,59 @@
import logging
from typing import Optional
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
MAPPING = {
'DEBUG' : 37, # white
'INFO' : 32, # green
'WARNING' : 33, # yellow
'ERROR' : 31, # red
'CRITICAL': 41, # white on red bg
}
PREFIX = '\033['
SUFFIX = '\033[0m'
def format(self, record):
colored_record = record
levelname = colored_record.levelname
seq = CustomFormatter.MAPPING.get(levelname, 37) # default white
colored_levelname = ('{0}{1}m{2}{3}') \
.format(
CustomFormatter.PREFIX,
seq,
levelname,
CustomFormatter.SUFFIX)
colored_record.levelname = colored_levelname
return logging.Formatter.format(self, colored_record)
def create_logger (
mod_name,
stream_log_level: int = logging.WARNING,
file_log_level: int = logging.INFO,
file_name: Optional[str] = None):
log = logging.getLogger(mod_name)
log.setLevel(min(stream_log_level, file_log_level))
if file_log_level > 0 and file_name:
file_handler = logging.FileHandler(file_name)
file_handler.setLevel(file_log_level)
file_formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - %(name)s: %(message)s")
file_handler.setFormatter(file_formatter)
log.addHandler(file_handler)
if stream_log_level > 0:
stream_handler = logging.StreamHandler()
stream_handler.setLevel(stream_log_level)
stream_formatter = CustomFormatter(
"[%(levelname)s][%(name)s] %(message)s")
stream_handler.setFormatter(stream_formatter)
log.addHandler(stream_handler)
return log

View File

@ -10,6 +10,7 @@ from systemrdl import RDLCompiler, RDLCompileError
# Local modules # Local modules
from components.addrmap import AddrMap from components.addrmap import AddrMap
from cli.cli import CliArguments from cli.cli import CliArguments
from log.log import create_logger
if __name__ == "__main__": if __name__ == "__main__":
# Take start timestamp # Take start timestamp
@ -33,8 +34,9 @@ if __name__ == "__main__":
addrmap = AddrMap(rdlc, root.top) addrmap = AddrMap(rdlc, root.top)
print("====================================================") logger = create_logger(
print("Elapsed time: {} seconds".format(time.time() - start)) __name__,
print("====================================================") stream_log_level=config['stream_log_level'],
file_log_level=config['file_log_level'],
file_name=config['file_log_location'])
logger.info("Elapsed time: %f seconds", time.time() - start)