Skip to content

logging

Worker-side logging/printing integration (internal).

The parent process owns the main logging configuration. The worker routes its own logging records and print() output into the parent's log queue, so users see a single unified stream.

Functions

get_logger()

Get or create the brmspy logger instance.

Returns a configured logger with a custom formatter that outputs messages in the format: [brmspy][method_name] msg here

Returns:

Type Description
Logger

Configured brmspy logger instance

Examples:

>>> from brmspy.helpers.log import get_logger
>>> logger = get_logger()
>>> logger.info("Starting process")  # Prints: [brmspy][<module>] Starting process
Source code in brmspy/helpers/log.py
def get_logger() -> logging.Logger:
    """
    Get or create the brmspy logger instance.

    Returns a configured logger with a custom formatter that outputs
    messages in the format: `[brmspy][method_name] msg here`

    Returns
    -------
    logging.Logger
        Configured brmspy logger instance

    Examples
    --------
    >>> from brmspy.helpers.log import get_logger
    >>> logger = get_logger()
    >>> logger.info("Starting process")  # Prints: [brmspy][<module>] Starting process
    """
    global _logger

    if _logger is None:
        _logger = logging.getLogger("brmspy")
        _logger.setLevel(logging.INFO)

        if not _logger.handlers:
            # Handler for "normal" logs
            normal_handler = logging.StreamHandler()
            normal_handler.setFormatter(BrmspyFormatter())
            normal_handler.addFilter(NonPrintFilter())
            _logger.addHandler(normal_handler)

            # print logs: preserve control chars and explicit \n/\r
            print_handler = logging.StreamHandler()
            print_handler.setFormatter(logging.Formatter("%(message)s"))
            print_handler.addFilter(PrintOnlyFilter())
            print_handler.terminator = ""
            _logger.addHandler(print_handler)

        if _running_under_pytest():
            _logger.propagate = True
        else:
            _logger.propagate = False

    return _logger

setup_worker_logging(log_queue, level=None)

Configure worker logging to forward into the parent's log queue.

Parameters:

Name Type Description Default
log_queue Queue

Queue owned by the parent; the worker will emit logging records into it.

required
level int | None

Root log level for the worker process. Defaults to logging.INFO.

None
Notes

When BRMSPY_WORKER=1, the worker replaces builtins.print to preserve raw control characters and line endings produced by R/cmdstan.

Source code in brmspy/_session/worker/logging.py
def setup_worker_logging(log_queue: Queue, level: int | None = None) -> None:
    """
    Configure worker logging to forward into the parent's log queue.

    Parameters
    ----------
    log_queue : multiprocessing.queues.Queue
        Queue owned by the parent; the worker will emit `logging` records into it.
    level : int | None, optional
        Root log level for the worker process. Defaults to `logging.INFO`.

    Notes
    -----
    When `BRMSPY_WORKER=1`, the worker replaces [`builtins.print`](https://docs.python.org/3/library/functions.html#print)
    to preserve raw control characters and line endings produced by R/cmdstan.
    """
    root = logging.getLogger()
    root.handlers.clear()
    root.setLevel(level or logging.INFO)
    root.addHandler(QueueHandler(log_queue))

    logger = get_logger()

    def _print(*values: object, **kwargs):
        sep = kwargs.get("sep", " ")
        end = kwargs.get("end", "\n")

        # Preserve raw control chars and end exactly as R/cmdstan intended
        msg = sep.join(str(v) for v in values) + end

        if msg == "":
            return

        logger.info(
            msg,
            extra={
                "method_name": "_print",
                "no_prefix": True,
                "from_print": True,  # important for filters
            },
        )

    if os.environ.get("BRMSPY_WORKER") == "1":
        builtins.print = _print