Skip to main content

⚡ pulselog

Non-blocking Python logger with a live browser dashboard.

PyPI License: MIT Python

pip install pulselog

Every log.info() call costs 1.8µs. Zero config. Browser opens automatically.


📚 Table of Contents


🚀 Performance Tiers

Tier How to get it Single-thread Multi-thread (8)
Pure Python pip install pulselog 355k/sec 301k/sec
Rust extension maturin build --release 735k/sec 608k/sec

The Rust-backed native extension delivers 2× throughput by moving the hot path into compiled code. Same API, zero code changes — just a different install method.

# Pure Python (default)
pip install pulselog

# Rust extension (macOS / Linux)
pip install maturin
git clone https://github.com/your-repo/pulselog
cd pulselog
maturin build --release
pip install dist/*.whl

📊 Benchmarks

Pure Python

(Windows, Python 3.12, dashboard=False)

Scenario Throughput Notes
Single-thread burst 355.8k/sec p50=1.8µs · p99=6.0µs · p99.9=39.7µs
Multi-thread burst (8 threads) 301.4k/sec matches single-thread — zero contention
Sustained (5s) 427.6k/sec 2.1M records logged
Queue saturation 383.9k/sec 0 drops — worker drained fast enough
Realistic (info + save + warn + error) 216.4k/sec mixed call types with kwargs
Fast path (info_fast) 455.4k/sec no kwargs — zero dict allocation
Mixed workload (6 threads, varied calls) 339.9k/sec 1.86M records, 0 drops

Rust Extension

(macOS, release build)

Scenario Throughput Notes
Single-thread (1M records) 735k/sec 2× over pure Python
Multi-thread stress (8 × 500k) 608k/sec 4M total, 0 drops

Latency Under Concurrent Load

p99 with 4 background threads flooding the queue (Pure Python):

p50 p99 p99.9
No contention 1.8µs 6.0µs 39.7µs
Under load (4 bg threads) 2.1µs 5.7µs 27.6µs

p99 is lower under load than idle — per-thread sharding means concurrent producers create zero interference with each other.

Version Improvements (v0.1.2 → v2.0.0)

Metric v0.1.2 v2.0.0 Change
Single-thread 263k/sec 356k/sec +35%
Multi-thread 41k/sec 301k/sec +633%
Fast path (new) 455k/sec 🆕
Realistic 160k/sec 216k/sec +35%
p99.9 latency 87.9µs 39.7µs −55%
Dropped records 0 0 still 0 ✅

A typical ML training loop logs 10–100 records/sec. PulseLog handles 2,164× that load before any issues.


⚡ Quick Start

from pulselog import Logger

log = Logger("my-app", checkpoint_path=":memory:")

log.info("training started", epoch=1)
log.warning("learning rate too high", lr=0.1)
log.save("epoch-1", {"acc": 0.91, "loss": 0.23}, status="DONE", progress=33)

log.shutdown()

A browser tab opens at http://localhost:5678 and streams every log in real time.


🤔 Why pulselog?

Standard logging blocks the calling thread on every write — waiting for a file, a socket, or a database. In tight loops (ML training, data pipelines, inference servers) this adds up fast.

pulselog never blocks. Every log call enqueues a record in O(1) and returns immediately. A daemon worker drains the queue every 10ms and pushes batches to the dashboard over WebSocket.

log.info()             ← O(1), ~1.8µs, never blocks
      │
      ▼
  ShardedLogQueue       ← per-thread deques, zero cross-thread contention
      │                    each thread writes to its own private deque
      │
      ▼  every 10ms (adaptive — halves under load)
BackgroundWorker        ← daemon thread, fan-drains all shards
      │
      ├──▶ DashboardServer.broadcast()  ← WebSocket → live browser
      │
      └──▶ (custom handlers)

Why per-thread sharding matters: A single shared queue means all producer threads compete for the same lock on every put(). At 8 threads, that bottleneck cut throughput from 356k to 41k/sec — an 87% collapse. Per-thread sharding eliminates the shared state entirely. Each thread appends to its own deque (a GIL-atomic operation) and the worker fan-drains all shards once per cycle. Result: multi-thread throughput matches single-thread.


🖥️ Dashboard

Single self-contained HTML file served over WebSocket — no build step, no CDN, no npm.

Logs Tab

  • 🎨 Colour-coded by level — DEBUG gray · INFO blue · WARNING amber · ERROR/CRITICAL red
  • 🔍 Full-text search — filter logs by message content in real time
  • 🎚️ Level filter — toggle DEBUG, INFO, WARNING, ERROR, CRITICAL visibility
  • 📜 Virtual list rendering — 100k+ logs with zero browser lag
  • ⏬ Auto-scroll with manual scroll override
  • 📤 Export session as JSON

Checkpoints Tab

  • 📈 Progress bar per checkpoint (overall = average across all checkpoints)
  • 🔎 Expandable JSON data viewer
  • 🏷️ Status badges — DONE ✅ · IN_PROGRESS 🟡 · FAILED 🔴 · SKIPPED
  • 🔍 Search checkpoints by name

🔧 API

Logger

log = Logger(
    name             = "my-app",
    host             = "localhost",
    port             = 5678,          # auto-increments if taken
    auto_open        = True,          # open browser on start
    dashboard        = True,          # False for CI / production
    checkpoint_path  = ".pulselog/checkpoints.db",  # ":memory:" for in-memory
    level            = "DEBUG",
    worker_interval  = 0.01,          # drain interval in seconds (default 10ms)
    queue_size       = 100_000,       # max records before oldest evicted
    overflow         = "drop",        # "drop" | "block" | "raise"
)

Logging

log.debug("msg", **extra)
log.info("msg", **extra)
log.warning("msg", **extra)
log.error("msg", **extra)
log.critical("msg", **extra)

# kwargs appear as structured metadata in the dashboard
log.info("request handled", user_id=42, latency_ms=12, status=200)

# exception() captures the current traceback automatically
try:
    result = model.predict(x)
except Exception:
    log.exception("prediction failed", input_shape=str(x.shape))

Zero-Allocation Fast Paths

When you call log.info("msg", key=val), Python builds the {"key": val} dict before the function is entered — in the C layer, before any pulselog code runs. At 216k/sec that's 216k dict allocations/sec you cannot avoid with **kwargs syntax.

For calls where you don't need per-record metadata, use the fast-path variants:

log.info_fast("step done")       # ~455k/sec — no dict allocated, ever
log.debug_fast("heartbeat")
log.warning_fast("queue high")
log.error_fast("connection lost")
log.critical_fast("out of memory")

When to use which:

# Tight loop — no metadata needed → use fast path
for step in range(100_000):
    log.info_fast("step")               # 455k/sec

# Need metadata → use standard API
log.info("step", loss=loss, acc=acc)    # 216k/sec — kwargs cost is unavoidable

Checkpoints

Checkpoints persist structured data with progress tracking — ideal for ML training, data pipelines, and long-running jobs.

# Save a checkpoint
log.save(
    name      = "epoch-5",
    data      = {"loss": 0.31, "acc": 0.94},
    status    = "DONE",        # "DONE" | "IN_PROGRESS" | "FAILED" | "SKIPPED"
    note      = "best so far",
    progress  = 50             # 0–100, shown as progress bar in dashboard
)

# Load a checkpoint (returns dict or None — never raises)
result = log.load("epoch-5")
# → {"loss": 0.31, "acc": 0.94, "status": "DONE", "note": "best so far", "progress": 50}

# List all checkpoints (most recent first)
names = log.checkpoints()
# → ["epoch-5", "epoch-4", "epoch-3"]

# Delete a checkpoint
log.delete_checkpoint("epoch-3")

In-memory mode for tests and ephemeral runs:

log = Logger("test", checkpoint_path=":memory:")
log.save("step-1", {"value": 42}, status="DONE", progress=50)
log.load("step-1")  # → works immediately, no disk I/O

Context and Grouping

# Tag groups subsequent logs under a label (per-thread — safe for concurrent use)
log.tag("training")

# Context manager — restores the previous tag on exit, even on exception
with log.context(tag="validation"):
    log.info("val loss", loss=0.41)
# tag is restored here

# Visual divider in the dashboard stream
log.divider("epoch boundary")

Utilities

log.flush(timeout=2.0)   # drain queue synchronously — returns False if timeout hit
log.shutdown()            # graceful teardown (also called automatically on exit)

stats = log.stats()
# {
#   "records_dropped":  int,
#   "drop_rate":        float,   # e.g. 0.04 = 4%
#   "queue_size":       int,
#   "queue_capacity":   int,
#   "queue_fill_pct":   float,
#   "checkpoints_saved": int,
#   "dashboard_clients": int,
#   "uptime_seconds":   float,
# }

🔌 stdlib logging Integration

Drop-in bridge — all structured fields (lineno, filename, funcName, exc_info) are forwarded to the dashboard.

import logging
from pulselog.handler import PulseHandler

logging.getLogger().addHandler(PulseHandler("my-app"))

logging.info("this appears in the dashboard")
logging.error("with traceback", exc_info=True)  # traceback preserved

⚙️ Configuration

Priority (highest → lowest): Logger() kwargs → env vars → pulselog.toml → defaults

Environment variables
PULSELOG_DASHBOARD=false
PULSELOG_HOST=0.0.0.0
PULSELOG_PORT=8080
PULSELOG_AUTO_OPEN=false
PULSELOG_CHECKPOINT_PATH=/data/checkpoints.db
PULSELOG_LEVEL=INFO
PULSELOG_WORKER_INTERVAL=0.01
pulselog.toml (place in project root)
[pulselog]
host            = "0.0.0.0"
port            = 8080
auto_open       = false
level           = "INFO"
worker_interval = 0.01

🏭 Production Usage

# Disable dashboard, keep checkpoints, log to stderr on drop
log = Logger(
    "prod",
    dashboard        = False,
    checkpoint_path  = "/data/checkpoints.db",
    overflow         = "drop",   # never block — warn on stderr instead
)

With dashboard=False:

  • ✅ No threads started beyond the background worker, no port bound, no browser opened
  • ✅ Checkpoint reads/writes still work
  • ✅ CI environments (CI=true) disable the dashboard automatically

📖 Examples

ML Training Example
from pulselog import Logger
import time

log = Logger("resnet-training", checkpoint_path=":memory:")

log.tag("training")
for i in range(10):
    loss = 1.0 - i * 0.08
    acc = 0.6 + i * 0.035

    log.info(f"epoch {i+1}", loss=round(loss, 3), acc=round(acc, 3))

    log.save(
        f"epoch-{i+1}",
        {"loss": loss, "acc": acc},
        status   = "DONE",
        progress = (i + 1) * 10,
    )

    if i > 0 and loss > prev_loss * 1.5:
        log.warning("loss spike", epoch=i+1, loss=loss)

    prev_loss = loss
    time.sleep(0.5)

log.shutdown()
Data Pipeline Example
from pulselog import Logger

log = Logger("etl-pipeline")

with log.context("ingestion"):
    log.info("loading source", table="events", rows=1_200_000)
    records = ingest()
    log.info("ingestion complete", rows=len(records))

with log.context("validation"):
    errors = validate(records)
    if errors:
        log.warning("schema errors found", count=len(errors))
    log.save("validation", {"errors": len(errors), "rows": len(records)},
             status="DONE", progress=40)

with log.context("feature_engineering"):
    for feat in ["activity_7d", "churn_score", "ltv_estimate"]:
        features = compute_feature(feat, records)
        log.info("feature computed", name=feat, coverage=features.coverage)
        if features.null_rate > 0.03:
            log.warning("high null rate", feat=feat, null_rate=features.null_rate)

with log.context("warehouse_write"):
    rows_written = write_to_warehouse(features)
    log.info("write complete", rows=rows_written, target="bigquery://features")
    log.save("pipeline_run", {"rows": rows_written, "features": 3},
             status="DONE", progress=100)

log.shutdown()

🧠 Design Notes

Design choice Why it matters
Per-thread sharding ShardedLogQueue gives each producer thread a private deque. put() appends to the caller's own deque — no lock, no shared state, no GIL contention. The worker registers each thread's deque on first use and fan-drains all shards every cycle — this is why multi-thread throughput matches single-thread.
Lock-free put() deque.append() is GIL-atomic in CPython. The hot path acquires no mutex. The threading.Event wake signal fires only on empty→non-empty transitions (~100/sec at steady state), not on every put() (which would be 300k+/sec).
Batch timestamps LogRecord.timestamp is set to None at creation. The worker stamps time.time() once per drain cycle and fills every record in the batch — moving ~300k time.time() syscalls/sec down to ~100/sec, at the cost of sub-10ms timestamp precision within a batch.
__slots__ on LogRecord Eliminates the per-instance __dict__ (~240 bytes each). At 300k records/sec, the original @dataclass design generated ~72 MB/sec of heap churn. With __slots__, allocation pressure drops ~3× and GC pause frequency falls accordingly — why p99.9 dropped from 87µs to 39µs.
Rust extension An optional native module built with maturin moves record allocation and queue insertion into compiled Rust. The Python API is identical; the extension is detected and used automatically when installed.
Worker Wakes immediately on new records via threading.Event, falls back to polling every 10ms. Adaptive: halves the interval when queue exceeds 50% capacity, restores it when calm. Drain rate is ~17–19M records/sec — 50× headroom over the producer ceiling.
Drop policy When a shard is full, the oldest record is evicted and a stderr warning is emitted every 1,000 drops. Configure overflow="block" to pause the caller instead, or overflow="raise" to surface the error explicitly.
Shutdown atexit and SIGTERM both call shutdown() once (guarded against double-invocation). flush() accepts a configurable timeout and returns False if the queue wasn't fully drained in time.
Thread safety tag() and context() use threading.local() so each thread maintains its own tag state independently. The overflow strategy is resolved to a bound method at __init__ time — no string comparisons on the hot path.

📦 Requirements

  • Python ≥ 3.8
  • websockets ≥ 11.0 (only needed with dashboard=True)
pip install pulselog            # includes websockets

📄 License

MIT

Author: DevBuddy

Download files

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

Source Distribution

pulselog-0.1.5.tar.gz (36.8 kB view details)

Uploaded Source

Built Distributions

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

pulselog-0.1.5-cp312-cp312-win_amd64.whl (242.1 kB view details)

Uploaded CPython 3.12Windows x86-64

pulselog-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

pulselog-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (384.5 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

pulselog-0.1.5-cp311-cp311-win_amd64.whl (240.4 kB view details)

Uploaded CPython 3.11Windows x86-64

pulselog-0.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.0 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

pulselog-0.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.2 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

pulselog-0.1.5-cp310-cp310-win_amd64.whl (240.7 kB view details)

Uploaded CPython 3.10Windows x86-64

pulselog-0.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.1 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

pulselog-0.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.3 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

pulselog-0.1.5-cp39-cp39-win_amd64.whl (241.7 kB view details)

Uploaded CPython 3.9Windows x86-64

pulselog-0.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.7 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

pulselog-0.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.7 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

pulselog-0.1.5-cp38-cp38-win_amd64.whl (243.0 kB view details)

Uploaded CPython 3.8Windows x86-64

pulselog-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (386.8 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

pulselog-0.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.3 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

Details for the file pulselog-0.1.5.tar.gz.

File metadata

  • Download URL: pulselog-0.1.5.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5.tar.gz
Algorithm Hash digest
SHA256 04a99950d65e5b8113100cc2b8bfd3833cd6fd2396994549d1c65aae706b3afd
MD5 f391685a5a8b25916beb5c8be37216d0
BLAKE2b-256 ff193d37b7be6b37d1210ea773e04e68d5ac972d20e4a5317d7abedb93590724

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: pulselog-0.1.5-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 242.1 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 66a102286faac5bb988ca82a1ab3b8aa1fd475e55392be72e0d8049ce01be3cd
MD5 49d28e57a8e1289c72bff4519897a82c
BLAKE2b-256 6b9c797ac1034cc6c26def0cffd5693e80bab5fe3c2c36749f7bac5a049fd5a8

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a50bfd9cd9ee341d925d922a896563b8134766bebad68db97d4dcf8055a00b8d
MD5 9846c5f8ca9fd35a7cbca70c5ff43da5
BLAKE2b-256 da9ac3ff26acedb163f2d430eba07227b9de9eebe34620251545cc04b0cc7bfc

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 f52c25246921e5c5e122d9239d66fb0cc1079245615c728604e402317f7f493f
MD5 b12ed1622f11b19e5ba663a17eba0af0
BLAKE2b-256 d83cf7ca7ce06b9ccb9b5a35aa1a6cf78a593eab8a35b6871d6f225c93bb193d

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: pulselog-0.1.5-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 240.4 kB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 0493de081c1084ada574510db57cc98662f6155f7f500f7167991487ddcf7f53
MD5 4c13cb96c92bfbac011effc9f9be42f9
BLAKE2b-256 b481d604256a2ff5d2ef656c591006e9b91d139d31c1d6806a4efe44ec3add0d

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a49e86eacc0797a777c6e7c54741612cd2f077e38a00dc9bbc6a79438fd21379
MD5 cdd7d451912698ba2447d8272be170fd
BLAKE2b-256 95ef23ca5e0db5f9978e71bc1237c7082b9980ce9a193d54c0d36e7b5b74bda6

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 189002e882451959e7f7e04603be7ccc9be1a1576a5584c34ec8d6dbce7db0fa
MD5 0f319148ee6be1f2672cbb0cdee7ed88
BLAKE2b-256 b8188a1223cddaf8dd19a5ac167646075ee0c78efd8bdc5ce01748463897dbe6

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: pulselog-0.1.5-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 240.7 kB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 5789c30984d16fd14e96c91e399b9935ab9b2ba4015afe347f8d6de80e6ec17c
MD5 3b599f44636eee4316cfd92491280baa
BLAKE2b-256 620e76e4e17dc31ad5b3511aef80c06034d61475e38dfadb0ca451f1044499ca

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 de6de950aba38441f1eee6121e95b15654f02ccf829f5923755bcc9d904139dc
MD5 45bcc7c4a02e5cada980d1555183e6ed
BLAKE2b-256 556628747619b30b90cc5002bbbe7bf69b4cbedc448052ba41de9f968813d1ed

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 7033b8d9cad993866e53c9703edd288079a12005213b1805148b06ea4daeb2a7
MD5 0dd220e0a3ab5c1feef5a3aba5a1e5b8
BLAKE2b-256 71ee485611effef25ccafdfef8a84cb7395793ff0130438c502b82c6701baa8b

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: pulselog-0.1.5-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 241.7 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 c2d49af49760d40b394af0369bc1c5a535052cf91651feb714c315ec41b664b2
MD5 41b1d4bbc48c8b9ace27c0cd092d0627
BLAKE2b-256 389b830b54a774c34eca421205578241af7ec64dd367d7cdbc6643a69c111e71

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 368c83c090ac36679376bca9fe1d44ea567a27d54dde7f552ee5bb0217d50a5d
MD5 6ac7bbab9dbce2e559dd25265a4f540d
BLAKE2b-256 a9ea87569c00e7a0d21cc028ba9f8035c398d1672345a391b3b94ede6e3f1396

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5b3a61a30dae16561e4d70556a09333ca75292950588d2a982075745f1d0ea17
MD5 c2cd1f4f88d26cad85d2cd3ef4432772
BLAKE2b-256 702dc13407164b88437039c7e534bc9bc9f3ac9e950edee9aa9428afb3076876

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: pulselog-0.1.5-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 243.0 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pulselog-0.1.5-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 f6c82e76d2974563dc47e4d2f0119484152aac082aa735591582dc8e51ea3b05
MD5 e35273d2d7275288a69a104274b48966
BLAKE2b-256 00b19e0dc4e8e55c16096cad145918bfb97e9f7c7bdd3bf85e5c4b80920d9ffe

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 141d0dce517e1dace052a21d800fd9a57e0c1473c6f4aadfa2cd661655f14b7b
MD5 0212fc91a3e03b62dd1c88b07efa3269
BLAKE2b-256 758eaf56ba1d72151bb0f4307c1180cd5dc821863ffb6224d7c55ea084d4b540

See more details on using hashes here.

File details

Details for the file pulselog-0.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pulselog-0.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 90335f101a3ce4d3afa46b25db1afae5ed6e354cde4a00c6378a3279083a7811
MD5 106f177b10c17725eaec356dfc2d531b
BLAKE2b-256 b1405a855e6c098dbab82ca9458bc4a9f98737fe3f9f26b245b55b82751eb599

See more details on using hashes here.

Supported by

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