import logging
import logging.handlers as handlers
import sys
from contextlib import contextmanager
from pathlib import Path
from astromodels import astromodels_config
from astromodels.utils.logging import (
LogFilter,
_console_formatter,
_dev_formatter,
_usr_formatter,
astromodels_console_log_handler,
astromodels_dev_log_handler,
astromodels_usr_log_handler,
)
from astromodels.utils.valid_variable import is_valid_variable_name
from rich.console import Console
from rich.logging import RichHandler
from rich.theme import Theme
from threeML.config.config import threeML_config
# set up the console logging
[docs]
def invalid_plugin_name(name: str, log: logging.Logger) -> None:
if not is_valid_variable_name(name):
log.error(
f"Name {name} is not a valid name for a plugin. You must use a name which is "
"a valid python identifier: no spaces, no operators (+,-,/,*), "
"it cannot start with a number, no special characters"
)
raise AssertionError(
f"Name {name} is not a valid name for a plugin. You must use a name which is "
"a valid python identifier: no spaces, no operators (+,-,/,*), "
"it cannot start with a number, no special characters"
)
[docs]
def get_path_of_log_dir() -> Path:
"""
get the path to the logging directory
"""
log_path: Path = Path(threeML_config.logging.path).expanduser()
if not log_path.exists():
log_path.mkdir(parents=True)
return log_path
_log_file_names = ["usr.log", "dev.log"]
[docs]
def get_path_of_log_file(log_file: str) -> Path:
"""
returns the path of the log files
"""
assert (
log_file in _log_file_names
), f"{log_file} is not one of {_log_file_names}"
return get_path_of_log_dir() / log_file
# now create the developer handler that rotates every day and keeps
# 10 days worth of backup
threeML_dev_log_handler = handlers.TimedRotatingFileHandler(
get_path_of_log_file("dev.log"), when="D", interval=1, backupCount=10
)
# lots of info written out
threeML_dev_log_handler.setFormatter(_dev_formatter)
threeML_dev_log_handler.setLevel(logging.DEBUG)
# now set up the usr log which will save the info
threeML_usr_log_handler = handlers.TimedRotatingFileHandler(
get_path_of_log_file("usr.log"), when="D", interval=1, backupCount=10
)
threeML_usr_log_handler.setLevel(logging.INFO)
# lots of info written out
threeML_usr_log_handler.setFormatter(_usr_formatter)
# now set up the console logger
_theme = {}
# Banner
_theme["h1"] = "deep_sky_blue3"
_theme["status.spinner"] = "cyan2"
_theme["status.text"] = "deep_sky_blue4"
_theme["repr.filename"] = "blue"
_theme["repr.number"] = "white"
_theme["repr.path"] = "grey37"
_theme["repr.str"] = "grey37"
_theme["repr.tag_name"] = "white"
_theme["repr.url"] = "not bold not italic underline grey84"
_theme["log.time"] = "green1"
_theme["log.message"] = f"{astromodels_config.logging.message_style}"
_theme["logging.level.debug"] = f"{astromodels_config.logging.debug_style}"
_theme["logging.level.error"] = f"{astromodels_config.logging.error_style}"
_theme["logging.level.info"] = f"{astromodels_config.logging.info_style}"
_theme["logging.level.warning"] = f"{astromodels_config.logging.warn_style}"
# mytheme = Theme().read(_get_data_file_path("log_theme.ini"))
mytheme = Theme(_theme)
console = Console(theme=mytheme)
threeML_console_log_handler = RichHandler(
level="INFO", rich_tracebacks=True, markup=True, console=console
)
threeML_console_log_handler.setFormatter(_console_formatter)
threeML_console_log_handler.setLevel(threeML_config.logging.level)
astromodels_console_log_handler.setLevel(threeML_config.logging.level)
warning_filter = LogFilter(logging.WARNING)
####
# These control the verbosity of 3ML
####
[docs]
class LoggingState(object):
def __init__(
self,
threeML_usr_log_handler,
threeML_console_log_handler,
astromodels_usr_log_handler,
astromodels_console_log_handler,
):
"""
A container to store the stat of the logs
"""
# attach the log handlers
self.threeML_usr_log_handler = threeML_usr_log_handler
self.threeML_console_log_handler = threeML_console_log_handler
self.astromodels_usr_log_handler = astromodels_usr_log_handler
self.astromodels_console_log_handler = astromodels_console_log_handler
# store their current states
self.threeML_usr_log_handler_state = threeML_usr_log_handler.level
self.threeML_console_log_handler_state = (
threeML_console_log_handler.level
)
self.astromodels_usr_log_handler_state = (
astromodels_usr_log_handler.level
)
self.astromodels_console_log_handler_state = (
astromodels_console_log_handler.level
)
def _store_state(self):
self.threeML_usr_log_handler_state = threeML_usr_log_handler.level
self.threeML_console_log_handler_state = (
threeML_console_log_handler.level
)
self.astromodels_usr_log_handler_state = (
astromodels_usr_log_handler.level
)
self.astromodels_console_log_handler_state = (
astromodels_console_log_handler.level
)
[docs]
def restore_last_state(self):
self.threeML_usr_log_handler.setLevel(
self.threeML_usr_log_handler_state
)
self.threeML_console_log_handler.setLevel(
self.threeML_console_log_handler_state
)
self.astromodels_usr_log_handler.setLevel(
self.astromodels_usr_log_handler_state
)
self.astromodels_console_log_handler.setLevel(
self.astromodels_console_log_handler_state
)
[docs]
def silence_logs(self):
# store the state
self._store_state()
# silence the logs
self.threeML_usr_log_handler.setLevel(logging.CRITICAL)
self.threeML_console_log_handler.setLevel(logging.CRITICAL)
self.astromodels_usr_log_handler.setLevel(logging.CRITICAL)
self.astromodels_console_log_handler.setLevel(logging.CRITICAL)
[docs]
def loud_logs(self):
# store the state
self._store_state()
# silence the logs
self.threeML_usr_log_handler.setLevel(logging.INFO)
self.threeML_console_log_handler.setLevel(logging.INFO)
self.astromodels_usr_log_handler.setLevel(logging.INFO)
self.astromodels_console_log_handler.setLevel(logging.INFO)
[docs]
def debug_logs(self):
# store the state
self._store_state()
# silence the logs
self.threeML_console_log_handler.setLevel(logging.DEBUG)
self.astromodels_console_log_handler.setLevel(logging.DEBUG)
_log_state = LoggingState(
threeML_usr_log_handler,
threeML_console_log_handler,
astromodels_usr_log_handler,
astromodels_console_log_handler,
)
[docs]
def silence_progress_bars():
"""
Turn off the progress bars
"""
threeML_config["interface"]["progress_bars"] = "off"
[docs]
def activate_progress_bars():
"""
Turn on the progress bars
"""
threeML_config["interface"]["progress_bars"] = "on"
[docs]
def toggle_progress_bars():
"""
toggle the state of the progress bars
"""
state = threeML_config["interface"]["progress_bars"]
threeML_config["interface"]["progress_bars"] = not state
[docs]
def silence_warnings():
"""
supress warning messages in console and file usr logs
"""
threeML_usr_log_handler.addFilter(warning_filter)
threeML_console_log_handler.addFilter(warning_filter)
astromodels_usr_log_handler.addFilter(warning_filter)
astromodels_console_log_handler.addFilter(warning_filter)
[docs]
def activate_warnings():
"""
supress warning messages in console and file usr logs
"""
threeML_usr_log_handler.removeFilter(warning_filter)
threeML_console_log_handler.removeFilter(warning_filter)
astromodels_usr_log_handler.removeFilter(warning_filter)
astromodels_console_log_handler.removeFilter(warning_filter)
[docs]
def update_logging_level(level):
"""
update the logging level to the console
"""
threeML_console_log_handler.setLevel(level)
astromodels_console_log_handler.setLevel(level)
[docs]
def silence_logs():
"""
Turn off all logging
"""
# handle dev logs independently
threeML_dev_log_handler.setLevel(logging.CRITICAL)
astromodels_dev_log_handler.setLevel(logging.CRITICAL)
_log_state.silence_logs()
[docs]
def quiet_mode():
"""
turn off all logging and progress bars
"""
silence_progress_bars()
# save state and silence
silence_logs()
[docs]
def loud_mode():
"""
turn on all progress bars and logging
"""
activate_progress_bars()
# save state and make loud
_log_state.loud_logs()
[docs]
def activate_logs():
"""
re-activate silenced logs
"""
# handle dev logs independently
threeML_dev_log_handler.setLevel(logging.DEBUG)
astromodels_dev_log_handler.setLevel(logging.DEBUG)
_log_state.restore_last_state()
[docs]
def debug_mode():
"""
activate debug in the console
"""
# store state and switch console to debug
_log_state.debug_logs()
[docs]
@contextmanager
def silence_console_log(and_progress_bars=True):
"""
temporarily silence the console and progress bars
"""
current_console_logging_level = threeML_console_log_handler.level
current_usr_logging_level = threeML_usr_log_handler.level
threeML_console_log_handler.setLevel(logging.CRITICAL)
threeML_usr_log_handler.setLevel(logging.CRITICAL)
if and_progress_bars:
progress_state = threeML_config.interface.progress_bars
threeML_config.interface.progress_bars = "off"
try:
yield
finally:
threeML_console_log_handler.setLevel(current_console_logging_level)
threeML_usr_log_handler.setLevel(current_usr_logging_level)
if and_progress_bars:
threeML_config.interface.progress_bars = progress_state
[docs]
def setup_logger(name: str) -> logging.Logger:
# A logger with name name will be created
# and then add it to the print stream
log = logging.getLogger(name)
# this must be set to allow debug messages through
log.setLevel(logging.DEBUG)
# add the handlers
if threeML_config["logging"]["developer"]:
log.addHandler(threeML_dev_log_handler)
else:
# if we do not want to log developer
# for 3ML, then lets not for astromodels
astromodels_dev_log_handler.setLevel(logging.CRITICAL)
if threeML_config["logging"]["console"]:
log.addHandler(threeML_console_log_handler)
if threeML_config["logging"]["usr"]:
log.addHandler(threeML_usr_log_handler)
# we do not want to duplicate teh messages in the parents
log.propagate = False
return log