1 26
import logging
2 26
import os
3

4
# This is modeled after YANK's nice logging facility.
5
# https://github.com/choderalab/yank/blob/4dfcc8e127c51c20180fe6caeb49fcb1f21730c6/Yank/utils.py#L78
6

7

8 26
def config_root_logger(verbose, log_file_path=None):
9
    """
10
    Setup the the root logger's configuration.
11
    The log messages are printed in the terminal and saved in the file specified
12
    by log_file_path (if not `None`) and printed. Note that logging use sys.stdout
13
    to print ``logging.INFO`` messages, and stderr for the others. The root logger's
14
    configuration is inherited by the loggers created by ``logging.getLogger(name)``.
15
    Different formats are used to display messages on the terminal and on the log
16
    file. For example, in the log file every entry has a timestamp which does not
17
    appear in the terminal. Moreover, the log file always shows the module that
18
    generate the message, while in the terminal this happens only for messages
19
    of level `WARNING` and higher.
20

21
    Parameters
22
    ----------
23
    verbose : bool
24
        Control the verbosity of the messages printed in the terminal. The logger
25
        displays messages of level ``logging.INFO`` and higher when ``verbose=False``.
26
        Otherwise those of level ``logging.DEBUG`` and higher are printed.
27
    log_file_path : str, optional, default = None
28
        If not `None`, this is the path where all the logger's messages of level
29
        ``logging.DEBUG`` or higher are saved.
30
    """
31

32 26
    class TerminalFormatter(logging.Formatter):
33
        """
34
        Simplified format for INFO and DEBUG level log messages.
35
        This allows to keep the logging.info() and debug() format separated from
36
        the other levels where more information may be needed. For example, for
37
        warning and error messages it is convenient to know also the module that
38
        generates them.
39
        """
40

41
        # This is the cleanest way I found to make the code compatible with both
42
        # Python 2 and Python 3
43 26
        simple_fmt = logging.Formatter("%(asctime)-15s: %(message)s")
44 26
        default_fmt = logging.Formatter(
45
            "%(asctime)-15s: %(levelname)s - %(name)s - %(message)s"
46
        )
47

48 26
        def format(self, record):
49 26
            if record.levelno <= logging.INFO:
50 26
                return self.simple_fmt.format(record)
51
            else:
52 26
                return self.default_fmt.format(record)
53

54
    # Add handler for stdout and stderr messages
55 26
    terminal_handler = logging.StreamHandler()
56 26
    terminal_handler.setFormatter(TerminalFormatter())
57 26
    if verbose:
58 26
        terminal_handler.setLevel(logging.DEBUG)
59
    else:
60 0
        terminal_handler.setLevel(logging.INFO)
61 26
    logging.root.addHandler(terminal_handler)
62

63
    # Add file handler to root logger
64 26
    file_format = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
65 26
    if log_file_path is not None:
66 0
        file_handler = logging.FileHandler(log_file_path)
67 0
        file_handler.setLevel(logging.DEBUG)
68 0
        file_handler.setFormatter(logging.Formatter(file_format))
69 0
        logging.root.addHandler(file_handler)
70

71
    # Do not handle logging.DEBUG at all if unnecessary
72 26
    if log_file_path is not None:
73 0
        logging.root.setLevel(logging.DEBUG)
74
    else:
75 26
        logging.root.setLevel(terminal_handler.level)
76

77
    # Setup critical logger file if a logfile is specified
78
    # No need to worry about MPI due to it already being set above
79 26
    if log_file_path is not None:
80 0
        basepath, ext = os.path.splitext(log_file_path)
81 0
        critical_log_path = basepath + "_CRITICAL" + ext
82
        # Create the critical file handler to only create the file IF a critical message is sent
83 0
        critical_file_handler = logging.FileHandler(critical_log_path, delay=True)
84 0
        critical_file_handler.setLevel(logging.CRITICAL)
85
        # Add blank lines to space out critical errors
86 0
        critical_file_format = file_format + "\n\n\n"
87 0
        critical_file_handler.setFormatter(logging.Formatter(critical_file_format))
88 0
        logging.root.addHandler(critical_file_handler)

Read our documentation on viewing source code .

Loading