#!/usr/bin/env python
"""
A set of logging utility functions that can be used by agents developed for the platform.
"""
from __future__ import annotations
import datetime
import logging
import os
import sys
from pathlib import Path
import colorlog
from negmas.config import negmas_config
__all__ = ["create_loggers"]
LOGS_BASE_DIR = Path(negmas_config("log_base", Path.home() / "negmas" / "logs"))
COMMON_LOG_FILE_NAME = str(
Path(LOGS_BASE_DIR)
/ "{}_{}.txt".format("log", datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
)
MODULE_LOG_FILE_NAME: dict[str, str] = dict()
[docs]
def create_loggers(
file_name: str | None = None,
module_name: str | None = None,
screen_level: int | None = logging.WARNING,
file_level: int | None = logging.DEBUG,
format_str: str = "%(asctime)s - %(levelname)s - %(message)s",
colored: bool = True,
app_wide_log_file: bool = True,
module_wide_log_file: bool = False,
) -> logging.Logger:
"""
Create a set of loggers to report feedback.
The logger created can log to both a file and the screen at the same time
with adjustable level for each of them. The default is to log everything to
the file and to log WARNING at least to the screen
Args:
module_wide_log_file:
app_wide_log_file:
file_name: The file to export_to the logs to. If None only the screen
is used for logging. If empty, a time-stamp is used
module_name: The module name to use. If not given the file name
without .py is used
screen_level: level of the screen logger
file_level: level of the file logger
format_str: the format of logged items
colored: whether or not to try using colored logs
Returns:
logging.Logger: The logger
"""
if module_name is None:
module_name = __file__.split("/")[-1][:-3]
# create logger if it does not already exist
logger = None
if module_wide_log_file or app_wide_log_file:
logger = logging.getLogger(module_name)
if len(logger.handlers) > 0:
return logger
logger.setLevel(logging.DEBUG)
else:
logger = logging.getLogger()
# create formatter
file_formatter = logging.Formatter(format_str)
if colored and "colorlog" in sys.modules and os.isatty(2) and screen_level:
date_format = "%Y-%m-%d %H:%M:%S"
cformat = "%(log_color)s" + format_str
screen_formatter = colorlog.ColoredFormatter(
cformat,
date_format,
log_colors={
"DEBUG": "magenta",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "bold_red",
},
)
else:
screen_formatter = logging.Formatter(format_str)
if screen_level is not None and (module_wide_log_file or app_wide_log_file):
# create console handler and set level to logdebug
screen_logger = logging.StreamHandler()
screen_logger.setLevel(screen_level)
# add formatter to ch
screen_logger.setFormatter(screen_formatter)
# add ch to logger
logger.addHandler(screen_logger)
if file_name is not None and file_level is not None:
file_name = str(file_name)
logger = logging.getLogger(file_name)
logger.setLevel(file_level)
if len(file_name) == 0:
if app_wide_log_file:
file_name = COMMON_LOG_FILE_NAME
elif module_wide_log_file and module_name in MODULE_LOG_FILE_NAME.keys():
file_name = MODULE_LOG_FILE_NAME[module_name]
else:
file_name = "{}/{}_{}.txt".format(
LOGS_BASE_DIR,
module_name,
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"),
)
MODULE_LOG_FILE_NAME[module_name] = file_name
os.makedirs(f"{LOGS_BASE_DIR}", exist_ok=True)
os.makedirs(os.path.dirname(file_name), exist_ok=True) # type: ignore
file_logger = logging.FileHandler(file_name)
file_logger.setLevel(file_level)
file_logger.setFormatter(file_formatter)
logger.addHandler(file_logger)
return logger