Skip to content

bs_logging module

Utilities for logging:

  • get_logger creates and returns a logger with colored output
  • init_logger initializes and returns a customized logger
  • log_execution is a decorator to log entry into and ext from a function.

get_logger(name, level=logging.INFO, log_to_file=False)

Create and return a logger with colored output.

Parameters:

Name Type Description Default
name str

Name of the logger.

required
level int

Minimum logging level.

INFO
log_to_file bool

If True, also log to a file named '{name}.log'.

False

Returns:

Type Description
Logger

Configured logger instance.

Examples:

1
2
>>> logger = get_logger("my_logger", level=logging.DEBUG, log_to_file=True)
>>> logger.debug("This is a debug message.")
Source code in bs_python_utils/bs_logging.py
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
def get_logger(
    name: str,
    level: int = logging.INFO,
    log_to_file: bool = False,
) -> logging.Logger:
    """Create and return a logger with colored output.

    Args:
        name: Name of the logger.
        level: Minimum logging level.
        log_to_file: If True, also log to a file named '{name}.log'.

    Returns:
        Configured logger instance.

    Examples:
        >>> logger = get_logger("my_logger", level=logging.DEBUG, log_to_file=True)
        >>> logger.debug("This is a debug message.")
    """
    logger = logging.getLogger(name)
    if logger.handlers:
        return logger  # Already configured
    logger.setLevel(level)
    formatter = ColorFormatter("%(asctime)s", datefmt="%H:%M:%S")
    stream_handler = logging.StreamHandler(sys.stdout)
    stream_handler.setFormatter(formatter)
    logger.addHandler(stream_handler)
    if log_to_file:
        file_handler = logging.FileHandler(f"{name}.log")
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    return logger

init_logger(logger_name, log_level_for_console='info', log_level_for_file='debug', save_dir=None)

Initialize a logger

Parameters:

Name Type Description Default
logger_name str

name for the logger

required
log_level_for_console str

minimum level of messages logged to the console logging

'info'
log_level_for_file str
'debug'
save_dir str | None
None

Returns:

Type Description
Logger

the logger

Examples:

1
2
3
>>> logger_dir = "logs"
>>> logger_name = "check_log"
>>> logger = init_logger(logger_name, save_dir=logger_dir)

This will create two logs:

  • one printed to console where we run the code (the StreamHandler),
  • and one that will be saved to file save_dir/logger_name.txt (the FileHandler).

'logger.propagate = False' makes sure that the logs sent to file will not be printed to console.

We use the Formatter class to define the format of the logs. Here:

  • The time of the log in a human-readable format, asctime
  • levelname is the level of the log, one out of INFO, DEBUG, WARNING, ERROR, CRITICAL.
  • The name of the file, filename, from which the log was generated, and the line number, lineno.
  • Lastly, the message itself — message.

The default has only INFO logs and above (i.e., also WARNING, ERROR and CRITICAL) displayed in the console; the file will also include DEBUG logs.

Source code in bs_python_utils/bs_logging.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def init_logger(
    logger_name: str,
    log_level_for_console: str = "info",
    log_level_for_file: str = "debug",
    save_dir: str | None = None,
) -> logging.Logger:
    """
    Initialize a logger

    Args:
        logger_name: name for the logger
        log_level_for_console: minimum level of messages logged to the
            console logging
        log_level_for_file:
        save_dir:

    Returns:
        the logger

    Examples:
        >>> logger_dir = "logs"
        >>> logger_name = "check_log"
        >>> logger = init_logger(logger_name, save_dir=logger_dir)

        This will create two logs:

        * one printed to console where we run the code (the
          `StreamHandler`),
        * and one that will be saved to file `save_dir/logger_name.txt`
          (the `FileHandler`).

        `'logger.propagate = False'` makes sure that the logs sent to file
        will not be printed to console.

        We use the `Formatter` class to define the format of the logs.
        Here:

        * The time of the log in a human-readable format, `asctime`
        * `levelname` is the level of the log, one out of `INFO, DEBUG, WARNING, ERROR, CRITICAL`.
        * The name of the file, `filename`, from which the log was generated,
        and the line number, `lineno`.
        * Lastly,  the message itself — `message`.

        The default has only `INFO` logs and above (i.e., also
        `WARNING, ERROR` and `CRITICAL`)
        displayed in the console; the file will also include `DEBUG` logs.
    """
    logger = logging.getLogger()
    logger.setLevel(level=logging.DEBUG)
    logger.propagate = False

    formatter = logging.Formatter(
        "%(asctime)s [%(levelname)s] %(filename)s %(lineno)d - %(message)s",
        "%Y-%m-%d %H:%M:%S",
    )

    ch = logging.StreamHandler()
    ch.setLevel(log_level_for_console.upper())
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    if save_dir is not None:
        Path(save_dir).mkdir(exist_ok=True, parents=True)
        fh = logging.FileHandler(save_dir + f"/{logger_name}.txt")
        fh.setLevel(log_level_for_file.upper())
        fh.setFormatter(formatter)
        logger.addHandler(fh)

    return logger

log_execution(func)

Decorator to log the execution of a function. Only records entry to and exit from the function, to the console.

Source code in bs_python_utils/bs_logging.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def log_execution(func: Callable) -> Callable:
    """Decorator to log the execution of a function.
    Only records entry to and exit from the function, to the console.
    """
    loglevel = logging.info

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        loglevel(f"Executing {func.__name__}")
        result = func(*args, **kwargs)
        loglevel(f"Finished executing {func.__name__}")
        return result

    return wrapper

    return wrapper
    return wrapper