Skip to main content

A Python library combining standard logging and Rich for beautiful logging.

Project description

logurich

Python 3.9+ License: MIT PyPI version

A Python library combining standard logging and Rich for beautiful logging.

Installation

pip install logurich
pip install logurich[click]

Usage

from rich.panel import Panel

from logurich import get_logger, init_logger

init_logger("INFO", enqueue=False)

logger = get_logger(__name__)

logger.info("This is a log message")
logger.info("Hello %s", "world")

logger.info(
    "Rich renderables",
    extra={
        "renderables": (
            Panel("Rich panel output", border_style="green"),
        )
    },
)

with logger.contextualize(app=logger.ctx("demo", style="yellow")):
    logger.info("This log has scoped context")

logger.info(
    "Per-call context",
    extra={
        "context": {
            "session": logger.ctx("sess-42", style="cyan", show_key=True),
        }
    },
)

For full IDE autocompletion of ctx(...), rich(...), bind(...), and contextualize(...), use get_logger(...) from logurich instead of logging.getLogger(...). It returns the same logger instance but typed as LogurichLogger. logger.ctx(...) is shorthand for the existing module-level ctx(...) helper, and logger.contextualize(...) is a convenience alias for global_context_configure(...). The module-level helpers remain supported if you prefer global_context_configure(...) or extra={"context": {"key": ctx(...)}}.

logging.getLogger(...) still works at runtime — only the typing differs.

For short-lived scripts and CLIs, init_logger() automatically registers an atexit hook, so you do not need to call shutdown_logger() just to flush logs at process exit.

Named Loggers

Use the standard library to create named loggers:

from logurich import get_logger, init_logger

init_logger("INFO", enqueue=False)

logger = get_logger(__name__)
logger.info("Hello from %s", __name__)

Use get_logger(...) from logurich for typed access. logging.getLogger(...) still works at runtime.

Using Logurich in Reusable Libraries

If you are writing a Python library that will be imported by another program, the library should not call init_logger() on its own. Let the main application own logging configuration, handler setup, and shutdown.

Inside the library, use standard named loggers:

# mylib/service.py
from rich.panel import Panel

from logurich import ctx, get_logger

logger = get_logger(__name__)


def run_job(job_id: str) -> None:
    logger.info(
        "Starting job %s",
        job_id,
        extra={"context": {"job": ctx(job_id, style="cyan", show_key=True)}},
    )
    logger.info(
        "Job details",
        extra={
            "renderables": (
                Panel(f"Job {job_id} is running", border_style="green"),
            )
        },
    )

Then configure Logurich once in the main program:

# main.py
from logurich import init_logger
from mylib.service import run_job

init_logger("INFO", enqueue=False)

run_job("job-42")

Guidelines for libraries:

  • Use get_logger(__name__) from logurich inside library modules.
  • Do not call init_logger() or shutdown_logger() from library code.
  • Emit normal stdlib log calls such as logger.info("Value %s", value).
  • Use extra={"context": ...} and extra={"renderables": ...} only as optional metadata; they render nicely when the consuming application uses Logurich, and logger.ctx(...) / logger.rich(...) are also available when Logurich has been imported/configured by the application.
  • If the library starts worker processes and the application uses enqueue=True, accept the queue from the application and call configure_child_logging(queue) inside each worker process.

Multiprocessing

When enqueue=True, Logurich is process-safe only if worker processes send records through the shared logging queue created by the parent process.

import multiprocessing as mp

from logurich import configure_child_logging, get_log_queue, get_logger, init_logger


def worker(log_queue: mp.Queue, worker_id: int) -> None:
    configure_child_logging(log_queue)
    get_logger(f"worker.{worker_id}").info("worker=%s ready", worker_id)


def main() -> None:
    init_logger("INFO", enqueue=True)
    log_queue = get_log_queue()

    processes = [
        mp.Process(target=worker, args=(log_queue, index), name=f"worker-{index}")
        for index in range(3)
    ]

    for process in processes:
        process.start()

    for process in processes:
        process.join()

Only the process that calls init_logger(..., enqueue=True) owns the console and file handlers. Child processes must call configure_child_logging(queue) before logging.

Call shutdown_logger() explicitly only when you need deterministic teardown before process exit, such as in tests or when reconfiguring logging multiple times in the same interpreter.

Click CLI helper

Install the optional Click extra to automatically expose logger configuration flags inside your commands:

import click

from logurich import get_logger
from logurich.opt_click import click_logger_params


@click.command()
@click_logger_params
def cli():
    logger = get_logger(__name__)
    logger.info("Click integration ready!")

The click_logger_params decorator injects --logger-level, --logger-verbose, --logger-filename, --logger-level-by-module, and --logger-rich flags and configures Logurich before your command logic runs. The usage example above is also available at examples/click_cli.py.

Idempotent initialisation (force)

By default, calling init_logger() a second time is a no-op — the existing configuration is kept and the call returns None. Pass force=True to tear down the current setup and reconfigure from scratch:

from logurich import init_logger

init_logger("INFO", enqueue=False)          # first call: configures logging
init_logger("DEBUG", enqueue=False)         # no-op, returns None
init_logger("DEBUG", enqueue=False, force=True)  # reconfigures logging

This is useful when tests or interactive sessions need to reset logging between runs.

User input

The user_input module provides Rich-enhanced prompts with type coercion, hidden input, and optional timeouts. It does not depend on Click.

from logurich import get_logger, init_logger, user_input, user_input_with_timeout

init_logger("INFO", enqueue=False)
logger = get_logger(__name__)

# Basic string input
name = user_input("Enter your name", type=str)

# Integer input with a default value
count = user_input("How many items?", type=int, default=5)

# Hidden input (e.g. passwords)
secret = user_input("Enter secret", type=str, hide_input=True)

# Input with a timeout (5 seconds, Unix only — falls back to regular input on Windows)
answer = user_input_with_timeout("Quick! Type something", timeout_duration=5)

A runnable example is available at examples/user_input_example.py.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

logurich-0.9.2.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

logurich-0.9.2-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file logurich-0.9.2.tar.gz.

File metadata

  • Download URL: logurich-0.9.2.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for logurich-0.9.2.tar.gz
Algorithm Hash digest
SHA256 12a629c05c175ea653698790e7ce95111361edf89aef88c6a63bb59306b95137
MD5 fec2420ec81024a354d32889f30a68eb
BLAKE2b-256 9da5ac384348bbe980891c5b55c638cdf588169d6b7f9d8e9dacdd000fdc5695

See more details on using hashes here.

Provenance

The following attestation bundles were made for logurich-0.9.2.tar.gz:

Publisher: ci.yml on PakitoSec/logurich

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file logurich-0.9.2-py3-none-any.whl.

File metadata

  • Download URL: logurich-0.9.2-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for logurich-0.9.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9f544e118995d02644635fe2f07bcd346d0e2e640de26ddb9f882e0696a37b2f
MD5 f3f5a4bc39bd1e1fe6f90f2d21c6817a
BLAKE2b-256 4d97fd7d32fdb5cdae3e6d6b402026d4f27fa1a764f355f6af249cd33019d72d

See more details on using hashes here.

Provenance

The following attestation bundles were made for logurich-0.9.2-py3-none-any.whl:

Publisher: ci.yml on PakitoSec/logurich

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page