Skip to content

academy.logging

init_logging

init_logging(
    level: int | str = INFO,
    *,
    logfile: str | Path | None = None,
    logfile_level: int | str | None = None,
    color: bool = True,
    extra: bool = False,
    force: bool = False
) -> Logger

Initialize global logger.

Parameters:

  • level (int | str, default: INFO ) –

    Minimum logging level.

  • logfile (str | Path | None, default: None ) –

    Configure a file handler for this path.

  • logfile_level (int | str | None, default: None ) –

    Minimum logging level for the file handler. Defaults to that of level.

  • color (bool, default: True ) –

    Use colorful logging for stdout.

  • extra (bool, default: False ) –

    Include extra info in log messages, such as thread ID and process ID. This is helpful for debugging.

  • force (bool, default: False ) –

    Remove any existing handlers attached to the root handler. This option is useful to silencing the third-party package logging. Note: should not be set when running inside pytest.

Returns:

Source code in academy/logging.py
def init_logging(  # noqa: PLR0913
    level: int | str = logging.INFO,
    *,
    logfile: str | pathlib.Path | None = None,
    logfile_level: int | str | None = None,
    color: bool = True,
    extra: bool = False,
    force: bool = False,
) -> logging.Logger:
    """Initialize global logger.

    Args:
        level: Minimum logging level.
        logfile: Configure a file handler for this path.
        logfile_level: Minimum logging level for the file handler. Defaults
            to that of `level`.
        color: Use colorful logging for stdout.
        extra: Include extra info in log messages, such as thread ID and
            process ID. This is helpful for debugging.
        force: Remove any existing handlers attached to the root
            handler. This option is useful to silencing the third-party
            package logging. Note: should not be set when running inside
            pytest.

    Returns:
        The root logger.
    """
    stdout_handler = logging.StreamHandler(sys.stdout)
    stdout_handler.setFormatter(_Formatter(color=color, extra=extra))
    stdout_handler.setLevel(level)
    if extra:
        stdout_handler.addFilter(_os_thread_filter)
    handlers: list[logging.Handler] = [stdout_handler]

    if logfile is not None:
        logfile_level = level if logfile_level is None else logfile_level
        path = pathlib.Path(logfile)
        path.parent.mkdir(parents=True, exist_ok=True)
        handler = logging.FileHandler(path)
        handler.setFormatter(_Formatter(color=False, extra=extra))
        handler.setLevel(logfile_level)
        handlers.append(handler)

    logging.basicConfig(
        datefmt='%Y-%m-%d %H:%M:%S',
        level=logging.NOTSET,
        handlers=handlers,
        force=force,
    )

    # This needs to be after the configuration of the root logger because
    # warnings get logged to a 'py.warnings' logger.
    logging.captureWarnings(True)

    logger = logging.getLogger()
    logger.info(
        'Configured logger (stdout-level=%s, logfile=%s, logfile-level=%s)',
        logging.getLevelName(level) if isinstance(level, int) else level,
        logfile,
        logging.getLevelName(logfile_level)
        if isinstance(logfile_level, int)
        else logfile_level,
    )

    return logger