Source code for smif.cli.log
import datetime
import logging
import logging.config
from collections import OrderedDict
# Make profiling methods available through the logger
[docs]
def profiling_start(self, operation, key):
time_placeholder = datetime.time(0, 0)
level = sum(
log[1]["stop"] == time_placeholder for log in logging.Logger._profile.items()
)
logging.Logger._profile[(operation, key)] = {
"start": datetime.datetime.now(),
"stop": time_placeholder,
"level": level,
}
[docs]
def profiling_stop(self, operation, key):
logging.Logger._profile[(operation, key)]["stop"] = datetime.datetime.now()
[docs]
def summary(self, *args, **kws):
if self.isEnabledFor(logging.INFO):
summary = []
columns = [30, 80, 12]
column = (
"{:"
+ str(columns[0])
+ "s}"
+ "{:"
+ str(columns[1])
+ "s}"
+ "{:"
+ str(columns[2])
+ "s}"
)
level_width = (
max([profile["level"] for profile in logging.Logger._profile.values()]) * 2
)
total_width = sum(columns) + level_width
# header
summary.append(
("{:*^" + str(total_width) + "s}").format(" Modelrun time profile ")
)
summary.append(column.format("Function", "Operation", "Duration [hh:mm:ss]"))
summary.append("*" * total_width)
# body
for profile in logging.Logger._profile.keys():
# calculate time diff
profile_data = logging.Logger._profile[profile]
diff = profile_data["stop"] - profile_data["start"]
s = diff.total_seconds()
time_spent = "{:02d}:{:02d}:{:05.2f}".format(
int(s // 3600), int(s % 3600 // 60), s % 60
)
# trunctuate long lines
if len(profile[0]) > columns[0] - 2:
func = profile[0][: columns[0] - 3] + ".."
else:
func = profile[0]
if len(profile[1]) > columns[1] - 2:
op = profile[1][: columns[1] - 3] + ".."
else:
op = profile[1]
summary.append(
profile_data["level"] * "| " + column.format(func, op, time_spent)
)
# footer
summary.append("*" * total_width)
for entry in summary:
self._log(logging.INFO, entry, args)
[docs]
def setup_logging(loglevel):
config = {
"version": 1,
"formatters": {
"default": {
"format": "%(asctime)s %(name)-12s: %(levelname)-8s %(message)s"
},
"message": {"format": "\033[1;34m%(levelname)-8s\033[0m %(message)s"},
},
"handlers": {
"file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "default",
"filename": "smif.log",
"mode": "a",
"encoding": "utf-8",
},
"stream": {
"class": "logging.StreamHandler",
"formatter": "message",
"level": "DEBUG",
},
},
"root": {"handlers": ["file", "stream"], "level": "DEBUG"},
# disable_existing_loggers defaults to True, which causes problems with class/module
# -specific loggers, especially in unit tests when this method might be called multiple
# times
"disable_existing_loggers": False,
}
if loglevel is None:
config["root"]["level"] = logging.WARNING
elif loglevel == 1:
config["root"]["level"] = logging.INFO
else:
config["root"]["level"] = logging.DEBUG
logging.config.dictConfig(config)
logging.debug("Debug logging enabled.")
logging.Logger.profiling_start = profiling_start
logging.Logger.profiling_stop = profiling_stop
logging.Logger.summary = summary
logging.Logger._profile = OrderedDict()