Skip to main content

without-logging

A without pipeline for logs. Logger calls (yours and every third-party library's) ultimately produce a stream of records; this package parses each one into an immutable Record value, lets you filter and enrich them as ordinary processors, and drains the result into a sink you own.

The two stdlib logging pain points it targets: the impenetrable, mutable, noun-heavy configuration, and the monolithic handlers that bundle unrelated decisions (when to flush, how to rotate, how to format) into one object. Here a record is a value, each stage is a Processor, and the sink is whatever you compose.

import logging

from without_streams import compose, from_selector, from_sink
from without_logging import Level, at_least, capture


async def write(record):
    print(f"{record.timestamp:%H:%M:%S} {record.level_name} {record.message}")


pipeline = compose(from_selector(at_least(Level.WARNING)), from_sink(write))

async with capture(pipeline):  # attaches to the root logger for the block
    logging.getLogger("app").warning("disk almost full", extra={"free_pct": 3})

capture is the one impure piece: it activates a handler on the root logger, turns the pushed records into a Stream, and runs your pipeline against them for the life of the block. Everything upstream of the sink is pure and testable without touching the logging machinery.

To write to a file without paying a thread hop per line, without-streams' offload runs a blocking writer on a single dedicated thread, fed by a queue that delivers items in bursts (so the writer flushes when it catches up, with no flush-frequency knob). Writers are named by destination: to_rotating_file writes to a file (owning the byte count and clock, rotating on any combination of size (max_bytes), a relative interval (max_age), and absolute wall-clock boundaries (schedule=at_times(...))), and to_stream writes to a caller-owned text stream (sys.stderr, a socket) without closing it. Both take strings (render a Record to text with a from_map in front):

from datetime import timedelta

from without_streams import compose, from_map, from_selector
from without_streams import offload
from without_logging import Level, at_least, to_rotating_file

writer = to_rotating_file(lambda i, when: directory / f"app.{i}.log", max_bytes=64 << 20, max_age=timedelta(hours=1))
async with offload(writer) as sink:
    lines = compose(from_map(render), sink)  # Record -> str -> file
    async with capture(compose(from_selector(at_least(Level.WARNING)), lines)):
        ...  # WARNING+ records written off the event loop, rotated by size and time

See the without-logging guide (with the API reference) for the design narrative: why stdlib becomes a one-way source, why filtering is just the core from_selector builder, and where fan-out to multiple sinks slots in.

Metadata

Release files for without-logging 0.0.7

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

Source distribution (sdist)

Source distribution for without-logging 0.0.7
File Size Uploaded
without_logging-0.0.7.tar.gz 12.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for without-logging 0.0.7
File Interpreter ABI Platform
without_logging-0.0.7-py3-none-any.whl Python 3 none any Details

Total release size: 29.2 kB

Release files / without_logging-0.0.7.tar.gz

Download URL without_logging-0.0.7.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e44236b72bfeed09d332acb3afac511e131550a1a09eaa89a7d08b008e8d9e61
BLAKE2b-256 checksum
How to use checksums
51c4c79464a8041010d7b645229588cc4554568dcccacd97bba81d1be8f59c9c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / without_logging-0.0.7-py3-none-any.whl

Download URL without_logging-0.0.7-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c28c2040d309f89ffacad267b916fd90d1c62c752e04d5bc76199f483b830663
BLAKE2b-256 checksum
How to use checksums
4de5e598e849202d0a9e6d5b0c537501109b7498e13c0494c2db59f5d8113653
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.0.9

2 release files

0.0.8

2 release files

This release

0.0.7 This release

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

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