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.6.tar.gz (37.4 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.6-cp312-cp312-win_amd64.whl (242.7 kB view details)

Uploaded CPython 3.12Windows x86-64

pulselog-0.1.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.8 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

pulselog-0.1.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.0 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

pulselog-0.1.6-cp311-cp311-win_amd64.whl (241.1 kB view details)

Uploaded CPython 3.11Windows x86-64

pulselog-0.1.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.6 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

pulselog-0.1.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (385.8 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

pulselog-0.1.6-cp310-cp310-win_amd64.whl (241.4 kB view details)

Uploaded CPython 3.10Windows x86-64

pulselog-0.1.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.7 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

pulselog-0.1.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (386.0 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

pulselog-0.1.6-cp39-cp39-win_amd64.whl (242.4 kB view details)

Uploaded CPython 3.9Windows x86-64

pulselog-0.1.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (388.3 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

pulselog-0.1.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (386.4 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

pulselog-0.1.6-cp38-cp38-win_amd64.whl (243.7 kB view details)

Uploaded CPython 3.8Windows x86-64

pulselog-0.1.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (387.3 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

pulselog-0.1.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (386.0 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

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

File metadata

  • Download URL: pulselog-0.1.6.tar.gz
  • Upload date:
  • Size: 37.4 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.6.tar.gz
Algorithm Hash digest
SHA256 279e97c6f8d4f3bbb277c2ca564c6ad279ce7a3bb393407da3d28f9dac0050ea
MD5 2459491c1a3b11333876277e0eea7d4d
BLAKE2b-256 f0dd5e75dd7293dc48bb99280e2405f012620175c717af3a963eff961dcc96d2

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pulselog-0.1.6-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 242.7 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.6-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 e010eb2a563f51545a058c588c8318c46fbe52076d5a07b764bdb4814e201ff6
MD5 7d40497cbd286ab1d5f8a6eabfb12ca0
BLAKE2b-256 5a3baaac5c087f9cec67c57ac53a0dace536280a70f0f4a17dbe72f64057dba3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9d638201ed05bb16d746768ecd2a5e413ddf2addb8e2f560ea07acb24facc8eb
MD5 1266e948c96153b213ecea83a2c9873b
BLAKE2b-256 06a6d64c697e794888d292ae5e5a5633fdddfff94ff71a4b1c86254aef3580a7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 96fdf0d5e43bee574a624828e7a7427b90dd98f66ed48fcf1a89b63c2f2dc1b9
MD5 3eb03f09fb56745fb6e6ac97790ad1cb
BLAKE2b-256 5e29f50f3b9e8226f40b1f57713e9ef5057ef6f7ed0eb4adea1af520d8a17409

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pulselog-0.1.6-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 241.1 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.6-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 6724c7d696c23bc3a067072285d8701cc5a99fcb780e8bf49a913ea7481f8b67
MD5 a44ad6afab8169b0b106c16a6759ec6c
BLAKE2b-256 d3ed8d803ab0fa1280e212ed8f89c64daccbe64c910a112d8016af1b4d2fc77b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 584318a61df9302b4b4278e704772380b2b1f62ee3fe78a703e2435b687b9571
MD5 84705c51a4692ae4901df3e302a3cfbe
BLAKE2b-256 82dff03c6d2026936a084b0b651bd0587aac8b16816c552c61af50450c44b717

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3ba2551c9f791963051718d5cd2ab163e113afff943e6fa3ba12216301808667
MD5 2b7c769aa7da6c1ae5b223ca150f6b18
BLAKE2b-256 28fdc53ac07ef2b11c2f87a4527699a3276bdfbcd99eb0cb4f8cc4c862777ff2

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pulselog-0.1.6-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 241.4 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.6-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 c3c39151cb49cefe7fcf8027cf24e91fccaad9dc765439fb8c409d74166d4952
MD5 af2d6f0d0768cc90890236b543c65b76
BLAKE2b-256 49f007e3d4c0f56be1ce2e6e02b5e6bf76bf4c8bd21b2c0e94a090217dc75558

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0ccdb87c02bc8308cebc7a4248db14d81278d8529a2ec8189722eca3a8f3dc26
MD5 8625be6e50d27ac004aad6aac8b86bba
BLAKE2b-256 8b93f3e56dd7e2860d55cefa45d8583b4d0f33f0d2d2181acc68ceaa2cbbb54d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6a76ce9cfae66fb5f16527b98d7dde22978ac25d571e5b95b0c20ad6e287dec5
MD5 fb1b427039bc6611b9b74399aa172c52
BLAKE2b-256 e5e9fcb19605e93ecf172e47f0072c1fdc611c7ee3e21d20bf3ee97def407202

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pulselog-0.1.6-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 242.4 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.6-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 75525074fb2a4f6a75b703eb1dacd47e9e6ca48fbfc802ac796eacc046123588
MD5 1c5c78afa144af55123e5148118d0509
BLAKE2b-256 6700a09597b79998bd7b3d6b8b3ae860388240bae4c8f8422acdc7602fc28493

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d57fe0e0380977af9674309726d659033d1ba6ebfbd07dce6c91529a0ff937a2
MD5 da60a1f5baa3987845761fd5e739fa4c
BLAKE2b-256 1faed4c33cc57644236ec80322837f51a9bf56266921580335dd70553978c849

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5722edb5292229e0f53a40d530ad7c47aefcccde1713db218b0fce2acace3176
MD5 02faf28afe72c1cb3071e7e04fad17e1
BLAKE2b-256 a5165fd72802cfcf03c6d32f9bfa2476b28c75e828a4fb5a811150b17406349c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: pulselog-0.1.6-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 243.7 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.6-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 ff4f5ee403614d9e43caa3ce8561578ecad00451e98ab7687430b21a37d6fbab
MD5 511d92edb48838f9fff32712528093f5
BLAKE2b-256 372ff9e4df36d691999bd2d0c08ff1db43d49d06e9e0f0ce4bc7da512de0e2d8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 88f36d42e705faa636f8be0c90fa0a7a655c854e09f147dadf96c6a4f799e292
MD5 5d2d430c8c70466d1697185f53480eac
BLAKE2b-256 7c5c0114edc87fcc56bc220619ed352acf8a2d0e0df75add50221b7dfea60a74

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pulselog-0.1.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1bde51998ce35c4a4803a500ade28821704870cbe655cecaee92e0075e9ed619
MD5 97b4d6cf43d20fb53e878b57c968e608
BLAKE2b-256 1930ef17ef221a710f6a7e88aa221774408e8cf6b5348c0c03408c2e913cb577

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