From 7d7f5f3025e930e4e1e304e6e94930c8de54e53c Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Thu, 11 Jul 2024 16:34:03 -0400 Subject: [PATCH 01/10] Added Output widgets for logging within Tabs --- tardis/io/logger/logger.py | 41 ++++++++++++++++++++++++++++++++------ tardis/simulation/base.py | 2 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 70fcabcc4e9..b4a78b6debe 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -1,17 +1,46 @@ import logging import sys +from ipywidgets import Output, Tab, Layout +from IPython.display import display from tardis.io.logger.colored_logger import ColoredFormatter, formatter_message logging.captureWarnings(True) logger = logging.getLogger("tardis") -console_handler = logging.StreamHandler(sys.stdout) -console_formatter = ColoredFormatter() -console_handler.setFormatter(console_formatter) +# Create Output widgets for each log level +log_outputs = { + "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), + "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), + "WARNING": Output(layout=Layout(height='300px', overflow_y='auto')), + "ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), + "CRITICAL": Output(layout=Layout(height='300px', overflow_y='auto')) +} + +# Create a Tab widget to hold the Output widgets +tab = Tab(children=[log_outputs[level] for level in log_outputs.keys()]) +for i, level in enumerate(log_outputs.keys()): + tab.set_title(i, level) + +display(tab) + +class WidgetHandler(logging.Handler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def emit(self, record): + log_entry = self.format(record) + level_name = record.levelname + if level_name in log_outputs: + with log_outputs[level_name]: + print(log_entry) + +# Setup widget handler +widget_handler = WidgetHandler() +widget_handler.setFormatter(ColoredFormatter()) -logger.addHandler(console_handler) -logging.getLogger("py.warnings").addHandler(console_handler) +logger.addHandler(widget_handler) +logging.getLogger("py.warnings").addHandler(widget_handler) LOGGING_LEVELS = { "NOTSET": logging.NOTSET, @@ -49,7 +78,7 @@ def filter(self, log_record): Parameters ---------- log_record : logging.record - which the paricular record upon which the + which the particular record upon which the filter will be applied Returns diff --git a/tardis/simulation/base.py b/tardis/simulation/base.py index 92a658b80d7..bcd0596a286 100644 --- a/tardis/simulation/base.py +++ b/tardis/simulation/base.py @@ -627,7 +627,7 @@ def log_plasma_state( plasma_state_log["next_w"] = next_dilution_factor plasma_state_log.columns.name = "Shell No." - if is_notebook(): + if False and is_notebook(): #TODO: remove it with something better logger.info("\n\tPlasma stratification:") # Displaying the DataFrame only when the logging level is NOTSET, DEBUG or INFO From 619033fc8ffaa8c9be13c765611838663623a480 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Fri, 12 Jul 2024 13:59:49 -0400 Subject: [PATCH 02/10] Fixed the text not showing in the logging tabs --- docs/quickstart.ipynb | 2 +- tardis/io/logger/logger.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.ipynb b/docs/quickstart.ipynb index 0f05cc333c8..129d9fcd8fa 100644 --- a/docs/quickstart.ipynb +++ b/docs/quickstart.ipynb @@ -116,7 +116,7 @@ "sim = run_tardis(\"tardis_example.yml\", \n", " virtual_packet_logging=True,\n", " show_convergence_plots=True,\n", - " export_convergence_plots=True,\n", + " export_convergence_plots=False, #TODO: to avoid double plots\n", " log_level=\"INFO\") " ] }, diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index b4a78b6debe..2b824141b02 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -37,7 +37,7 @@ def emit(self, record): # Setup widget handler widget_handler = WidgetHandler() -widget_handler.setFormatter(ColoredFormatter()) +widget_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) # TODO: Make ColoredFormatter work here logger.addHandler(widget_handler) logging.getLogger("py.warnings").addHandler(widget_handler) From 6a6d00ecef68558686c6f2bb1729d8ef39c0a04f Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Tue, 16 Jul 2024 11:19:34 -0400 Subject: [PATCH 03/10] Changed the logger widget to display two tabs "DEBUG/INFO" and "WARNING/ERROR" instead of the initial five tabs --- tardis/io/logger/logger.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 2b824141b02..66a00214b72 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -10,11 +10,8 @@ # Create Output widgets for each log level log_outputs = { - "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), - "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), - "WARNING": Output(layout=Layout(height='300px', overflow_y='auto')), - "ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), - "CRITICAL": Output(layout=Layout(height='300px', overflow_y='auto')) + "DEBUG/INFO": Output(layout=Layout(height='300px', overflow_y='auto')), + "WARNING/ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), } # Create a Tab widget to hold the Output widgets @@ -30,9 +27,11 @@ def __init__(self, *args, **kwargs): def emit(self, record): log_entry = self.format(record) - level_name = record.levelname - if level_name in log_outputs: - with log_outputs[level_name]: + if record.levelno in (logging.DEBUG, logging.INFO): + with log_outputs["DEBUG/INFO"]: + print(log_entry) + elif record.levelno in (logging.WARNING, logging.ERROR): + with log_outputs["WARNING/ERROR"]: print(log_entry) # Setup widget handler @@ -48,7 +47,6 @@ def emit(self, record): "INFO": logging.INFO, "WARNING": logging.WARNING, "ERROR": logging.ERROR, - "CRITICAL": logging.CRITICAL, } DEFAULT_LOG_LEVEL = "INFO" DEFAULT_SPECIFIC_STATE = False @@ -66,8 +64,8 @@ class FilterLog(object): particular log_level """ - def __init__(self, log_level): - self.log_level = log_level + def __init__(self, log_levels): + self.log_levels = log_levels def filter(self, log_record): """ @@ -88,7 +86,7 @@ def filter(self, log_record): False, if the current log_record doesn't have the same log_level as the specified one """ - return log_record.levelno == self.log_level + return log_record.levelno in self.log_levels def logging_state(log_level, tardis_config, specific_log_level): @@ -165,7 +163,7 @@ def logging_state(log_level, tardis_config, specific_log_level): logger.removeFilter(filter) if specific_log_level: - filter_log = FilterLog(LOGGING_LEVELS[logging_level]) + filter_log = FilterLog([LOGGING_LEVELS[logging_level], logging.INFO, logging.DEBUG]) for logger in tardis_loggers: logger.addFilter(filter_log) else: From a4efc27e4d6277dc5655d589f83ade5e2bb94251 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Tue, 16 Jul 2024 14:12:16 -0400 Subject: [PATCH 04/10] Modified the tabs to have "WARNING/ERROR", "INFO" "DEBUG" in that order --- tardis/io/logger/logger.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 66a00214b72..0264ea77ba4 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -2,22 +2,23 @@ import sys from ipywidgets import Output, Tab, Layout from IPython.display import display - -from tardis.io.logger.colored_logger import ColoredFormatter, formatter_message +from tardis.io.logger.colored_logger import ColoredFormatter logging.captureWarnings(True) logger = logging.getLogger("tardis") # Create Output widgets for each log level log_outputs = { - "DEBUG/INFO": Output(layout=Layout(height='300px', overflow_y='auto')), "WARNING/ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), + "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), + "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), } # Create a Tab widget to hold the Output widgets -tab = Tab(children=[log_outputs[level] for level in log_outputs.keys()]) -for i, level in enumerate(log_outputs.keys()): - tab.set_title(i, level) +tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"]]) +tab.set_title(0, "WARNING/ERROR") +tab.set_title(1, "INFO") +tab.set_title(2, "DEBUG") display(tab) @@ -27,16 +28,19 @@ def __init__(self, *args, **kwargs): def emit(self, record): log_entry = self.format(record) - if record.levelno in (logging.DEBUG, logging.INFO): - with log_outputs["DEBUG/INFO"]: - print(log_entry) - elif record.levelno in (logging.WARNING, logging.ERROR): + if record.levelno in (logging.WARNING, logging.ERROR): with log_outputs["WARNING/ERROR"]: print(log_entry) + elif record.levelno == logging.INFO: + with log_outputs["INFO"]: + print(log_entry) + elif record.levelno == logging.DEBUG: + with log_outputs["DEBUG"]: + print(log_entry) # Setup widget handler widget_handler = WidgetHandler() -widget_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) # TODO: Make ColoredFormatter work here +widget_handler.setFormatter(ColoredFormatter()) #TODO: Make ColoredFormatter work logger.addHandler(widget_handler) logging.getLogger("py.warnings").addHandler(widget_handler) @@ -47,6 +51,7 @@ def emit(self, record): "INFO": logging.INFO, "WARNING": logging.WARNING, "ERROR": logging.ERROR, + #"CRITICAL": logging.CRITICAL, } DEFAULT_LOG_LEVEL = "INFO" DEFAULT_SPECIFIC_STATE = False From d7da4efa231631ca2dad56b5e1ce068f54068d27 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Thu, 18 Jul 2024 13:55:25 -0400 Subject: [PATCH 05/10] Made changes to the logger to have it show the 4 tabs- WARNING/ERROR, INFO, DEBUG and ALL. --- tardis/io/logger/logger.py | 245 ++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 142 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 0264ea77ba4..1ef262090d9 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -1,115 +1,10 @@ import logging -import sys +import re from ipywidgets import Output, Tab, Layout -from IPython.display import display -from tardis.io.logger.colored_logger import ColoredFormatter - -logging.captureWarnings(True) -logger = logging.getLogger("tardis") - -# Create Output widgets for each log level -log_outputs = { - "WARNING/ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), - "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), - "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), -} - -# Create a Tab widget to hold the Output widgets -tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"]]) -tab.set_title(0, "WARNING/ERROR") -tab.set_title(1, "INFO") -tab.set_title(2, "DEBUG") - -display(tab) - -class WidgetHandler(logging.Handler): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def emit(self, record): - log_entry = self.format(record) - if record.levelno in (logging.WARNING, logging.ERROR): - with log_outputs["WARNING/ERROR"]: - print(log_entry) - elif record.levelno == logging.INFO: - with log_outputs["INFO"]: - print(log_entry) - elif record.levelno == logging.DEBUG: - with log_outputs["DEBUG"]: - print(log_entry) - -# Setup widget handler -widget_handler = WidgetHandler() -widget_handler.setFormatter(ColoredFormatter()) #TODO: Make ColoredFormatter work - -logger.addHandler(widget_handler) -logging.getLogger("py.warnings").addHandler(widget_handler) - -LOGGING_LEVELS = { - "NOTSET": logging.NOTSET, - "DEBUG": logging.DEBUG, - "INFO": logging.INFO, - "WARNING": logging.WARNING, - "ERROR": logging.ERROR, - #"CRITICAL": logging.CRITICAL, -} -DEFAULT_LOG_LEVEL = "INFO" -DEFAULT_SPECIFIC_STATE = False - - -class FilterLog(object): - """ - Filter Log Class for Filtering Logging Output - to a particular level - - Parameters - ---------- - log_level : logging object - allows to have a filter for the - particular log_level - """ - - def __init__(self, log_levels): - self.log_levels = log_levels - - def filter(self, log_record): - """ - filter() allows to set the logging level for - all the record that are being parsed & hence remove those - which are not of the particular level - - Parameters - ---------- - log_record : logging.record - which the particular record upon which the - filter will be applied - - Returns - ------- - boolean : True, if the current log_record has the - level that of the specified log_level - False, if the current log_record doesn't have the - same log_level as the specified one - """ - return log_record.levelno in self.log_levels - +from IPython.display import display, HTML +#from tardis.io.logger.colored_logger import ColoredFormatter def logging_state(log_level, tardis_config, specific_log_level): - """ - Function to set the logging configuration for the simulation output - Called from within run_tardis() - Configured via functional arguments passed through run_tardis() - log_level & specific_log_level - Configured via YAML parameters under `debug` section - log_level & specific_log_level - - Parameters - ---------- - log_level: str - Allows to input the log level for the simulation - Uses Python logging framework to determine the messages that will be output - specific_log_level: boolean - Allows to set specific logging levels. Logs of the `log_level` level would be output. - """ - if "debug" in tardis_config: specific_log_level = ( tardis_config["debug"]["specific_log_level"] @@ -121,7 +16,6 @@ def logging_state(log_level, tardis_config, specific_log_level): log_level if log_level else tardis_config["debug"]["log_level"] ) - # Displays a message when both log_level & tardis["debug"]["log_level"] are specified if log_level and tardis_config["debug"]["log_level"]: print( "log_level is defined both in Functional Argument & YAML Configuration {debug section}" @@ -131,36 +25,30 @@ def logging_state(log_level, tardis_config, specific_log_level): ) else: - # Adds empty `debug` section for the YAML tardis_config["debug"] = {} if log_level: logging_level = log_level else: - tardis_config["debug"]["log_level"] = DEFAULT_LOG_LEVEL + tardis_config["debug"]["log_level"] = "INFO" logging_level = tardis_config["debug"]["log_level"] if not specific_log_level: - tardis_config["debug"][ - "specific_log_level" - ] = DEFAULT_SPECIFIC_STATE + tardis_config["debug"]["specific_log_level"] = False specific_log_level = tardis_config["debug"]["specific_log_level"] logging_level = logging_level.upper() - if not logging_level in LOGGING_LEVELS: + if not logging_level in ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "ALL"]: raise ValueError( - f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following {list(LOGGING_LEVELS.keys())}" + f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following ['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR']" ) - # Getting the TARDIS logger & all its children loggers logger = logging.getLogger("tardis") + tardis_loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("tardis")] - # Creating a list for Storing all the Loggers which are derived from TARDIS - tardis_loggers = tardis_logger() - - if logging_level in LOGGING_LEVELS: + if logging_level in ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR"]: for logger in tardis_loggers: - logger.setLevel(LOGGING_LEVELS[logging_level]) + logger.setLevel(getattr(logging, logging_level)) if logger.filters: for filter in logger.filters: @@ -168,7 +56,7 @@ def logging_state(log_level, tardis_config, specific_log_level): logger.removeFilter(filter) if specific_log_level: - filter_log = FilterLog([LOGGING_LEVELS[logging_level], logging.INFO, logging.DEBUG]) + filter_log = FilterLog([getattr(logging, logging_level), logging.INFO, logging.DEBUG]) for logger in tardis_loggers: logger.addFilter(filter_log) else: @@ -176,23 +64,96 @@ def logging_state(log_level, tardis_config, specific_log_level): for logger in tardis_loggers: logger.removeFilter(filter) +log_outputs = { + "WARNING/ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), + "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), + "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), + "ALL": Output(layout=Layout(height='300px', overflow_y='auto')) +} + +tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"], log_outputs["ALL"]]) +tab.set_title(0, "WARNING/ERROR") +tab.set_title(1, "INFO") +tab.set_title(2, "DEBUG") +tab.set_title(3, "ALL") + +display(tab) + +# Function to remove ANSI escape sequences +def remove_ansi_escape_sequences(text): + ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') + return ansi_escape.sub('', text) + +class WidgetHandler(logging.Handler): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def emit(self, record): + log_entry = self.format(record) + clean_log_entry = remove_ansi_escape_sequences(log_entry) + + if record.levelno == logging.INFO: + color = '#D3D3D3' + elif record.levelno == logging.WARNING: + color = 'orange' + elif record.levelno == logging.ERROR: + color = 'red' + elif record.levelno == logging.CRITICAL: + color = 'orange' + elif record.levelno == logging.DEBUG: + color = 'blue' + else: + color = 'black' + + parts = clean_log_entry.split(' ', 2) + if len(parts) > 2: + prefix = parts[0] + levelname = parts[1] + message = parts[2] + html_output = f'{prefix} {levelname} {message}' + else: + html_output = clean_log_entry + + if record.levelno in (logging.WARNING, logging.ERROR): + with log_outputs["WARNING/ERROR"]: + display(HTML(f"
{html_output}
")) + elif record.levelno == logging.INFO: + with log_outputs["INFO"]: + display(HTML(f"
{html_output}
")) + elif record.levelno == logging.DEBUG: + with log_outputs["DEBUG"]: + display(HTML(f"
{html_output}
")) + with log_outputs["ALL"]: + display(HTML(f"
{html_output}
")) + +widget_handler = WidgetHandler() +widget_handler.setFormatter(logging.Formatter('%(name)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)')) + +logging.captureWarnings(True) +logger = logging.getLogger("tardis") +logger.setLevel(logging.DEBUG) + +# To fix the issue of duplicate logs +for handler in logger.handlers[:]: + logger.removeHandler(handler) + +root_logger = logging.getLogger() +for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + +logger.addHandler(widget_handler) +logging.getLogger("py.warnings").addHandler(widget_handler) + +class FilterLog(object): + def __init__(self, log_levels): + self.log_levels = log_levels + + def filter(self, log_record): + return log_record.levelno in self.log_levels -def tardis_logger(): - """ - Generates the list of the loggers which are derived from TARDIS - All loggers which are of the form `tardis.module_name` are added to the list - - Parameters - ---------- - list_for_loggers : list - List for storing the loggers derived from TARDIS - - Returns - ------- - list_for_loggers : list - """ - list_for_loggers = [] - for name in logging.root.manager.loggerDict: - if not name.find("tardis"): - list_for_loggers.append(logging.getLogger(name)) - return list_for_loggers +# Example for testing, will remove later +logger.debug('This is a debug message.') +logger.info('This is an info message.') +logger.warning('This is a warning message.') +logger.error('This is an error message.') +logger.critical('This is a critical message.') From 426337f649df1f69d0bb77edc5c2e38632e929ab Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Mon, 22 Jul 2024 17:58:46 -0400 Subject: [PATCH 06/10] Added documentation and cleaned up the code, fixed an issue in line 58, where log level "ALL" was not added. --- tardis/io/logger/colored_logger.py | 83 ------------------------------ tardis/io/logger/logger.py | 83 ++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 94 deletions(-) delete mode 100644 tardis/io/logger/colored_logger.py diff --git a/tardis/io/logger/colored_logger.py b/tardis/io/logger/colored_logger.py deleted file mode 100644 index a03d88a1fac..00000000000 --- a/tardis/io/logger/colored_logger.py +++ /dev/null @@ -1,83 +0,0 @@ -import logging - -""" -Code for Custom Logger Classes (ColoredFormatter and ColorLogger) and its helper function -(formatter_message) is used from this thread -http://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output -""" - -FORMAT = "[$BOLD{name:20s}$RESET][{levelname:18s}] \n\t{message:s} ($BOLD{filename:s}$RESET:{lineno:d})" -DEBUG_FORMAT = "[$BOLD{name:20s}$RESET][{levelname:18s}] {message:s} ($BOLD{filename:s}$RESET:{lineno:d})" - - -def formatter_message(message, use_color=True): - """ - Helper Function used for Coloring Log Output - """ - # These are the sequences need to get colored ouput - RESET_SEQ = "\033[0m" - BOLD_SEQ = "\033[1m" - if use_color: - message = message.replace("$RESET", RESET_SEQ).replace( - "$BOLD", BOLD_SEQ - ) - else: - message = message.replace("$RESET", "").replace("$BOLD", "") - return message - - -class ColoredFormatter(logging.Formatter): - """ - Custom logger class for changing levels color - """ - - def __init__(self, use_color=True): - self.non_debug = formatter_message(FORMAT, True) - self.debug = formatter_message(DEBUG_FORMAT, True) - logging.Formatter.__init__(self, self.non_debug, style="{") - self.use_color = use_color - - def format(self, record): - COLOR_SEQ = "\033[1;%dm" - RESET_SEQ = "\033[0m" - BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) - COLORS = { - "WARNING": YELLOW, - "INFO": WHITE, - "DEBUG": BLUE, - "CRITICAL": YELLOW, - "ERROR": RED, - } - - levelname = record.levelname - if self.use_color and levelname in COLORS: - levelname_color = ( - COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ - ) - record.levelname = levelname_color - - if record.levelno == logging.DEBUG: - self._style._fmt = self.debug - else: - self._style._fmt = self.non_debug - - return logging.Formatter.format(self, record) - - -class ColoredLogger(logging.Logger): - """ - Custom logger class with multiple destinations - """ - - COLOR_FORMAT = formatter_message(FORMAT, True) - - def __init__(self, name): - logging.Logger.__init__(self, name, logging.DEBUG) - - color_formatter = ColoredFormatter(self.COLOR_FORMAT) - - console = logging.StreamHandler() - console.setFormatter(color_formatter) - - self.addHandler(console) - return diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 1ef262090d9..311f5976260 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -2,9 +2,24 @@ import re from ipywidgets import Output, Tab, Layout from IPython.display import display, HTML -#from tardis.io.logger.colored_logger import ColoredFormatter def logging_state(log_level, tardis_config, specific_log_level): + """ + Function to set the logging configuration for the simulation output + Called from within run_tardis() + Configured via functional arguments passed through run_tardis() - log_level & specific_log_level + Configured via YAML parameters under `debug` section - log_level & specific_log_level + + Parameters + ---------- + log_level : str + Allows input of the log level for the simulation. + Uses Python logging framework to determine the messages that will be output. + tardis_config : dict + Configuration dictionary for TARDIS. + specific_log_level : bool + Allows setting specific logging levels. Logs of the `log_level` level would be output. + """ if "debug" in tardis_config: specific_log_level = ( tardis_config["debug"]["specific_log_level"] @@ -40,7 +55,7 @@ def logging_state(log_level, tardis_config, specific_log_level): logging_level = logging_level.upper() if not logging_level in ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "ALL"]: raise ValueError( - f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following ['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR']" + f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following ['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'ALL']" ) logger = logging.getLogger("tardis") @@ -79,16 +94,44 @@ def logging_state(log_level, tardis_config, specific_log_level): display(tab) -# Function to remove ANSI escape sequences def remove_ansi_escape_sequences(text): + """ + Remove ANSI escape sequences from a string. + + Parameters + ---------- + text : str + The input string containing ANSI escape sequences. + + Returns + ------- + str + The cleaned string without ANSI escape sequences. + """ ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') return ansi_escape.sub('', text) class WidgetHandler(logging.Handler): + """ + A custom logging handler that outputs log messages to IPython widgets. + + Parameters + ---------- + logging.Handler : class + Inherits from the logging.Handler class. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def emit(self, record): + """ + Emit a log record. + + Parameters + ---------- + record : logging.LogRecord + The log record to be emitted. + """ log_entry = self.format(record) clean_log_entry = remove_ansi_escape_sequences(log_entry) @@ -145,15 +188,33 @@ def emit(self, record): logging.getLogger("py.warnings").addHandler(widget_handler) class FilterLog(object): + """ + Filter Log Class for Filtering Logging Output + to a particular level. + + Parameters + ---------- + log_level : logging object + allows to have a filter for the + particular log_level + """ def __init__(self, log_levels): self.log_levels = log_levels def filter(self, log_record): - return log_record.levelno in self.log_levels - -# Example for testing, will remove later -logger.debug('This is a debug message.') -logger.info('This is an info message.') -logger.warning('This is a warning message.') -logger.error('This is an error message.') -logger.critical('This is a critical message.') + """ + Determine if the specified record is to be logged. + + Parameters + ---------- + log_record : logging.LogRecord + The log record to be filtered. + + Returns + ------- + boolean : True, if the current log_record has the + level that of the specified log_level, + False, if the current log_record doesn't have the + same log_level as the specified one. + """ + return log_record.levelno in self.log_levels \ No newline at end of file From 7faadf3c3d1d30832339160179f9518e9231cf47 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Wed, 24 Jul 2024 15:01:18 -0400 Subject: [PATCH 07/10] Added my email to the .mailmap file --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index c10dde1a38c..31278f86bb0 100644 --- a/.mailmap +++ b/.mailmap @@ -64,6 +64,8 @@ Christian Vogl chvogl Debajyoti Dasgupta +Deeksha Mohanty + Dhruv Sondhi Dhruv Sondhi Dhruv Dhruv Sondhi Dhruv Sondhi <66117751+DhruvSondhi@users.noreply.github.com> From 6899a457948e44849b9bff2f17dca1385436df28 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Sun, 28 Jul 2024 18:49:55 -0400 Subject: [PATCH 08/10] Re-added the 'LOGGING_LEVELS' dictionary to have all the logging levels in one place. --- tardis/io/logger/logger.py | 66 +++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 311f5976260..291878177be 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -3,7 +3,18 @@ from ipywidgets import Output, Tab, Layout from IPython.display import display, HTML -def logging_state(log_level, tardis_config, specific_log_level): +LOGGING_LEVELS = { + "NOTSET": logging.NOTSET, + "DEBUG": logging.DEBUG, + "INFO": logging.INFO, + "WARNING": logging.WARNING, + "ERROR": logging.ERROR, + "CRITICAL": logging.CRITICAL, +} +DEFAULT_LOG_LEVEL = "INFO" +DEFAULT_SPECIFIC_STATE = False + +def logging_state(log_level, tardis_config, specific_log_level=None): """ Function to set the logging configuration for the simulation output Called from within run_tardis() @@ -22,48 +33,33 @@ def logging_state(log_level, tardis_config, specific_log_level): """ if "debug" in tardis_config: specific_log_level = ( - tardis_config["debug"]["specific_log_level"] - if specific_log_level is None - else specific_log_level + tardis_config["debug"].get("specific_log_level", specific_log_level) ) - - logging_level = ( - log_level if log_level else tardis_config["debug"]["log_level"] - ) - - if log_level and tardis_config["debug"]["log_level"]: + logging_level = log_level or tardis_config["debug"].get("log_level", "INFO") + if log_level and tardis_config["debug"].get("log_level"): print( "log_level is defined both in Functional Argument & YAML Configuration {debug section}" ) print( f"log_level = {log_level.upper()} will be used for Log Level Determination\n" ) - else: tardis_config["debug"] = {} - - if log_level: - logging_level = log_level - else: - tardis_config["debug"]["log_level"] = "INFO" - logging_level = tardis_config["debug"]["log_level"] - - if not specific_log_level: - tardis_config["debug"]["specific_log_level"] = False - specific_log_level = tardis_config["debug"]["specific_log_level"] + logging_level = log_level or DEFAULT_LOG_LEVEL + specific_log_level = specific_log_level or DEFAULT_SPECIFIC_STATE logging_level = logging_level.upper() - if not logging_level in ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "ALL"]: + if logging_level not in LOGGING_LEVELS: raise ValueError( - f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following ['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'ALL']" + f"Passed Value for log_level = {logging_level} is Invalid. Must be one of the following {list(LOGGING_LEVELS.keys())}" ) logger = logging.getLogger("tardis") tardis_loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("tardis")] - if logging_level in ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR"]: + if logging_level in LOGGING_LEVELS: for logger in tardis_loggers: - logger.setLevel(getattr(logging, logging_level)) + logger.setLevel(LOGGING_LEVELS[logging_level]) if logger.filters: for filter in logger.filters: @@ -71,7 +67,7 @@ def logging_state(log_level, tardis_config, specific_log_level): logger.removeFilter(filter) if specific_log_level: - filter_log = FilterLog([getattr(logging, logging_level), logging.INFO, logging.DEBUG]) + filter_log = FilterLog([LOGGING_LEVELS[logging_level], logging.INFO, logging.DEBUG]) for logger in tardis_loggers: logger.addFilter(filter_log) else: @@ -189,21 +185,19 @@ def emit(self, record): class FilterLog(object): """ - Filter Log Class for Filtering Logging Output - to a particular level. + Filter Log Class for Filtering Logging Output to a particular level. Parameters ---------- - log_level : logging object - allows to have a filter for the - particular log_level + log_levels : list + List of log levels to be filtered. """ def __init__(self, log_levels): self.log_levels = log_levels def filter(self, log_record): """ - Determine if the specified record is to be logged. + Determine if the specified record is to be logged. Parameters ---------- @@ -212,9 +206,7 @@ def filter(self, log_record): Returns ------- - boolean : True, if the current log_record has the - level that of the specified log_level, - False, if the current log_record doesn't have the - same log_level as the specified one. + bool + True if the log record's level is in the specified log_levels, False otherwise. """ - return log_record.levelno in self.log_levels \ No newline at end of file + return log_record.levelno in self.log_levels From cf92b5ab84fb0ba39ce89eebbd9d4a7c19aaa48e Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Wed, 11 Sep 2024 21:24:38 -0400 Subject: [PATCH 09/10] 1. Defined a function to return the output widget 2. Moved the display logic to a function 3. Used a dictionary to manage colors for log levels. 4. Removed the if block from base.py --- tardis/io/logger/logger.py | 70 ++++++++++++++++++++++++++------------ tardis/simulation/base.py | 38 ++++++--------------- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 291878177be..552ef458240 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -11,6 +11,16 @@ "ERROR": logging.ERROR, "CRITICAL": logging.CRITICAL, } + +LOG_LEVEL_COLORS = { + logging.INFO: '#D3D3D3', + logging.WARNING: 'orange', + logging.ERROR: 'red', + logging.CRITICAL: 'orange', + logging.DEBUG: 'blue', + 'default': 'black' +} + DEFAULT_LOG_LEVEL = "INFO" DEFAULT_SPECIFIC_STATE = False @@ -75,20 +85,45 @@ def logging_state(log_level, tardis_config, specific_log_level=None): for logger in tardis_loggers: logger.removeFilter(filter) +def create_output_widget(height='300px'): + """ + Creates an Output widget with the specified layout. + + Parameters + ---------- + height : str, optional + Height of the widget in pixels, by default '300px'. + + Returns + ------- + ipywidgets.Output + Configured Output widget. + """ + return Output(layout=Layout(height=height, overflow_y='auto')) + log_outputs = { - "WARNING/ERROR": Output(layout=Layout(height='300px', overflow_y='auto')), - "INFO": Output(layout=Layout(height='300px', overflow_y='auto')), - "DEBUG": Output(layout=Layout(height='300px', overflow_y='auto')), - "ALL": Output(layout=Layout(height='300px', overflow_y='auto')) + "WARNING/ERROR": create_output_widget(), + "INFO": create_output_widget(), + "DEBUG": create_output_widget(), + "ALL": create_output_widget() } -tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"], log_outputs["ALL"]]) -tab.set_title(0, "WARNING/ERROR") -tab.set_title(1, "INFO") -tab.set_title(2, "DEBUG") -tab.set_title(3, "ALL") +def create_and_display_log_tab(log_outputs): + """ + Creates a Tab widget for logging outputs and displays it. -display(tab) + Parameters + ---------- + log_outputs : dict + Dictionary containing Output widgets for different log levels. + """ + tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"], log_outputs["ALL"]]) + tab.set_title(0, "WARNING/ERROR") + tab.set_title(1, "INFO") + tab.set_title(2, "DEBUG") + tab.set_title(3, "ALL") + + display(tab) def remove_ansi_escape_sequences(text): """ @@ -131,18 +166,7 @@ def emit(self, record): log_entry = self.format(record) clean_log_entry = remove_ansi_escape_sequences(log_entry) - if record.levelno == logging.INFO: - color = '#D3D3D3' - elif record.levelno == logging.WARNING: - color = 'orange' - elif record.levelno == logging.ERROR: - color = 'red' - elif record.levelno == logging.CRITICAL: - color = 'orange' - elif record.levelno == logging.DEBUG: - color = 'blue' - else: - color = 'black' + color = LOG_LEVEL_COLORS.get(record.levelno, LOG_LEVEL_COLORS['default']) parts = clean_log_entry.split(' ', 2) if len(parts) > 2: @@ -182,6 +206,7 @@ def emit(self, record): logger.addHandler(widget_handler) logging.getLogger("py.warnings").addHandler(widget_handler) +create_and_display_log_tab(log_outputs) class FilterLog(object): """ @@ -210,3 +235,4 @@ def filter(self, log_record): True if the log record's level is in the specified log_levels, False otherwise. """ return log_record.levelno in self.log_levels + \ No newline at end of file diff --git a/tardis/simulation/base.py b/tardis/simulation/base.py index bcd0596a286..88c3968eb96 100644 --- a/tardis/simulation/base.py +++ b/tardis/simulation/base.py @@ -626,34 +626,16 @@ def log_plasma_state( plasma_state_log["w"] = dilution_factor plasma_state_log["next_w"] = next_dilution_factor plasma_state_log.columns.name = "Shell No." - - if False and is_notebook(): #TODO: remove it with something better - logger.info("\n\tPlasma stratification:") - - # Displaying the DataFrame only when the logging level is NOTSET, DEBUG or INFO - if logger.level <= logging.INFO: - if not logger.filters: - display( - plasma_state_log.iloc[::log_sampling].style.format( - "{:.3g}" - ) - ) - elif logger.filters[0].log_level == 20: - display( - plasma_state_log.iloc[::log_sampling].style.format( - "{:.3g}" - ) - ) - else: - output_df = "" - plasma_output = plasma_state_log.iloc[::log_sampling].to_string( - float_format=lambda x: f"{x:.3g}", - justify="center", - ) - for value in plasma_output.split("\n"): - output_df = output_df + f"\t{value}\n" - logger.info("\n\tPlasma stratification:") - logger.info(f"\n{output_df}") + + output_df = "" + plasma_output = plasma_state_log.iloc[::log_sampling].to_string( + float_format=lambda x: f"{x:.3g}", + justify="center", + ) + for value in plasma_output.split("\n"): + output_df = output_df + f"\t{value}\n" + logger.info("\n\tPlasma stratification:") + logger.info(f"\n{output_df}") logger.info( f"\n\tCurrent t_inner = {t_inner:.3f}\n\tExpected t_inner for next iteration = {next_t_inner:.3f}\n" From a95096c48a0732ce27c273de693fbc5d324b9fc9 Mon Sep 17 00:00:00 2001 From: Deeksha Mohanty Date: Mon, 16 Sep 2024 11:53:55 -0400 Subject: [PATCH 10/10] Balckified the code --- tardis/io/logger/logger.py | 98 ++++++++++++++++++++++++++++---------- tardis/simulation/base.py | 2 +- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/tardis/io/logger/logger.py b/tardis/io/logger/logger.py index 552ef458240..a14a3eb9a5c 100644 --- a/tardis/io/logger/logger.py +++ b/tardis/io/logger/logger.py @@ -13,17 +13,18 @@ } LOG_LEVEL_COLORS = { - logging.INFO: '#D3D3D3', - logging.WARNING: 'orange', - logging.ERROR: 'red', - logging.CRITICAL: 'orange', - logging.DEBUG: 'blue', - 'default': 'black' + logging.INFO: "#D3D3D3", + logging.WARNING: "orange", + logging.ERROR: "red", + logging.CRITICAL: "orange", + logging.DEBUG: "blue", + "default": "black", } DEFAULT_LOG_LEVEL = "INFO" DEFAULT_SPECIFIC_STATE = False + def logging_state(log_level, tardis_config, specific_log_level=None): """ Function to set the logging configuration for the simulation output @@ -42,10 +43,12 @@ def logging_state(log_level, tardis_config, specific_log_level=None): Allows setting specific logging levels. Logs of the `log_level` level would be output. """ if "debug" in tardis_config: - specific_log_level = ( - tardis_config["debug"].get("specific_log_level", specific_log_level) + specific_log_level = tardis_config["debug"].get( + "specific_log_level", specific_log_level + ) + logging_level = log_level or tardis_config["debug"].get( + "log_level", "INFO" ) - logging_level = log_level or tardis_config["debug"].get("log_level", "INFO") if log_level and tardis_config["debug"].get("log_level"): print( "log_level is defined both in Functional Argument & YAML Configuration {debug section}" @@ -65,7 +68,11 @@ def logging_state(log_level, tardis_config, specific_log_level=None): ) logger = logging.getLogger("tardis") - tardis_loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict if name.startswith("tardis")] + tardis_loggers = [ + logging.getLogger(name) + for name in logging.root.manager.loggerDict + if name.startswith("tardis") + ] if logging_level in LOGGING_LEVELS: for logger in tardis_loggers: @@ -77,7 +84,9 @@ def logging_state(log_level, tardis_config, specific_log_level=None): logger.removeFilter(filter) if specific_log_level: - filter_log = FilterLog([LOGGING_LEVELS[logging_level], logging.INFO, logging.DEBUG]) + filter_log = FilterLog( + [LOGGING_LEVELS[logging_level], logging.INFO, logging.DEBUG] + ) for logger in tardis_loggers: logger.addFilter(filter_log) else: @@ -85,7 +94,8 @@ def logging_state(log_level, tardis_config, specific_log_level=None): for logger in tardis_loggers: logger.removeFilter(filter) -def create_output_widget(height='300px'): + +def create_output_widget(height="300px"): """ Creates an Output widget with the specified layout. @@ -99,15 +109,17 @@ def create_output_widget(height='300px'): ipywidgets.Output Configured Output widget. """ - return Output(layout=Layout(height=height, overflow_y='auto')) + return Output(layout=Layout(height=height, overflow_y="auto")) + log_outputs = { "WARNING/ERROR": create_output_widget(), "INFO": create_output_widget(), "DEBUG": create_output_widget(), - "ALL": create_output_widget() + "ALL": create_output_widget(), } + def create_and_display_log_tab(log_outputs): """ Creates a Tab widget for logging outputs and displays it. @@ -117,14 +129,22 @@ def create_and_display_log_tab(log_outputs): log_outputs : dict Dictionary containing Output widgets for different log levels. """ - tab = Tab(children=[log_outputs["WARNING/ERROR"], log_outputs["INFO"], log_outputs["DEBUG"], log_outputs["ALL"]]) + tab = Tab( + children=[ + log_outputs["WARNING/ERROR"], + log_outputs["INFO"], + log_outputs["DEBUG"], + log_outputs["ALL"], + ] + ) tab.set_title(0, "WARNING/ERROR") tab.set_title(1, "INFO") tab.set_title(2, "DEBUG") tab.set_title(3, "ALL") - + display(tab) + def remove_ansi_escape_sequences(text): """ Remove ANSI escape sequences from a string. @@ -139,8 +159,9 @@ def remove_ansi_escape_sequences(text): str The cleaned string without ANSI escape sequences. """ - ansi_escape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') - return ansi_escape.sub('', text) + ansi_escape = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]") + return ansi_escape.sub("", text) + class WidgetHandler(logging.Handler): """ @@ -151,6 +172,7 @@ class WidgetHandler(logging.Handler): logging.Handler : class Inherits from the logging.Handler class. """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -166,9 +188,11 @@ def emit(self, record): log_entry = self.format(record) clean_log_entry = remove_ansi_escape_sequences(log_entry) - color = LOG_LEVEL_COLORS.get(record.levelno, LOG_LEVEL_COLORS['default']) + color = LOG_LEVEL_COLORS.get( + record.levelno, LOG_LEVEL_COLORS["default"] + ) - parts = clean_log_entry.split(' ', 2) + parts = clean_log_entry.split(" ", 2) if len(parts) > 2: prefix = parts[0] levelname = parts[1] @@ -179,18 +203,39 @@ def emit(self, record): if record.levelno in (logging.WARNING, logging.ERROR): with log_outputs["WARNING/ERROR"]: - display(HTML(f"
{html_output}
")) + display( + HTML( + f"
{html_output}
" + ) + ) elif record.levelno == logging.INFO: with log_outputs["INFO"]: - display(HTML(f"
{html_output}
")) + display( + HTML( + f"
{html_output}
" + ) + ) elif record.levelno == logging.DEBUG: with log_outputs["DEBUG"]: - display(HTML(f"
{html_output}
")) + display( + HTML( + f"
{html_output}
" + ) + ) with log_outputs["ALL"]: - display(HTML(f"
{html_output}
")) + display( + HTML( + f"
{html_output}
" + ) + ) + widget_handler = WidgetHandler() -widget_handler.setFormatter(logging.Formatter('%(name)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)')) +widget_handler.setFormatter( + logging.Formatter( + "%(name)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)" + ) +) logging.captureWarnings(True) logger = logging.getLogger("tardis") @@ -208,6 +253,7 @@ def emit(self, record): logging.getLogger("py.warnings").addHandler(widget_handler) create_and_display_log_tab(log_outputs) + class FilterLog(object): """ Filter Log Class for Filtering Logging Output to a particular level. @@ -217,6 +263,7 @@ class FilterLog(object): log_levels : list List of log levels to be filtered. """ + def __init__(self, log_levels): self.log_levels = log_levels @@ -235,4 +282,3 @@ def filter(self, log_record): True if the log record's level is in the specified log_levels, False otherwise. """ return log_record.levelno in self.log_levels - \ No newline at end of file diff --git a/tardis/simulation/base.py b/tardis/simulation/base.py index 88c3968eb96..c09d6ae1914 100644 --- a/tardis/simulation/base.py +++ b/tardis/simulation/base.py @@ -626,7 +626,7 @@ def log_plasma_state( plasma_state_log["w"] = dilution_factor plasma_state_log["next_w"] = next_dilution_factor plasma_state_log.columns.name = "Shell No." - + output_df = "" plasma_output = plasma_state_log.iloc[::log_sampling].to_string( float_format=lambda x: f"{x:.3g}",