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 4.0.4

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 4.0.4
File Size Uploaded
xtr_logging_contracts-4.0.4.tar.gz 11.0 kB Details

Built distribution (wheel)

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

Total release size: 25.1 kB

Release files / xtr_logging_contracts-4.0.4.tar.gz

Download URL xtr_logging_contracts-4.0.4.tar.gz
Size 11.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6de3d0a513629e30860d60c3c3713f069fe39ca138c2c55fe60f18ea9757193b
BLAKE2b-256 checksum
How to use checksums
ce3fdf670252559556f02873f9ae672dcb0297527404adf0a424f6141a9a378d
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 Oct 15, 2026.

Transparency log

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

Download URL xtr_logging_contracts-4.0.4-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ffc346a2b4f03c5db070ab5c4580a39d4b0b3ee62f264bbd31a63dce2278b9e6
BLAKE2b-256 checksum
How to use checksums
037df0232e7af4ef29bd7773cafd965721d4a295285f3e58f4cec13996050a79
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 Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

4.1.0

2 release files

This release

4.0.4 This release

2 release files

4.0.3

2 release files

4.0.2

2 release files

3.0.0

2 release files

2.0.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.0.1

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