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 builds on these types rather than redefining them: its loggers implement this LoggerInterface and its levels are this Level. Import them from xtr_logging_contracts — xtr-logging does not re-export them. That one set of types 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.

Metadata

Release files for xtr-logging-contracts 5.2.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 5.2.1
File Size Uploaded Provenance Actions
xtr_logging_contracts-5.2.1.tar.gz 11.0 kB With provenance Details for xtr_logging_contracts-5.2.1.tar.gz

Built distribution (wheel)

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

Total release size: 25.1 kB

Release files / xtr_logging_contracts-5.2.1.tar.gz

Download URL xtr_logging_contracts-5.2.1.tar.gz
Size 11.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f54e21dc058e4415c45800d7cd32b3eaf662565f429b74c2a6d3c330e9759141
BLAKE2b-256 checksum
How to use checksums
a0bfb4bef17d9df6ecba951ed4da2fc657ae954ca6136e0b44e4e227ad1bd721
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 28, 2026.

Transparency log

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

Download URL xtr_logging_contracts-5.2.1-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ade4344e1b10c9b49728c4718535d60229385ae138690943bd40a2c0abc51ac
BLAKE2b-256 checksum
How to use checksums
83a9fc496f902d8b8d881d996ddb3ab20fafe40f3f257ec90cf9326a28960ed8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 28, 2026.

Transparency log

Security for xtr-logging-contracts 5.2.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for xtr_logging_contracts-5.2.1.tar.gz

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 Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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