Skip to main content

xtr-logging-contracts

The logging contract, and nothing else — so a library that logs installs nothing else.

python 3.11+ core dependencies: 1 typed license MIT

Why?

A library that logs should not decide where records go. It takes a LoggerInterface, defaults to a NullLogger, and leaves handlers, formatters and configuration to the application that wires it.

Which means a library needs the contract, not an implementation — and should not pay for one. Depending on xtr-logging to annotate one parameter drags in a JSON codec, a clock and seventeen handlers the library never touches. This package is that dependency, reduced to what the seam is actually made of:

  • 🧩 LoggerInterface — eight severities plus log(), with a context mapping.
  • 🕳️ NullLogger — what makes logging optional for your callers.
  • 🪜 Level — the eight RFC 5424 severities, comparable as integers.
  • 🗂️ Context — the mapping every method takes, and the key an exception goes under.
  • 🪶 One dependency — typing-extensions, for @override on 3.11.
from xtr_logging_contracts import LoggerInterface, NullLogger


class Checkout:
    def __init__(self, logger: LoggerInterface | None = None) -> None:
        self._logger = logger or NullLogger()

    def pay(self, order: Order) -> None:
        self._logger.error("payment {order} failed", {"order": order.id})

Install

uv add xtr-logging-contracts

Requires Python 3.11+.

Who installs what

Depends on
A library that logs xtr-logging-contracts at runtime, xtr-logging as a dev dependency — its tests build real loggers and assert on a TestHandler.
An application xtr-logging, which implements this contract and wires channels, handlers and processors from configuration.

xtr-logging re-exports every symbol here rather than redefining it, so xtr_logging.LoggerInterface is xtr_logging_contracts.LoggerInterface. That identity is what lets a container register a logger under the interface and a library, which never imported xtr-logging, receive it.

The interface

class LoggerInterface(Protocol):
    def emergency(self, message: str, /, context: Context | None = None) -> None: ...
    def alert(self, message: str, /, context: Context | None = None) -> None: ...
    def critical(self, message: str, /, context: Context | None = None) -> None: ...
    def error(self, message: str, /, context: Context | None = None) -> None: ...
    def warning(self, message: str, /, context: Context | None = None) -> None: ...
    def notice(self, message: str, /, context: Context | None = None) -> None: ...
    def info(self, message: str, /, context: Context | None = None) -> None: ...
    def debug(self, message: str, /, context: Context | None = None) -> None: ...
    def log(self, level: LevelLike, message: str, /, context: Context | None = None) -> None: ...

The rules:

  • context is a mapping of anything. Formatters describe what they cannot serialise; a value never makes logging fail.
  • An exception to report goes under context["exception"].
  • {key} placeholders in the message are filled from context downstream, not by the logger, so a handler can still see the template and the values apart.

AbstractLogger implements the eight severity methods on top of log(), so an implementation writes one method. LoggerAware gives a class a logger that is a NullLogger until set_logger() is called.

ruff's PLE1205 assumes every logger.info(...) is the standard library's and flags the context mapping as a stray format argument. Ignore it in projects using this interface.

Levels

Level Value RFC 5424 Level Value RFC 5424
DEBUG 100 7 ERROR 400 3
INFO 200 6 CRITICAL 500 2
NOTICE 250 5 ALERT 550 1
WARNING 300 4 EMERGENCY 600 0

Anywhere a level is accepted, Level.parse reads it: a Level, its value, an RFC 5424 severity, or a name in any case ("error"). Anything else raises InvalidLevelError.

Context

Context is Mapping[str, object] — the second argument to every method above. Values are object because a caller may log anything; whatever writes the record normalises what it cannot serialise rather than refusing it. An exception to report goes under EXCEPTION_KEY:

from xtr_logging_contracts import EXCEPTION_KEY

logger.error("payment failed", {"order": order.id, EXCEPTION_KEY: error})

What is not here

Everything that acts on what was logged: LogRecord, Logger, LoggerFactory, LoggingConfig, the seventeen handlers, the processors, the formatters, ambient bound_context(), and the standard-library bridge. All of that is xtr-logging.

LogRecord in particular belongs there, not here: it appears in no signature above. A library that logs never builds one or sees one — it is made inside the logger and consumed by handlers and processors, which are implementation.

HandlerInterface, ProcessorInterface and FormatterInterface are deliberately absent for the same reason, plus one more: nothing outside xtr-logging implements them yet, and a contract package earns its stability by staying small. They move here when a third-party handler needs them — and LogRecord would move with them.

Errors

Error Raised when
LoggingError Never directly — the base every logging error derives from, xtr-logging's included
InvalidLevelError A value names no level (also a ValueError)

Development

Developed in the python-xtr monorepo, under packages/xtr-logging-contracts; run the commands below from there. The python-xtr-logging-contracts repository is a read-only copy, so send issues and pull requests to the monorepo.

uv sync
uv run ruff check . && uv run ruff format --check .
uv run basedpyright
uv run ty check
uv run pytest

License

MIT — see LICENSE.

Release files for xtr-logging-contracts 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xtr-logging-contracts 1.0.0
File Size Uploaded
xtr_logging_contracts-1.0.0.tar.gz 9.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for xtr-logging-contracts 1.0.0
File Interpreter ABI Platform
xtr_logging_contracts-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 23.7 kB

Release files / xtr_logging_contracts-1.0.0.tar.gz

Download URL xtr_logging_contracts-1.0.0.tar.gz
Size 9.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3f7ecf9478713cb627a35b4f4d56e88beb841745819bbd22649fa2e99dc3717b
BLAKE2b-256 checksum
How to use checksums
e8c3a040dae79e3e814cad1b16f4420282024b8cdfeb6bbfc0139c180bc30254
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / xtr_logging_contracts-1.0.0-py3-none-any.whl

Download URL xtr_logging_contracts-1.0.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
56a4f943770a7927bbfba55e506e4d92fe6f97327059f8683848b344ec578ab6
BLAKE2b-256 checksum
How to use checksums
a35421716fc8bf45ed28108f689e637e3edd2a21ccac3e04007945a5c292033b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.1

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page