Skip to content

vulcan.logs

logs


Logger setup for VULCAN.


CustomFormatter

Bases: Formatter

setup_logger(logpath='new.log', level='INFO', logterm=True)

Source code in src/vulcan/logs.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def setup_logger(logpath: str = 'new.log', level: str = 'INFO', logterm: bool = True):
    logger_name = 'fwl'

    # https://stackoverflow.com/a/61457119
    custom_logger = logging.getLogger(logger_name)

    # Clear existing handlers to prevent accumulation when setup_logger is called multiple times
    # (e.g., when CLI command groups are invoked multiple times in tests)
    custom_logger.handlers.clear()

    if os.path.exists(logpath):
        os.remove(logpath)

    level = str(level).strip().upper()
    if level not in ['INFO', 'DEBUG', 'ERROR', 'WARNING']:
        raise ValueError(f'Invalid log level: {level}')
    level_code = logging.getLevelNamesMapping()[level]

    # Add terminal output to logger
    if logterm:
        sh = logging.StreamHandler(sys.stdout)
        sh.setFormatter(CustomFormatter())
        sh.setLevel(level_code)
        custom_logger.addHandler(sh)

    # Add file output to logger
    fh = logging.FileHandler(logpath)
    fh.setFormatter(logging.Formatter('[ %(levelname)-5s ] %(message)s'))

    fh.setLevel(level)
    custom_logger.addHandler(fh)
    custom_logger.setLevel(level_code)

    # Capture unhandled exceptions
    # https://stackoverflow.com/a/16993115
    def handle_exception(exc_type, exc_value, exc_traceback):
        if issubclass(exc_type, KeyboardInterrupt):
            custom_logger.error('KeyboardInterrupt')
            sys.__excepthook__(exc_type, exc_value, exc_traceback)
            return
        custom_logger.critical(
            'Uncaught exception', exc_info=(exc_type, exc_value, exc_traceback)
        )

    sys.excepthook = handle_exception

    return custom_logger