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.1

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.1
File Size Uploaded
xtr_logging_contracts-1.0.1.tar.gz 9.8 kB Details

Built distribution (wheel)

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

Total release size: 23.7 kB

Release files / xtr_logging_contracts-1.0.1.tar.gz

Download URL xtr_logging_contracts-1.0.1.tar.gz
Size 9.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6e440060487237dad08b0bd7401490054982514ea7a943ce05bad0a9d397b1ff
BLAKE2b-256 checksum
How to use checksums
037d37d8b5b04baa1de1acbdcbad3d737e700c81740757d5b7cc6eba3b90678c
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.1-py3-none-any.whl

Download URL xtr_logging_contracts-1.0.1-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c37d02079c9dd17eec001eb57db1f0516c1b523f17037d87a33a5d25bb6881dd
BLAKE2b-256 checksum
How to use checksums
16812fc9a2c83a25c8a92d27b50736384be6dcc91d03a55fbf357c3c658566ed
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

This release

1.0.1 This release

2 release files

1.0.0

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