Skip to main content

One CLI. Zero code changes. Full system flow visibility for any Python service or distributed system.

Project description

pylow

One CLI. Zero code changes. Full system flow visibility for any Python service or distributed system.

PyPI version Python Docker License: MIT

Version 0.1.14118 MCP tools + 118 CLI commands across kernel tracing, Python internals, JSON analysis, HTTP tooling, protocol diagnostics, pipeline patterns, distributed workflow orchestration, and MCP server.


Installation & Setup

pip install pylow

[!IMPORTANT] If you get Command 'pylow' not found after installation, your Python user bin directory is not on PATH. Fix it:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

MCP Server

pylow ships a fully-featured Model Context Protocol server — every CLI command is available as an MCP tool, callable directly from Claude Desktop, Cursor, or any MCP-compatible client.

Run locally (stdio — Claude Desktop)

pip install pylow
pylow-mcp --stdio

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "pylow": {
      "command": "pylow-mcp",
      "args": ["--stdio"]
    }
  }
}

Run via Docker (Streamable-HTTP — MCP Inspector / remote clients)

# Pull and run
docker run -p 8765:8765 chiefj/pylow-mcp:latest

# Full stack: MCP server + Inspector UI + Jaeger + Prometheus
docker compose up

# Then open MCP Inspector at http://localhost:6274

Add to MCP client config (HTTP transport):

{
  "mcpServers": {
    "pylow": {
      "url": "http://localhost:8765/mcp"
    }
  }
}

[!NOTE] The MCP server registers 113 tools — one per pylow command. All tools return the same plain-text diagnostic output the CLI prints. Pass pid=0 for tools that use internal mock data and don't need a live process.


Docker

# Pull the image
docker pull chiefj/pylow-mcp:latest

# Run standalone MCP server
docker run --rm -p 8765:8765 chiefj/pylow-mcp:latest

# Run with stdio transport (pipe into Claude)
docker run --rm -i chiefj/pylow-mcp:latest pylow-mcp --stdio

# Full observability stack (MCP + Inspector + Jaeger + Prometheus)
docker compose up --build

# Include optional webhook receiver
docker compose --profile webhook up

Ports exposed by docker-compose:

Service Port URL
pylow MCP server 8765 http://localhost:8765/mcp
MCP Inspector UI 6274 http://localhost:6274
Jaeger trace UI 16686 http://localhost:16686
Prometheus 9090 http://localhost:9090
Webhook receiver 9000 http://localhost:9000

Image: chiefj/pylow-mcp — multi-stage build on python:3.10-slim. Includes curl, jq, tcpdump, openssl, iproute2 for the diagnostic sub-process pipelines. Non-root pylow user.


How It Works

Three layers working together:

Layer 1 → OTel auto-instrumentation  (HTTP, DB, gRPC, queues — zero code changes)
Layer 2 → bpftrace USDT              (Python function call tree, syscalls, kernel)
Layer 3 → pylow CLI                  (stitches both, renders the flow)

Quick Start

# Find the PID of your process first
pgrep -f "your_app.py"

# Attach and start collecting
pylow attach <PID>

# Render the execution flow
pylow flow --last

# 10-second triage — tells you exactly what category problem you have
pylow triage <PID>

Complete Command Reference

All 113 commands, grouped by category. Every command is also available as an MCP tool via pylow-mcp.


CATEGORY 1 — Core Tracing

pylow attach <PID>

Attach to any running Python process and start collecting trace logs.

pylow attach 4821
✓ Attached to process 4821. Monitoring execution events...

pylow flow [--last]

Render the full execution flow tree from collected spans.

pylow flow --last
handle_request 450ms
├── authenticate_user 20ms
│   └── [redis GET session_id] 15ms
└── call_llm_chain 410ms
    └── [POST api.openai.com/v1/chat/completions] 400ms
        └── waiting (epoll_wait) 390ms   ← bottleneck

pylow stitch --services <list>

Stitch distributed traces together across service boundaries.

pylow stitch --services api,worker,ml-service
REQUEST trace-id: t_demo_flow_123

  api-gateway     450ms  handle_request
  └── api-gateway  20ms  authenticate_user
      └── api-gateway  15ms  redis GET session_id

pylow slow [--threshold <ms>] [--watch]

Surface slow execution paths exceeding a latency threshold.

pylow slow --threshold 200ms --watch
Continuous monitoring daemon started. Threshold: 200ms

SLOW PATHS detected (last 5 min):
  #1  handle_request → call_llm_chain → [waiting (epoll_wait)]
      avg: 390ms  occurrences: 1
      root cause: epoll_wait 310ms — network latency

pylow diff --before <id> --after <id>

Compare execution flow metrics between two releases to detect regressions.

pylow diff --before deploy-v1.2 --after deploy-v1.3
REGRESSIONS:
  handle_request  +150ms avg  (was 200ms, now 350ms)
  serialize        +12ms avg  (was 3ms, now 15ms)

NEW CALLS in v1.3:
  validate_schema  8ms

REMOVED in v1.3:
  legacy_cache_check

CATEGORY 2 — Kernel & Syscall Tracing

pylow syscall <PID>

Trace syscall counts and histogram latency.

pylow syscall 4821
--- BPF Map: @sys_counts ---
  sys_enter_read: 231
  sys_enter_write: 184
  sys_enter_epoll_wait: 42

pylow malloc <PID>

Profile memory allocation sizes and callers.

pylow malloc 4821
--- BPF Map: @alloc_sizes (bytes) ---
[64, 127]              45 |@@@@@@@@@@@                         |
[512, 1023]           120 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[4096, 8191]           18 |@@@@                                |

pylow tcp <PID>

Trace outbound TCP sendmsg latency.

pylow tcp 4821
--- BPF Map: @tcp_send_us (ns delay) ---
[100000, 200000]       21 |@@@@@@@@@@                          |

pylow io <PID>

Trace File and Block I/O read/write latencies.

pylow io 4821
--- BPF Map: @read_lat (ns) ---
[4096, 8191]          150 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[16384, 32767]         23 |@@@@@                               |

pylow flame <PID> [--duration <sec>]

Generate CPU sampling flame graphs (SVG).

pylow flame 4821 --duration 5
✓ Sampling for 5s...
✓ Saved flame graph to flamegraph.svg

pylow sched <PID>

Trace runqueue scheduler latency.

pylow sched 4821
--- BPF Map: @runq_latency_us ---
[1, 2]                 98 |@@@@@@@@@@@@@@@@@@@@@@@@            |
[4, 8]                142 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

pylow page-faults <PID>

Trace page fault hotspots — which code is triggering cold memory access.

pylow page-faults 4821
=== PAGE FAULT HOTSPOTS ===
@faults[
    malloc+0x24
    PyBytes_FromStringAndSize+0x18
    load_dataset @ ml/data.py:12
]: 421

pylow context-switches <PID>

Profile preemption and voluntary context switches.

pylow context-switches 4821
OFF CPU 87ms next_cpu=2

--- BPF Map: @off_cpu_ms ---
[0, 1]                12 |@@@@                                |
[64, 128]            210 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

pylow kernel-blocked <PID>

Trace the exact kernel call path where the process is sleeping uninterruptibly.

pylow kernel-blocked 4821
BLOCKED IN KERNEL:
    futex_wait+0x120
    take_gil+0x42
    execute_query+0x91  db.py:102

pylow tlb-shootdowns <PID>

Profile TLB flush rates and reasons.

pylow tlb-shootdowns 4821
--- BPF Map: @tlb_reason ---
[0] (TLB_FLUSH_ON_TASK_SWITCH)    42
[1] (TLB_FLUSH_ON_PAGE_FAULT)    187

pylow irq-impact <PID>

Monitor soft and hard IRQ CPU cycles stolen from the process.

pylow irq-impact 4821
--- BPF Map: @sirq_type ---
[3] (NET_RX_SOFTIRQ)    187

--- BPF Map: @sirq_lat (us) ---
[8, 16]              187 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

CATEGORY 3 — Python Runtime Tracing

pylow pycall <PID>

Profile Python PyObject_Call function execution timings.

pylow pycall 4821
SLOW execute_query took 12400us
SLOW process_job took 1850us

pylow pyframe <PID>

Trace exact Python frames: file, function name, line number.

pylow pyframe 4821
ENTER execute_query() @ db/models.py:142
slow_query() @ db/models.py:142 — p99: 340ms

pylow pycpu <PID>

Identify CPU hotspots in Python stacks.

pylow pycpu 4821
--- BPF Map: @stacks ---
  → Resolved: call_llm_chain @ gateway/orchestrator.py:120  (145 samples)

pylow pyexcept <PID>

Trace raised Python exceptions with user stack.

pylow pyexcept 4821
EXCEPTION KeyError @ tid=10234
  get_user_context @ db/client.py:48

pylow pyiowait <PID>

Trace Python code blocking on I/O.

pylow pyiowait 4821
BLOCKING READ 12ms
  fetch_metadata @ db/client.py:54

pylow pygil <PID>

Profile GIL acquisition delays and thread contention.

pylow pygil 4821
GIL WAIT 1250us tid=10234
  calculate_features @ ml/engine.py:89

pylow pyleak <PID>

Profile heap allocations to detect memory leak patterns.

pylow pyleak 4821
=== TOP ALLOCATORS ===
@allocs[0x7f3b821034bc]: 10485760 bytes
  → load_dataset @ ml/data.py:12

pylow pyreq <PID>

Measure end-to-end request lifecycle breakdown.

pylow pyreq 4821
REQ START tid=10234
REQ DONE total=340ms db=310ms other=30ms

pylow timeline <PID> [--duration <sec>] [--threshold <ms>]

Render a chronological timeline call graph with entry/exit timestamps.

pylow timeline 4821 --duration 5.0 --threshold 2.0
[     0.000ms] → handle_request()  server.py:45
[     0.040ms]   → parse_headers()  http.py:12
[     0.051ms]   ← parse_headers()  [0.011ms]
[    91.230ms]   ← execute_query()  [91.175ms]  ⚠️ SLOW

pylow pythread <PID>

Trace thread-aware function call timelines with self-time per thread.

pylow pythread 4821
--- Thread ID: 10001 ---
  parse_headers() spent 0.00ms (Self time: 0.00ms)

pylow pyasync <PID>

Trace async coroutine suspend/resume metrics.

pylow pyasync 4821
--- Coroutine: 0x7f3b821034bc ---
  Suspended counts: 2
  Total CPU Time: 80us

pylow pyargs <PID>

Profile Python function call argument types and layout (CPython struct dereference).

pylow pyargs 4821
1000 CALL obj=0x7f3b821034bc args=0x7f3b821051fa

pylow pysyscall <PID>

Profile syscalls attributed directly to Python frames.

pylow pysyscall 4821
=== SLOW READ fd=4 dur=12ms ===
  fetch_metadata @ db/client.py:54

pylow pynplus1 <PID>

Detect N+1 query loop patterns from ORM calls.

pylow pynplus1 4821
⚠️  N+1 CANDIDATE: db/models.py:142
   Called 15x in 5s (3.0/s)

pylow pygraph <PID>

Trace hierarchical call relationships as a call graph.

pylow pygraph 4821
handle_request()  calls=1  avg=0.00ms  (server.py:45)
  execute_query()  calls=1  avg=0.00ms  (db.py:88)

pylow pyanomaly <PID>

Identify statistically anomalous slow function calls vs baseline.

pylow pyanomaly 4821
[BASELINE] execute_query(): mean=10.33ms stddev=0.76ms
🚨 ANOMALY execute_query(): 45.00ms vs baseline 10.33ms

pylow pydash <PID>

Stream live function call metrics to a curses dashboard.

pylow pydash 4821
=== LIVE FUNCTION TRACER ===
RECENT CALLS:
  handle_request() 120.40ms

pylow pysingle <PID> <func>

Trace a single function's complete execution tree with self-time.

pylow pysingle 4821 handle_request
[     0.000ms] → handle_request()  server.py:45
[     0.011ms]   → validate_token()  auth.py:12
[     0.015ms]   ← validate_token()  total=0.004ms  self=0.003ms

CATEGORY 4 — Diagnostic Workflows

pylow triage <PID>

Run a 10-second triage profile. Tells you exactly which problem category to investigate.

pylow triage 4821
cpu_samples : 842    ← high → CPU BOUND
off_cpu     : 12
syscalls    : 234
page_faults : 8

→ DIAGNOSIS: CPU BOUND — use pylow cpu-bound 4821

pylow cpu-bound <PID>

Full CPU-bound diagnostic: hottest stacks + duration attribution.

pylow cpu-bound 4821
CPU HOTSPOT: call_llm_chain @ gateway/orchestrator.py:120
  Samples: 145 / 200 total (72.5%)
  Avg duration: 1240ms

pylow io-bound <PID>

Full I/O-bound diagnostic: blocking syscall + Python origin stack.

pylow io-bound 4821
BLOCKED syscall=read 87ms
  fetch_metadata @ db/client.py:54
  → fd=7 → /var/run/postgresql/.s.PGSQL.5432

pylow syscall-storm <PID> [--id <syscall_id>]

Diagnose high-frequency syscall storm. Optionally filter to a specific syscall ID.

pylow syscall-storm 4821
pylow syscall-storm 4821 --id 1   # filter to sys_write only
TOP SYSCALLS (5s):
  id=1 (write)   3841 calls
  id=0 (read)    1204 calls
  id=202 (futex)  892 calls

pylow deadlock <PID>

Detect deadlocks: locks acquired but never released within the observation window.

pylow deadlock 4821
=== LOCKS HELD > 5s ===
@lock[tid=10234, lock=0x7f3b820f1234]: held since 8234ms
  → PyThread_acquire_lock @ db/connection_pool.py:88

pylow service-map <PID>

Map inbound and outbound request flow: connections, bytes in/out.

pylow service-map 4821
=== SERVICE pid=4821 ===
outbound_calls : 14
bytes_in       : 204800
bytes_out      : 81920
conn_time_ms:
  [10, 20]     8 |@@@@@@@@@@@@                        |

pylow ordered-log <PID> [--filter-internals]

Output a chronological ordered log of every Python function call and return.

pylow ordered-log 4821
pylow ordered-log 4821 --filter-internals   # strip CPython bootstrap noise
1749558821000001  ENTER  handle_request          server.py:45
1749558821000040  ENTER  authenticate_user        auth.py:12
1749558821000055  EXIT   authenticate_user
1749558821000060  ENTER  execute_query            db.py:88
1749558821091000  EXIT   execute_query            91ms ⚠️ SLOW

pylow intercept <PID> [func]

Intercept entry/exit payloads at a boundary function. Shows file, line, tid, stack.

pylow intercept 4821 process_payment
=== process_payment CALLED ===
file : payments/service.py
line : 142
tid  : 10234
time : 1749558821000060

=== process_payment RETURNED ===
duration: 88ms

pylow anomaly-trigger <PID> [func]

Arm a trigger on a function — capture full call tree only when an exception or anomaly fires.

pylow anomaly-trigger 4821 validate_payment
[WATCHING] validate_payment — armed, zero noise until trigger

!!! EXCEPTION DURING PAYMENT !!!
type  : ValueError
after : 23ms
  → process_refund @ payments/service.py:89
  → validate_payment @ payments/service.py:34

pylow correlation <PID> [service_name]

Trace cross-service chronological correlation. Stamp every call with service name + nanosecond timestamp.

pylow correlation 4821 api-gateway
# Run simultaneously on all service PIDs, then merge:
# cat /tmp/trace_*.log | sort -k2 -t=
SVC=api-gateway TS=1749558821000001 ENTER handle_request server.py:45
SVC=api-gateway TS=1749558821000040 CONNECT
SVC=api-gateway TS=1749558821000060 EXIT  handle_request

CATEGORY 5 — HTTP & API Tooling

pylow curl-perf <PID> [url]

Trace HTTP request timing breakdown: DNS, connect, TTFB, total.

pylow curl-perf 4821 https://api.example.com/payments
=== HTTP PERFORMANCE: https://api.example.com/payments ===
  time_namelookup  :   0.012s
  time_connect     :   0.031s
  time_starttransfer:  0.087s
  time_total       :   0.091s
  http_code        :   200
  size_download    :   4096 bytes

pylow jwt-decode <PID>

Intercept and decode JWT Authorization tokens inline from HTTP headers.

pylow jwt-decode 4821
=== JWT DECODED ===
Header : {"alg":"RS256","typ":"JWT"}
Payload: {"sub":"user_9821","exp":1749645221,"roles":["admin"]}
Expiry : 2026-06-12T06:00:21Z (valid)

pylow cert-check <PID> [domain]

Trace SSL/TLS handshake and certificate expiry for a domain.

pylow cert-check 4821 api.example.com
=== TLS CERTIFICATE: api.example.com ===
  Subject : CN=api.example.com
  Issuer  : Let's Encrypt Authority X3
  Expires : 2026-09-01T00:00:00Z
  Days left: 82
  Status  : ✓ VALID

pylow rate-limit-test <PID>

Diagnose API rate limit backoff — detect HTTP 429 responses and retry delays.

pylow rate-limit-test 4821
HTTP 429 received — Retry-After: 60s
Backoff attempt 1/3 — waiting 60s
Backoff attempt 2/3 — waiting 120s
Total blocked time: 180s

pylow parallel-fetch <PID> [--concurrency <n>]

Trace parallel request pipelines and concurrent connection activity.

pylow parallel-fetch 4821 --concurrency 8
Active parallel workers: 8
Fetched 10 payment details in parallel (duration: 120ms)
Successfully processed items: 10/10

CATEGORY 6 — Shell Pipeline Patterns

pylow jq-search <PID> [query]

Search JSON stream for field names or patterns.

pylow jq-search 4821 error
Searching stream for "error"...
  .actStatus.name = "ERROR"
  .response.errorCode = "PAYMENT_FAILED"

pylow awk-stats <PID>

Compute statistics (count, sum, avg, min, max, percentages) on tabular data streams.

pylow awk-stats 4821
=== STREAM STATISTICS ===
  count : 1042
  sum   : 94821ms
  avg   : 91.0ms
  min   : 12ms
  max   : 3421ms
  p95   : 340ms

pylow tee-branch <PID>

Monitor output stream branching — writes to multiple sinks simultaneously.

pylow tee-branch 4821
Stream branching active:
  → stdout     (live display)
  → /tmp/response.log  (file capture)
  Bytes written: 40960

pylow pipe-decouple <PID> [fifo_path]

Monitor named pipe FIFO decoupler. Shows producer/consumer throughput.

pylow pipe-decouple 4821 /tmp/payment_pipe
Named pipe: /tmp/payment_pipe
  Producer writes: 412
  Consumer reads : 409
  Buffer lag     : 3 items

pylow sed-mask <PID>

Monitor stream for PII masking rules applied by sed filters.

pylow sed-mask 4821
=== MASKING ACTIVE ===
  Pattern: [0-9]{16}       → CARD-XXXX-XXXX-XXXX-XXXX
  Pattern: \b[\w.]+@[\w.]+\b → EMAIL-MASKED
  Lines processed: 1042
  Patterns matched: 87

CATEGORY 7 — JSON Response Analysis

All jq-* commands analyze live JSON responses from the traced process. Pass a pid to attach.


pylow jq-schema <PID>

Discover the full recursive type schema of a JSON response (shape only, no values).

pylow jq-schema 4821
{
  "id": "number",
  "actStatus": { "id": "number", "name": "string" },
  "refLinkTo": { "id": "number", "payModes": "null" }
}

pylow jq-nulls <PID>

List all null fields as a flat array of dotted key paths.

pylow jq-nulls 4821
["newconconno", "newconleadsid", "reminderdate", "actStatus.salesActivities"]

pylow jq-null-paths <PID>

Same as jq-nulls with full dotted path for nested nulls.

pylow jq-null-paths 4821
actStatus.salesActivities = null
refLinkTo.payModes = null
reminderdate = null

pylow jq-locate-key <PID> [key]

Find all paths in the document tree that contain a given key name.

pylow jq-locate-key 4821 salesActivities
Paths containing "salesActivities":
  .actStatus.salesActivities
  .refLinkTo.actStatus.salesActivities

pylow jq-key-path <PID> [key]

Find all paths and their values for a given key name.

pylow jq-key-path 4821 name
.actStatus.name = "COMPLETED"
.refLinkTo.name = "PAYMENT_MODE_CASH"

pylow jq-all-keys <PID>

Discover the full vocabulary of unique keys across the entire document.

pylow jq-all-keys 4821
All unique keys (32):
  id, enddate, actStatus, name, salesActivities, refLinkTo,
  payModes, reminderdate, newconconno, ...

pylow jq-leaf-paths <PID> [--filter-val <term>]

List all leaf (non-object) paths with their values. Optionally filter.

pylow jq-leaf-paths 4821
pylow jq-leaf-paths 4821 --filter-val COMPLETED
.id = 28920212268
.enddate = "2026-06-10"
.actStatus.name = "COMPLETED"

pylow jq-clean-nulls <PID>

Output the response with all null fields removed.

pylow jq-clean-nulls 4821
{
  "id": 28920212268,
  "enddate": "2026-06-10",
  "actStatus": { "id": 1, "name": "COMPLETED" }
}

pylow jq-depth-map <PID>

Calculate nesting depth per key branch across the entire document.

pylow jq-depth-map 4821
Root depth: 1
  actStatus: depth=2
    salesActivities: depth=3
  refLinkTo: depth=2
    payModes: depth=3

pylow jq-type-map <PID>

Map every leaf path to its resolved data type.

pylow jq-type-map 4821
.id              → number
.enddate         → string
.actStatus.name  → string
.refLinkTo.payModes → null

pylow jq-find-value <PID> [value]

Find the exact path where a specific value occurs.

pylow jq-find-value 4821 gravity_admin
Value "gravity_admin" found at:
  .createdBy.username
  .modifiedBy.username

pylow jq-structural-diff <PID>

Compare two response snapshots and show added/removed/changed fields.

pylow jq-structural-diff 4821
STRUCTURAL DIFF:
  ADDED   : .actStatus.resolvedAt
  REMOVED : .legacyFlag
  CHANGED : .actStatus.name  (PENDING → COMPLETED)

pylow jq-extract-subtree <PID> [key]

Surgically extract a subtree from the document by key name.

pylow jq-extract-subtree 4821 appModulesId
{
  "appModulesId": {
    "id": 5,
    "name": "PAYMENT_MODULE",
    "active": true
  }
}

pylow jq-summary <PID>

Print response statistics: total keys, null count, max depth, array sizes.

pylow jq-summary 4821
=== RESPONSE SUMMARY ===
  Total keys   : 47
  Null fields  : 12  (25.5%)
  Max depth    : 4
  Arrays       : 3
  Largest array: salesActivities (0 items — empty)

pylow jq-validate-schema <PID> [schema_file]

Validate the response structure against a JSON schema template file.

pylow jq-validate-schema 4821 schema.json
Schema validation against schema.json:
  ✓ id            present
  ✓ actStatus     present
  ✗ paymentRef    MISSING
  Result: INVALID — 1 field missing

pylow jq-array-schema <PID>

Inspect all arrays: their sizes and the schema of their items.

pylow jq-array-schema 4821
.salesActivities  : 0 items  (empty)
.paymentHistory   : 3 items  → {id: number, date: string, amount: number}
.tags             : 5 items  → string

pylow jq-null-pct <PID>

Calculate the percentage of null fields relative to total fields.

pylow jq-null-pct 4821
Null field percentage: 25.5%
  Total fields : 47
  Null fields  : 12
  Filled fields: 35

pylow jq-non-null-leaves <PID>

List only the fields that have actual non-null values.

pylow jq-non-null-leaves 4821
Non-null leaves (35):
  .id = 28920212268
  .enddate = "2026-06-10"
  .actStatus.name = "COMPLETED"
  ...

pylow jq-parent-context <PID> [key]

Show a key along with its parent object and sibling fields for context.

pylow jq-parent-context 4821 salesActivities
Key: salesActivities
Parent: actStatus
Siblings:
  id   = 1
  name = "COMPLETED"
  salesActivities = null  ← target

pylow jq-locate-value-contains <PID> [partial]

Find all paths where a value contains a partial string match.

pylow jq-locate-value-contains 4821 Kernel
Paths with values containing "Kernel":
  .modules[2].name = "Kernel Payment Module"
  .description = "Kernel-level trace event"

pylow jq-trace-all-keys <PID> [key]

Find every occurrence of a key across all nested objects and arrays.

pylow jq-trace-all-keys 4821 username
All occurrences of "username":
  .createdBy.username = "gravity_admin"
  .modifiedBy.username = "system"
  .audit[0].username = "gravity_admin"

pylow jq-heavy-objects <PID>

Identify the heaviest nested objects by field count.

pylow jq-heavy-objects 4821
=== HEAVIEST OBJECTS ===
  .actStatus          : 8 fields
  .refLinkTo.payModes : 6 fields
  .createdBy          : 5 fields

pylow jq-repeated-schema <PID>

Find repeated structural patterns (e.g. audit trails, references) across the document.

pylow jq-repeated-schema 4821
Repeated schemas detected:
  Pattern {id, name} appears 6x at:
    .actStatus, .refLinkTo, .createdBy, ...

pylow jq-common-audit <PID>

Find nested objects that share the same audit trail key set (createdBy, modifiedBy, etc).

pylow jq-common-audit 4821
=== AUDIT TRAIL ANALYSIS ===
Objects with common audit keys:
  .actStatus     → createdBy, modifiedBy, createdAt
  .refLinkTo     → createdBy, modifiedBy

pylow jq-schema-evolution <PID>

Compare schema across paginated response pages to detect structural drift.

pylow jq-schema-evolution 4821
=== SCHEMA EVOLUTION (page 1 → page 2) ===
  ADDED   : .newField
  REMOVED : .deprecatedFlag
  STABLE  : 44 fields unchanged

pylow jq-validate-fields <PID>

Assert that mandatory fields are present and non-null.

pylow jq-validate-fields 4821
=== FIELD VALIDATION ===
  ✓ id           present
  ✓ actStatus    present
  ✗ paymentRef   MISSING
  ✗ enddate      NULL
  Result: 2 violations

pylow jq-watch-changes <PID>

Snapshot responses over time and track which fields change between calls.

pylow jq-watch-changes 4821
=== Snapshot 1 → Snapshot 2 ===
  CHANGED: .actStatus.name  PENDING → COMPLETED
  CHANGED: .modifiedAt      2026-06-10T10:00Z → 2026-06-10T10:05Z
  STABLE : 45 fields

CATEGORY 8 — DAG Execution Engine

Run arbitrary step graphs with proper dependency resolution, cycle detection, and level-by-level parallel execution.

DAG Spec Format

"step_name:dep1,dep2  step2:dep1  step3:"
  • Each node: name:comma-separated-deps
  • No deps: auth: or just auth
  • Spaces separate nodes

pylow dag-dry-run --dag "<spec>" [--dag-file <path>] [--workers <n>]

Validate DAG — resolve levels, detect cycles. No execution.

pylow dag-dry-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog"
=== DAG EXECUTION ENGINE ===
  Steps     : 4
  Levels    : 3
  Workers   : 4
  Mode      : DRY RUN

  Level 0  [auth]
  Level 1  [get_user, get_catalog]
  Level 2  [create_order]

✓ DAG is valid. No cycles. Dry-run complete.

Cycle detection:

pylow dag-dry-run --dag "a:b b:a"
# [dag-engine] CYCLE DETECTED — aborting.

pylow dag-run --dag "<spec>" [--dag-file <path>] [--workers <n>]

Execute the DAG. Runs each level in parallel (up to --workers at a time). Stops at first failed level.

pylow dag-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog" --workers 4
=== DAG EXECUTION ENGINE ===
  Steps     : 4
  Levels    : 3
  Workers   : 4
  Mode      : EXECUTE

  Level 0  [auth]
  Level 1  [get_user, get_catalog]
  Level 2  [create_order]

▶ Level 0: ['auth']
  ✓ auth                  51.2ms

▶ Level 1: ['get_user', 'get_catalog']
  ✓ get_user              50.8ms
  ✓ get_catalog           51.1ms

▶ Level 2: ['create_order']
  ✓ create_order          50.9ms

✓ All 4 steps completed in 153.8ms

Load from a JSON file:

# dag.json
{
  "auth": [],
  "get_user": ["auth"],
  "get_catalog": ["auth"],
  "create_order": ["get_user", "get_catalog"]
}

pylow dag-run --dag-file dag.json --workers 8

pylow dag-status

Show the step-level status of the last DAG run in this session.

pylow dag-status
=== DAG STATUS ===
  DONE              auth
  DONE              get_user
  DONE              get_catalog
  DONE              create_order

CATEGORY 9 — Saga Orchestrator

Forward/compensate pattern. Steps run in order. On failure, completed steps are rolled back in reverse. Every event is appended to a JSON Lines saga log.

pylow saga-run --steps <list> [--fail-at <step>] [--log-file <path>]

Run forward steps. On failure, compensating transactions execute in reverse for all completed steps.

# Happy path — all steps commit
pylow saga-run --steps auth,create_order,reserve_inventory,create_payment
=== SAGA ORCHESTRATOR [saga-1749558821] ===
  Steps    : auth, create_order, reserve_inventory, create_payment
  Log file : /tmp/pylow_saga.log
  Inject   : none

  ▶ Forward  auth                  ✓
  ▶ Forward  create_order          ✓
  ▶ Forward  reserve_inventory     ✓
  ▶ Forward  create_payment        ✓

✓ Saga committed. All 4 steps succeeded.
  Log: /tmp/pylow_saga.log
# Inject failure to test rollback
pylow saga-run --steps auth,create_order,reserve_inventory --fail-at create_order
=== SAGA ORCHESTRATOR [saga-1749558822] ===
  Steps    : auth, create_order, reserve_inventory
  Inject   : create_order

  ▶ Forward  auth                  ✓
  ▶ Forward  create_order          ✗  FAILED

  Rolling back completed steps...
  ↩ Compensate auth               ✓

✗ Saga rolled back. Failed at: create_order
  Log: /tmp/pylow_saga.log

Custom log file:

pylow saga-run --steps auth,create_order --log-file /var/log/my_saga.log

pylow saga-log [--log-file <path>]

Display the saga event log with color-coded events.

pylow saga-log
pylow saga-log --log-file /var/log/my_saga.log
=== SAGA LOG: /tmp/pylow_saga.log ===

  2026-06-11T06:09:01Z  SAGA_START          — steps=['auth', 'create_order', ...]
  2026-06-11T06:09:01Z  FORWARD_START       [auth]
  2026-06-11T06:09:01Z  FORWARD_OK          [auth]
  2026-06-11T06:09:01Z  FORWARD_START       [create_order]
  2026-06-11T06:09:01Z  FORWARD_OK          [create_order]
  2026-06-11T06:09:02Z  SAGA_COMMITTED      — all_steps=['auth', 'create_order']

Event color key:

Event Color
SAGA_START / SAGA_COMMITTED Cyan / Green
FORWARD_START Yellow
FORWARD_OK Green
FORWARD_FAIL Red
COMPENSATE_START / COMPENSATE_OK Magenta / Green
SAGA_ROLLED_BACK Red

pylow saga-replay [--log-file <path>]

Re-run the steps from a committed saga log (no injected failures).

pylow saga-replay
pylow saga-replay --log-file /var/log/my_saga.log
[saga-replay] Replaying 4 steps from /tmp/pylow_saga.log

=== SAGA ORCHESTRATOR [saga-1749558900] ===
  ▶ Forward  auth                  ✓
  ▶ Forward  create_order          ✓
  ▶ Forward  reserve_inventory     ✓
  ▶ Forward  create_payment        ✓

✓ Saga committed. All 4 steps succeeded.
  Log: /tmp/pylow_saga.log.replay

Diagnostic Decision Tree

Run pylow triage <PID> first. Read the output and go to the right command:

cpu_samples high + offcpu low   → pylow cpu-bound <PID>
cpu_samples low  + offcpu high  → pylow io-bound <PID>
syscalls very high              → pylow syscall-storm <PID>
page_faults high                → pylow page-faults <PID>
all counts low                  → pylow deadlock <PID>

Full Drill-Down by Symptom

Symptom First Command Drill-Down
App is slow triage cpu-bound or io-bound
Memory growing malloc pyleak
Random crashes pyexcept anomaly-trigger
Process hangs deadlock kernel-blocked
Too many syscalls syscall-storm syscall
Wrong output / logic ordered-log interceptjq-structural-diff
Multi-service slowdown service-map correlation
Unknown JSON schema jq-schema jq-nullsjq-validate-fields
API HTTP issue curl-perf jwt-decode or cert-check
Distributed workflow dag-dry-run dag-run
Rollback on failure saga-run saga-log

bpftrace Prerequisites

The kernel tracing commands (syscall, malloc, tcp, io, flame, sched, page-faults, context-switches, kernel-blocked, tlb-shootdowns, irq-impact, pycall, pyframe, pycpu, pyexcept, pyiowait, pygil, pyleak, pyreq, pythread, pyasync, pyargs, pysyscall, pynplus1, pygraph, pyanomaly, pydash, pysingle, timeline, triage, cpu-bound, io-bound, syscall-storm, deadlock, service-map, ordered-log, intercept, anomaly-trigger, correlation) require bpftrace on the host.

Install bpftrace:

# Ubuntu / Debian
sudo apt-get install bpftrace

# Fedora / RHEL
sudo dnf install bpftrace

USDT Python probes require a debug-enabled Python build:

python3 -c "import sys; print(sys.version)"
# Should show: with DTrace support
# Or check: readelf -n /usr/bin/python3 | grep stapsdt

Commands that do not require bpftrace (pure Python): attach, flow, stitch, slow, diff, pyanomaly, pydash, jq-*, curl-perf, jwt-decode, cert-check, rate-limit-test, parallel-fetch, jq-search, awk-stats, tee-branch, pipe-decouple, sed-mask, dag-run, dag-dry-run, dag-status, saga-run, saga-log, saga-replay


Command Quick Reference

# ── CORE ──────────────────────────────────────────────────────
pylow attach <PID>
pylow flow --last
pylow stitch --services api,worker,ml-service
pylow slow --threshold 200ms --watch
pylow diff --before v1.2 --after v1.3

# ── KERNEL & SYSCALL ──────────────────────────────────────────
pylow syscall <PID>
pylow malloc <PID>
pylow tcp <PID>
pylow io <PID>
pylow flame <PID> --duration 5
pylow sched <PID>
pylow page-faults <PID>
pylow context-switches <PID>
pylow kernel-blocked <PID>
pylow tlb-shootdowns <PID>
pylow irq-impact <PID>

# ── PYTHON RUNTIME ────────────────────────────────────────────
pylow pycall <PID>
pylow pyframe <PID>
pylow pycpu <PID>
pylow pyexcept <PID>
pylow pyiowait <PID>
pylow pygil <PID>
pylow pyleak <PID>
pylow pyreq <PID>
pylow timeline <PID> --duration 5.0
pylow pythread <PID>
pylow pyasync <PID>
pylow pyargs <PID>
pylow pysyscall <PID>
pylow pynplus1 <PID>
pylow pygraph <PID>
pylow pyanomaly <PID>
pylow pydash <PID>
pylow pysingle <PID> handle_request

# ── DIAGNOSTIC WORKFLOWS ──────────────────────────────────────
pylow triage <PID>
pylow cpu-bound <PID>
pylow io-bound <PID>
pylow syscall-storm <PID>
pylow deadlock <PID>
pylow service-map <PID>
pylow ordered-log <PID> --filter-internals
pylow intercept <PID> process_payment
pylow anomaly-trigger <PID> validate_payment
pylow correlation <PID> api-gateway

# ── HTTP & API ────────────────────────────────────────────────
pylow curl-perf <PID> https://api.example.com/endpoint
pylow jwt-decode <PID>
pylow cert-check <PID> api.example.com
pylow rate-limit-test <PID>
pylow parallel-fetch <PID> --concurrency 8

# ── PIPELINE PATTERNS ─────────────────────────────────────────
pylow jq-search <PID> error
pylow awk-stats <PID>
pylow tee-branch <PID>
pylow pipe-decouple <PID> /tmp/payment_pipe
pylow sed-mask <PID>
pylow wire-grep --payload '{"payment_id": "pay_982"}' --pattern '"payment_id"\s*:\s*"([^"]+)"'
pylow jq-foreach <PID>
pylow jq-custom-stdlib --action percentile --data "10,20,30,40,50"
pylow pipeline-etl
pylow grep-jq-interleave --size 1000
pylow jq-sql-export <PID> --table payments


# ── JSON ANALYSIS ─────────────────────────────────────────────
pylow jq-schema <PID>
pylow jq-nulls <PID>
pylow jq-null-paths <PID>
pylow jq-locate-key <PID> salesActivities
pylow jq-key-path <PID> name
pylow jq-all-keys <PID>
pylow jq-leaf-paths <PID> --filter-val COMPLETED
pylow jq-clean-nulls <PID>
pylow jq-depth-map <PID>
pylow jq-type-map <PID>
pylow jq-find-value <PID> gravity_admin
pylow jq-structural-diff <PID>
pylow jq-extract-subtree <PID> appModulesId
pylow jq-summary <PID>
pylow jq-validate-schema <PID> schema.json
pylow jq-array-schema <PID>
pylow jq-null-pct <PID>
pylow jq-non-null-leaves <PID>
pylow jq-parent-context <PID> salesActivities
pylow jq-locate-value-contains <PID> Kernel
pylow jq-trace-all-keys <PID> username
pylow jq-heavy-objects <PID>
pylow jq-repeated-schema <PID>
pylow jq-common-audit <PID>
pylow jq-schema-evolution <PID>
pylow jq-validate-fields <PID>
pylow jq-watch-changes <PID>
pylow jq-reduce <PID>
pylow jq-early-exit <PID> --threshold 1000.0
pylow jq-path-find <PID> --type null
pylow jq-stream-filter <PID>
pylow jq-flat-map <PID>
pylow jq-format-matrix <PID> --format csv


# ── DAG ENGINE ────────────────────────────────────────────────
pylow dag-dry-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog"
pylow dag-run --dag "auth: get_user:auth get_catalog:auth create_order:get_user,get_catalog" --workers 4
pylow dag-run --dag-file dag.json
pylow dag-status

# ── SAGA ORCHESTRATOR ─────────────────────────────────────────
pylow saga-run --steps auth,create_order,reserve_inventory,create_payment
pylow saga-run --steps auth,create_order --fail-at create_order
pylow saga-log
pylow saga-log --log-file /var/log/my_saga.log
pylow saga-replay
pylow saga-replay --log-file /var/log/my_saga.log

# ── PROTOCOL & ADVANCED PIPELINE CONTROL ───────────────────────
pylow net-tcp <PID> --url https://api.example.com
pylow chaos-run --url https://api.example.com/payments --rate 30
pylow contract-test <PID> --contract /path/to/contract.json
pylow rate-limit-check <PID> --headers-file /tmp/headers.txt
pylow shadow-compare --prod-file /tmp/prod.json --staging-file /tmp/staging.json
pylow load-gen --url https://api.example.com/payments --rps 20 --duration 10
pylow webhook-mock --port 9000 --secret whsec_secretkey
pylow behavior-fingerprint --url https://api.example.com/payments
pylow mtls-diagnose --cert /tmp/client.crt --key /tmp/client.key
pylow grpc-proto --method PaymentsService/GetPayment --payload '{"id": 462}'
pylow graphql-nplus1 --url https://api.example.com/graphql
pylow ws-handshake --url wss://api.example.com/ws
pylow infra-fingerprint --url https://api.example.com/payments
pylow diff-fuzz --url-a https://api-v1.example.com --url-b https://api-v2.example.com

pylow mtls-diagnose

Verify mutual TLS configuration, certificate chains validity and pin server certificates.

pylow mtls-diagnose --cert /tmp/client.crt --key /tmp/client.key

pylow grpc-proto

Encode protobuf structures to gRPC 5-byte frame binary formats and analyze status trailers.

pylow grpc-proto --method PaymentsService/GetPayment --payload '{"id": 462}'

pylow graphql-nplus1

Audit GraphQL schemas for N+1 database round-trip loop overheads and depth limits checks.

pylow graphql-nplus1 --url https://api.example.com/graphql

pylow ws-handshake

Verify WebSocket protocol upgrades and handshake connection status.

pylow ws-handshake --url wss://api.example.com/ws

pylow infra-fingerprint

Probe reverse proxy paths, CDN edge caching latency, and WAF rulesets.

pylow infra-fingerprint --url https://api.example.com/payments

pylow diff-fuzz

Execute differential fuzz testing between two API versions using prototype pollution and boundary payloads.

pylow diff-fuzz --url-a https://api-v1.example.com --url-b https://api-v2.example.com

pylow jq-reduce <PID>

Calculate group summary reductions and running averages from streaming lists.

pylow jq-reduce 4821

pylow jq-early-exit <PID>

Perform conditional breaks inside pipelines to exit loops early when values exceed thresholds.

pylow jq-early-exit 4821 --threshold 1000.0

pylow jq-path-find <PID>

Locate absolute nested keys matching targeted types (e.g. null, number, string, boolean).

pylow jq-path-find 4821 --type null

pylow jq-stream-filter <PID>

Process heavy arrays incrementally without memory overflows using streaming paths.

pylow jq-stream-filter 4821

pylow jq-flat-map <PID>

Serialize nested structures into a flat map of dotted keys, and reconstruct them back.

pylow jq-flat-map 4821

pylow jq-format-matrix <PID>

Convert JSON payloads to TSV, CSV, Prometheus metrics, or Elasticsearch bulk index maps.

pylow jq-format-matrix 4821 --format csv

pylow wire-grep

Extract raw structured values directly from text payloads using PCRE regex capture groups.

pylow wire-grep --payload '{"payment_id": "pay_982"}' --pattern '"payment_id"\s*:\s*"([^"]+)"'

pylow jq-foreach <PID>

Track running average values and state bounds dynamically over lists of entries.

pylow jq-foreach 4821

pylow jq-custom-stdlib

Run JQ Custom stdlib math routines (percentile, zscore, sliding window) over a list of parameters.

pylow jq-custom-stdlib --action percentile --data "10,20,30,40,50"

pylow pipeline-etl

Perform multi-stage ETL extraction, data cleaning, type coercion, and validation checking.

pylow pipeline-etl

pylow grep-jq-interleave

Benchmark DFA regex pre-filtering speeds vs JQ AST interpreter parser checks.

pylow grep-jq-interleave --size 1000

pylow jq-sql-export <PID>

Convert JSON attributes into SQL INSERT statements.

pylow jq-sql-export 4821 --table payments

pylow pipeline-run

Run the source-agnostic live transformation pipeline.

pylow pipeline-run

Integrating Custom Curl Sources (Streaming & Polling)

The pipeline is designed to be completely source-agnostic—meaning the core pipeline doesn't care what format your API returns.

To connect any custom curl command, read the target input pipe location from /tmp/pylow_pipeline_latest and append to it:

# 1. Start the pipeline in the background or another terminal
pylow pipeline-run

# 2. Feed any HTTP JSON stream directly into the pipeline
curl -sN "https://stream.example.com/events" >> $(cat /tmp/pylow_pipeline_latest)/pipes/ingest_in

# 3. Poll a REST API every 10 seconds and pipe it in
while true; do
  curl -s -H "Authorization: Bearer KEY" "https://api.example.com/payments" >> $(cat /tmp/pylow_pipeline_latest)/pipes/ingest_in
  sleep 10
done

pylow pipeline-inject <record>

Inject a manual test record into the live pipeline.

pylow pipeline-inject '{"id": "pay_1", "amount": 100, "currency": "USD"}' --stage ingest

pylow pipeline-replay

Replay dead letter box records into the pipeline.

pylow pipeline-replay --filter "." --stage normalize

pylow pipeline-test-stage <record> <transform>

Test a single stage transform without the full pipeline.

pylow pipeline-test-stage '{"id": "pay_1", "amount": 100, "currency": "USD"}' 01_normalize.jq

pylow pipeline-tap <stage>

Tap and display live logs from a specific pipeline stage.

pylow pipeline-tap normalize

CATEGORY 10 — MCP Server

pylow-mcp

Start the MCP server (Streamable-HTTP on port 8765 by default). Exposes all 113 pylow tools to any MCP-compatible client.

# HTTP mode (MCP Inspector, Cursor, remote clients)
pylow-mcp

# stdio mode (Claude Desktop local config)
pylow-mcp --stdio

# Custom host/port via env vars
MCP_HOST=0.0.0.0 MCP_PORT=9000 pylow-mcp

MCP Tool Naming

Every CLI command maps to an MCP tool with the same name (hyphens become underscores):

CLI command MCP tool name
pylow attach <PID> attach(pid)
pylow wire-grep --payload ... --pattern ... wire_grep(payload, pattern)
pylow jq-locate-value-contains <PID> <term> jq_locate_value_contains(pid, partial_val)
pylow dag-run --dag ... dag_run(dag, dag_file, workers)
pylow saga-run --steps ... saga_run(steps, fail_at, log_file)
pylow pipeline-etl pipeline_etl()
pylow graphql-nplus1 --url ... graphql_nplus1(url)
pylow mtls-diagnose --cert ... --key ... mtls_diagnose(cert, key)

[!TIP] Use MCP Inspector (http://localhost:6274 when running docker-compose) to browse all 113 tools, view their schemas, and run them interactively from the browser.


Links

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

pylow-0.1.14.tar.gz (136.8 kB view details)

Uploaded Source

Built Distribution

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

pylow-0.1.14-py3-none-any.whl (165.7 kB view details)

Uploaded Python 3

File details

Details for the file pylow-0.1.14.tar.gz.

File metadata

  • Download URL: pylow-0.1.14.tar.gz
  • Upload date:
  • Size: 136.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for pylow-0.1.14.tar.gz
Algorithm Hash digest
SHA256 832c8b5830e3fc13957cf246b127b9992a3e162bc6698c08d0b8d5084307b06f
MD5 b4a3932b949e44df75a55a1387904acf
BLAKE2b-256 a565ca0624ea2955d452db8df3c683825865ace15e4423a8d2b1edc99a7eae90

See more details on using hashes here.

File details

Details for the file pylow-0.1.14-py3-none-any.whl.

File metadata

  • Download URL: pylow-0.1.14-py3-none-any.whl
  • Upload date:
  • Size: 165.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for pylow-0.1.14-py3-none-any.whl
Algorithm Hash digest
SHA256 69ae5f2204e5e1d5906d353f742568e50e45aec5f42e73d61008dd84accb69f6
MD5 0e30609d971817efe7cd79934f4af664
BLAKE2b-256 3692e9cd605cf810227cd004aee1acd4aab1f4a0a23db0c2d1d0d4e361557b4c

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 Pingdom Monitoring Sentry Error logging StatusPage Status page