Source code for swxsoc.util.logger

"""
This code is based on that provided by SunPy and AstroPy see
    licenses/SUNPY.rst and licenses/ASTROPY.rst
"""

import os
import sys
import logging

from astropy.logger import AstropyLogger

from swxsoc.util.exceptions import SWXWarning

__all__ = ["MyLogger", "_init_log", "_config_to_loggerConf"]


[docs] class MyLogger(AstropyLogger): """ This class is used to set up logging. This inherits the logging enhancements of `~astropy.logger.AstropyLogger`. This logger is able to capture and log warnings that are based on `~swxsoc.util.exceptions.SWXWarning`. Other warnings will be ignored and passed on to other loggers (e.g., from Astropy). """ # Override the existing _showwarning() to capture SWXWarning instead of AstropyWarning def _showwarning(self, *args, **kwargs): # Bail out if we are not catching a warning if not isinstance(args[0], SWXWarning): return self._showwarning_orig(*args, **kwargs) warning = args[0] # Deliberately not using isinstance here: We want to display # the class name only when it's not the default class, # SWXWarning. The name of subclasses of SWXWarning should # be displayed. if type(warning) not in (SWXWarning,): message = f"{warning.__class__.__name__}: {args[0]}" else: message = str(args[0]) mod_path = args[2] # Now that we have the module's path, we look through sys.modules to # find the module object and thus the fully-package-specified module # name. The module.__file__ is the original source file name. mod_name = None mod_path, ext = os.path.splitext(mod_path) for name, mod in list(sys.modules.items()): try: # Believe it or not this can fail in some cases: # https://github.com/astropy/astropy/issues/2671 path = os.path.splitext(getattr(mod, "__file__", ""))[0] except Exception: continue if path == mod_path: mod_name = mod.__name__ break if mod_name is not None: self.warning(message, extra={"origin": mod_name}) else: self.warning(message)
[docs] def _init_log(config=None): """ Initializes the log. In most circumstances this is called automatically when importing. This code is based on that provided by Astropy see "licenses/ASTROPY.rst". """ orig_logger_cls = logging.getLoggerClass() logging.setLoggerClass(MyLogger) try: log = logging.getLogger("swxsoc") if config is not None: _config_to_loggerConf(config) log._set_defaults() # Prevent duplicate log records when downstream packages configure # the root logger (e.g. via ``logging.basicConfig``). The swxsoc # logger already attaches its own ``StreamHandler`` via # ``_set_defaults``, so propagation to the root logger would cause # each record to be emitted twice. log.propagate = False finally: logging.setLoggerClass(orig_logger_cls) return log
[docs] def _config_to_loggerConf(config): """ Translates a user-provided YAML config dictionary to `astropy.logger.LoggerConf`. Parameters: config (dict): Configuration dictionary loaded from a YAML file. Returns: LoggerConf: Configured LoggerConf object, or None if 'logger' section is missing. """ if "logger" in config: from astropy.logger import Conf as LoggerConf conf = LoggerConf() loggerconf_option_list = [ "log_level", "use_color", "log_warnings", "log_exceptions", "log_to_file", "log_file_path", "log_file_level", "log_file_format", ] # Iterate through each option and set it on the LoggerConf object if it exists for this_option in loggerconf_option_list: if this_option in config["logger"]: setattr(conf, this_option, config["logger"][this_option]) return conf else: return None