Skip to main content

A hierarchical, context-manager logger utility with multiprocess mapping capabilities

Project description

logmap

A hierarchical context-manager logger with multiprocess mapping.

Install

pip install logmap

Usage

from logmap import logmap

Basic

with logmap('testing...'):
    # ... do something ...
    pass
⎾ testing... @ 2026-04-19 15:12:55,349
⎿ 0 seconds @ 2026-04-19 15:12:55,349

Duration

with logmap('testing...') as lm:
    naptime = lm.nap()

assert naptime == lm.duration
⎾ testing... @ 2026-04-19 15:12:55,349
│ napping for 0.6 seconds @ 2026-04-19 15:12:55,349
⎿ 0.6 seconds @ 2026-04-19 15:12:55,954

Nested

Nesting indents automatically. lm.log("msg") writes at the current depth.

with logmap('outer') as lm:
    with logmap('middle') as lm2:
        with logmap('inner') as lm3:
            lm3.nap()
⎾ outer @ 2026-04-19 15:12:55,954
│ ⎾ middle @ 2026-04-19 15:12:55,954
│ │ ⎾ inner @ 2026-04-19 15:12:55,954
│ │ │ napping for 0.2 seconds @ 2026-04-19 15:12:55,954
│ │ ⎿ 0.2 seconds @ 2026-04-19 15:12:56,159
│ ⎿ 0.2 seconds @ 2026-04-19 15:12:56,159
⎿ 0.2 seconds @ 2026-04-19 15:12:56,159

Parallel map

import random, time

def fn(naptime):
    t = random.random() * naptime / 2
    time.sleep(t)
    return t

with logmap('function mapping') as lm:
    results = lm.map(fn, list(range(5)), num_proc=2)
⎾ function mapping @ 2026-04-19 15:12:56,159
│ mapping fn to 5 objects [2x]: 100%|██████████| 5/5 [00:02<00:00, 2.31it/s]
⎿ 2.2 seconds @ 2026-04-19 15:12:58,335

For streaming results as they arrive:

with logmap('function mapping') as lm:
    for res in lm.imap(fn, list(range(5)), num_proc=2):
        lm.log(f'got {res:.2f}')   # updates the progress bar

lm.run(...) is the same but discards results — useful when you only care about side effects.

Module-level helpers pmap, pmap_iter, pmap_run provide the same semantics without a logmap context:

from logmap import pmap
pmap(fn, items, num_proc=4)

Note: logmap uses stdlib multiprocessing, so functions passed to parallel map must be picklable (defined at module level — no lambdas or closures).

Progress bar

lm.progress(iterable) wraps any iterable with a progress bar at the current nesting depth:

with logmap('training') as lm:
    for batch in lm.progress(batches, desc='epochs'):
        train(batch)

Async support

logmap works as an async context manager:

async with logmap('fetching') as lm:
    result = await fetch(url)
    lm.log(f'got {len(result)} bytes')

Function decorator

@logmap.fn wraps a function in a logmap context — logging the call, timing it, and optionally logging the return value:

@logmap.fn
def process(x):
    return x * 2

process(21)
⎾ process(21) @ 2026-04-26 12:00:00,000
│ >>> 42 @ 2026-04-26 12:00:00,001
⎿ 0 seconds @ 2026-04-26 12:00:00,001

With options:

@logmap.fn(level="INFO", log_return=False)
def transform(data):
    return data

@logmap.fn(log_args=False)          # hide arguments (e.g. passwords)
def authenticate(token):
    return True

Works with async functions too:

@logmap.fn
async def fetch(url):
    async with aiohttp.ClientSession() as session:
        resp = await session.get(url)
        return await resp.text()

Decorated functions nest naturally with logmap contexts and other decorated functions.

Without a with block

# just a logger
lm = logmap('app')
lm.log('ready')
lm.warning('careful')

# explicit lifecycle (equivalent to a `with` block)
lm = logmap('manual').start()
lm.log('doing stuff')
lm.stop()

Redirecting output

logmap writes to sys.stderr by default. Use configure() to redirect:

from logmap import configure
import sys

configure(sink=sys.stdout)          # e.g. so it doesn't mingle with stderr diagnostics
configure(sink="run.log")           # path — opened line-buffered
configure(sink=my_stringio)         # any writable stream
configure(level="INFO")             # drop DEBUG messages

Colors auto-disable when the sink isn't a TTY (files, StringIO, etc.).

Stdlib logging integration

Route all logmap output through a standard library logging.Logger:

import logging
from logmap import configure

configure(logger=logging.getLogger("myapp"))

This lets you attach any stdlib handler (file, syslog, etc.) and have logmap participate in your application's logging hierarchy. Level integers match stdlib conventions, so filtering works as expected. Pass logger=None to switch back to direct sink output.

Structured (JSON) output

For log aggregation or machine parsing, enable JSON-lines mode:

configure(structured=True)

Each line becomes a JSON object:

{"ts": "2026-04-19T15:12:55.349000", "level": "INFO", "msg": "doing work", "depth": 1, "task": "outer"}

The msg field contains the clean message (no indentation prefix), while depth and task give you the hierarchical context as structured data.

Silencing

with logmap.quiet():
    with logmap('no output'):       # nothing gets printed
        ...

logmap.disable()                    # global off switch
logmap.enable()                     # back on

with logmap('task', announce=False): suppresses just the open/close lines while keeping lm.log() active.

Thread safety

Nesting depth and quiet state are thread-local — multiple threads can use logmap concurrently without interleaving indentation or silencing each other. Output writes are serialized with a lock to prevent garbled lines.

Multiprocessing on macOS

On macOS, logmap defaults to the forkserver multiprocessing context instead of fork (which Python 3.12+ deprecates due to deadlock risk with threads). You can still pass context="spawn" or context="fork" explicitly to lm.map() / pmap() if needed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

logmap-0.3.1.tar.gz (29.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

logmap-0.3.1-py3-none-any.whl (22.5 kB view details)

Uploaded Python 3

File details

Details for the file logmap-0.3.1.tar.gz.

File metadata

  • Download URL: logmap-0.3.1.tar.gz
  • Upload date:
  • Size: 29.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for logmap-0.3.1.tar.gz
Algorithm Hash digest
SHA256 d7f9c49d6f6363ce2c0cb38d7b9bf2c6d21aafb884e030b94227555aca63c8c2
MD5 9a59589402ca75a6c5eda3a995fa657e
BLAKE2b-256 b498f76ffa17c211a231b1687b3c23c5d51b23e09ff5eec35a8f41317f8f9783

See more details on using hashes here.

Provenance

The following attestation bundles were made for logmap-0.3.1.tar.gz:

Publisher: publish.yml on quadrismegistus/logmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file logmap-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: logmap-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 22.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for logmap-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 920b5bd468d8872a8c8040019e1aef97af277b7257bbdc27f4c48793a9ef3838
MD5 e74dd8a64d358346693a3894cb85235c
BLAKE2b-256 081670d1e65d5526745c2c5646b82cce78257960e4ccec7ed0969271b2d068f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for logmap-0.3.1-py3-none-any.whl:

Publisher: publish.yml on quadrismegistus/logmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page