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

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.10
File Size Uploaded
without_logging-0.0.10.tar.gz 12.8 kB Details

Built distribution (wheel)

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

Total release size: 29.2 kB

Release files / without_logging-0.0.10.tar.gz

Download URL without_logging-0.0.10.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
15c8cb346b267a75749da5c166d1d762a6c607236b38a1c7072f7b91988c6684
BLAKE2b-256 checksum
How to use checksums
8ee872a5a72b8c439980296ed99b944368bbc9b31a24ecd71e2755878e4a3d0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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.10-py3-none-any.whl

Download URL without_logging-0.0.10-py3-none-any.whl
Size 16.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fbeabdf30dc39c91fa182b3b20e6cccdca5aa77e94a812af7379818d122adf23
BLAKE2b-256 checksum
How to use checksums
c850febb9401ad60997f54d1633faa36408523df57d87c93c0005772dd91cd12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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

This release

0.0.10 This release

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

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