โโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโ โโโโโโ
โโโโโโโ โโโโโโ โโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโ โโโโโโ
โโโ โโโโโโโโโโโ โโโโโโ โโโ โโโ โโโโโโ โโโโโโโโโโโโโโโโโโโ
โโโ โโโโโโโโโโโ โโโโโโ โโโ โโโ โโโโโโ โโโ โโโโโโโโโโโโโโโ
pip install perftracePerfTrace is a Python performance-tracing library and CLI tool that instruments functions, class methods, and arbitrary code blocks with a single decorator or context manager โ no external agents, no cloud accounts, no configuration required.
It captures execution time, memory allocation, CPU load, file I/O, network activity, thread context, garbage collection, and exception tracebacks โ all persisted in a local DuckDB database (or PostgreSQL) and surfaced through a rich, color-coded CLI.
โญโโโโโโโโโโโโโโโโโโโโโโโโ PerfTrace โโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Decorator โ Collectors โ Storage โ CLI Query/Export โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
- Architecture
- Key Features
- Installation
- Quick Start
- Instrumentation
- Tracing Workflow
- CLI Reference
- Terminal Previews
- Configuration
- Exception Tracking
- Database Schema
- Schema Migrations
- Examples
- Testing
- PerfTrace vs APM Tools
- Contributing
- License
PerfTrace is structured as three independent layers that compose together.
graph TD
subgraph Instrumentation["Instrumentation Layer (core/)"]
D["@perf_trace_metrics\ndecorator"]
CL["@perf_trace_metrics_cl\nclass decorator"]
CM["PerfTraceContextManager"]
end
subgraph Collectors["Collectors (collectors.py)"]
direction LR
EC["ExecutionCollector"]
MC["MemoryCollector"]
CC["CPUCollector"]
FI["FileIOCollector"]
GC["GarbageCollector"]
TC["ThreadContextCollector"]
NA["NetworkActivityCollector"]
XC["ExceptionCollector"]
end
subgraph Storage["Storage Layer (storage/)"]
CF["ConfigManager\n(~/.perftrace/config.yaml)"]
DU["DuckDB\n(default, zero-setup)"]
PG["PostgreSQL\n(optional)"]
end
subgraph CLI["CLI Layer (cli/)"]
direction LR
SH["show / recent\n/ stats"]
AN["compare / top\n/ exceptions"]
EX["export CSV\n/ JSON / HTML"]
SM["system-monitor\n/ memory"]
end
D --> Collectors
CL --> Collectors
CM --> Collectors
Collectors -->|"report() โ dict"| Storage
CF --> Storage
Storage --> CLI
graph LR
subgraph core
decorators["decorators.py\n@perf_trace_metrics\n@perf_trace_metrics_cl"]
context_manager["context_manager.py\nPerfTraceContextManager"]
collectors["collectors.py\n9 collectors"]
end
subgraph storage
factory["__init__.py\nget_storage()"]
duckdb["duckdb/\nduckdb_storager.py\nschema.py"]
postgres["postgres/\nPostgres_storager.py\nschema.py"]
loader["database_loader.py\nDataFrame converters"]
config["config_manager.py\nYAML config"]
end
subgraph cli
main["main.py\nClick group"]
registry["registry.py\ncli_commands dict"]
logger["logger.py\nRich formatters"]
commands["commands/\n30+ CLI commands"]
end
decorators --> collectors
context_manager --> collectors
collectors --> factory
factory --> duckdb
factory --> postgres
duckdb --> loader
postgres --> loader
config --> factory
loader --> commands
commands --> logger
registry --> main
| Feature | Detail |
|---|---|
| Zero-boilerplate instrumentation | One decorator or with block โ nothing else to configure |
| Sub-ยตs execution timing | Formatted automatically as ยตs / ms / s |
| Memory tracking | tracemalloc current + peak; displayed as B / KB / MB / GB |
| CPU monitoring | Per-call CPU % and RAM delta (MB) |
| File I/O | Read/write byte and op-count deltas via psutil |
| Thread context | Thread count and voluntary/involuntary context-switch deltas |
| Network activity | TCP/UDP connection and byte-transfer deltas |
| Garbage collection | GC count deltas per generation |
| Exception tracking | Type, message, and full traceback โ always captured |
| Statistical summaries | min / max / avg / std-dev / p90 / p95 / p99 |
| Side-by-side comparison | Diff any two functions or context tags โ avg exec, mem, CPU, RAM |
| Multi-format export | CSV ยท JSON ยท HTML (dark-themed, collapsible tracebacks) |
| Live system monitor | 2-column layout: per-core CPU + sparkline, RAM + Swap, network I/O |
| Health diagnostics | doctor checks config, DB, disk space, and Python version |
| Pluggable storage | DuckDB (default, zero-config) or PostgreSQL |
pip install perftraceRequirements: Python 3.11+
# Verify your setup
perftrace doctor
# Colorized performance overview
perftrace summary
# Top memory consumers across all traced code
perftrace top-memory
# Hottest CPU functions (top 5)
perftrace top-cpu --limit 5
# All runs that raised exceptions
perftrace exceptions
# Head-to-head function comparison
perftrace compare-function load_data process_data
# Head-to-head context tag comparison
perftrace compare-context batch_job import_flow
# Per-function file I/O breakdown
perftrace io-report
# Live system monitor (3-second refresh)
perftrace system-monitorPer-command help:
perftrace <command> --helpshows arguments and options inline.
from perftrace import perf_trace_metrics
@perf_trace_metrics(profilers=["cpu", "memory", "file", "execution"])
def process_data(records):
return [r * 2 for r in records]Pass profilers="all" to activate every collector at once:
@perf_trace_metrics(profilers="all")
def full_trace():
...| Key | Collector | Metrics captured |
|---|---|---|
execution |
ExecutionCollector |
wall-clock time (always active) |
cpu |
CPUCollector |
CPU %, RAM delta (MB) |
memory |
MemoryCollector |
tracemalloc current + peak |
file |
FileIOCollector |
read/write bytes and op counts |
garbagecollector |
GarbageCollector |
GC generation count deltas |
ThreadContext |
ThreadContextCollector |
thread count + ctx-switch deltas |
network |
NetworkActivityCollector |
TCP/UDP connections + byte transfer |
ExceptionCollectorandExecutionCollectorare always active โ they run regardless of theprofilersargument.
from perftrace import perf_trace_metrics_cl
@perf_trace_metrics_cl(profilers=["cpu", "memory"])
class Pipeline:
def step_one(self, x):
return x + 1
@staticmethod
def step_two(y):
return y * 2from perftrace import PerfTraceContextManager
with PerfTraceContextManager(context_tag="etl-load", cls_collectors=["cpu", "memory", "file"]):
data = load_large_dataset()sequenceDiagram
participant App as Your Application
participant Dec as Decorator / ContextManager
participant Col as Collectors
participant DB as Storage (DuckDB / PG)
participant CLI as perftrace CLI
App->>Dec: function call / with block
Dec->>Col: start() โ snapshot baseline metrics
Dec->>App: execute wrapped code
App-->>Dec: return / raise
Dec->>Col: stop() โ capture deltas
Col-->>Dec: report() โ dict per collector
Dec->>DB: INSERT row (JSON columns per collector)
Note over DB: ProfilerReport table
CLI->>DB: SELECT / aggregate
DB-->>CLI: DataFrame
CLI->>CLI: render Rich table / export file
| Command | Description |
|---|---|
version |
Show installed PerfTrace version and runtime info |
help |
Grouped command reference (also: --help / -h) |
doctor |
Health-check config, DB, disk space, and Python version |
summary |
Color-coded performance overview with hotspot detection |
list |
List all profiled functions and context tags |
| Command | Args / Options | Description |
|---|---|---|
show-function <name> |
โ | All runs in one compact table |
recent-function <name> |
โ | Deep per-metric breakdown of the latest run |
stats-function <name> |
โ | Statistical summary (min/max/avg/p90/p95/p99) |
search-function <name> |
โ | Historical run list |
count-function |
--limit N |
Call frequency with distribution bars |
slowest |
โ | Top-10 by cumulative execution time |
fastest |
โ | Top-10 by cumulative execution time (ascending) |
compare-function <A> <B> |
โ | Side-by-side avg metric comparison of two functions |
| Command | Args / Options | Description |
|---|---|---|
show-context <tag> |
โ | All runs for a context tag in one compact table |
recent-context <tag> |
โ | Deep breakdown of the most recent run |
stats-context <tag> |
โ | Statistical summary |
search-context <tag> |
โ | Historical run list |
count-context |
--limit N |
Call frequency with distribution bars |
compare-context <A> <B> |
โ | Side-by-side avg metric comparison of two context tags |
| Command | Options | Description |
|---|---|---|
top-memory |
--limit N (default 10) |
Top N functions/contexts by peak memory allocation |
top-cpu |
--limit N (default 10) |
Top N functions/contexts by average CPU usage |
exceptions |
--limit N (default 50) |
All trace records where an exception was raised |
compare-function <A> <B> |
โ | Avg exec time, memory, CPU, RAM delta โ side by side |
compare-context <A> <B> |
โ | Same comparison for context manager tags |
io-report |
--limit N (default 20) |
Aggregated file read/write bytes and op counts |
| Command | Description |
|---|---|
today |
All calls executed today |
history |
Calls by date range |
| Command | Options | Description |
|---|---|---|
system-status |
โ | Current system snapshot with formatted sections |
system-info |
โ | Static hardware / OS / Python environment details |
system-monitor |
--interval N |
Live 2-column monitor โ per-core CPU + sparkline, RAM + Swap, network I/O delta; default 3 s refresh |
memory |
โ | Memory breakdown per function/context |
All exports print the absolute file path, row count, and file size on completion.
| Command | Options | Description |
|---|---|---|
export-csv |
--filename |
All records |
export-function-csv |
--filename |
Function records only |
export-context-csv |
--filename |
Context records only |
| Command | Options | Description |
|---|---|---|
export-json |
--filename --limit |
All records |
export-function-json |
--filename --limit |
Function records only |
export-context-json |
--filename --limit |
Context records only |
| Command | Options | Description |
|---|---|---|
export-html |
--filename |
All records โ dark-themed responsive report |
export-function-html |
--filename |
Function records |
export-context-html |
--filename |
Context records |
HTML reports include:
- Dark-themed responsive table
- JSON metric cells expanded inline (
key: value) - Exception column colour-coded โ red with collapsible traceback / green for clean runs
- Generated timestamp and row count in the header
$ perftrace compare-function load_data process_data
โญโโโ PerfTrace Function Comparison โโโโฎ
Function Comparison โ Average Metrics
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโ
โ Metric โ load_data โ process_data โ ฮ (A โ B) โ
โ โ (8 runs) โ (8 runs) โ โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโค
โ Avg Exec Time โ 12.450 ms โ 3.210 ms โ +9.240 ms โ
โ Avg Peak Mem โ 2.34 MB โ 512.00 KB โ +1.84 MB โ
โ Avg CPU % โ 14.2% โ 8.7% โ โ โ
โ Avg RAM ฮ โ +0.120 MB โ +0.031 MB โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโ
compare-context batch_job import_flow produces the same layout with context tag names as headers.
$ perftrace show-function my_function
my_function (12 records)
โโโโโฌโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ # โ Timestamp โ Exec Time โ Peak Mem โ RAM ฮ โ CPU% โ Threads ฮโ Status โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโค
โ 1 โ 2024-06-22 10:00:00 โ 1.234 ms โ 45.32 KB โ+0.001 MBโ 2.1%โ 0 โ โ OK โ
โ 2 โ 2024-06-22 10:01:05 โ 2.891 ms โ 48.10 KB โ+0.002 MBโ 1.8%โ 0 โ โ OK โ
โ 3 โ 2024-06-22 10:02:11 โ 0.891 ms โ 45.00 KB โ+0.000 MBโ 1.5%โ 0 โโ ValueErrorโ
โโโโโดโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโ
$ perftrace top-memory --limit 5
Top 5 โ Peak Memory Usage
โโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโ
โ # โ Name โ Type โ Current Mem โ Peak Mem โ
โโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโค
โ 1 โ load_data โ function โ 2.10 MB โ 2.34 MB โ
โ 2 โ etl-load โ context โ 1.80 MB โ 1.95 MB โ
โ 3 โ process_data โ function โ 498.10 KB โ 512.00 KBโ
โโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโ
$ perftrace exceptions
Exception Traces (2 records)
โโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโ
โ Timestamp โ Name โ Type โ Exec Time โ Exception Typeโ Message โ
โโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโค
โ 2024-06-22 10:02:11 โ process_data โ function โ 0.891 ms โ ValueError โ invalid literal ... โ
โ 2024-06-22 14:15:33 โ etl-load โ context โ 3.212 ms โ KeyError โ 'user_id' โ
โโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ
โญโโโโโโโโโโโโโโโโโโโโ Live System Monitor โโโโโโโโโโโโโโโโโโโโโโฎ
โ Left column โ Right column โ
โ โโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ CPU (per-core bars โ Disk Used / Free / % โ
โ + sparkline trend) โ Network โ sent/s โ recv/s โ
โ Memory RAM + Swap โ System Uptime ยท Processes โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Colours: green โค 50 % ยท yellow 50โ80 % ยท red > 80 %
Sparkline characters: โโโโโ
โโโ (last 20 readings)
Config file locations:
| OS | Path |
|---|---|
| Linux / macOS | ~/.perftrace/config.yaml |
| Windows | %USERPROFILE%\.perftrace\config.yaml |
database:
engine: duckdb
duckdb:
path: ./data/perftrace.duckdbdatabase:
engine: postgresql
postgresql:
host: localhost
port: 5432
user: postgres
password: your_passwordInteractive wizard:
perftrace set-config
perftrace doctor # verify connectionEvery instrumented call records an exception_collector entry automatically โ no opt-in required.
| Field | Description |
|---|---|
occurred |
true if an exception was raised |
exception_type |
Exception class name, e.g. ValueError |
exception_message |
The string message |
traceback |
Full formatted traceback |
show-functionandshow-contexthighlight failed runs withโ ExcTypein red.exceptionslists every failed trace across all functions and contexts.- HTML exports include collapsible
<details>blocks, colour-coded red for failures.
Table: ProfilerReport
| Column | Type | Source |
|---|---|---|
timestamp |
TIMESTAMP | datetime.datetime.now() |
function_name |
VARCHAR | decorator: func.__name__; context: NULL |
context_tag |
VARCHAR | decorator: NULL; context: user-supplied tag |
execution_collector |
JSON | ExecutionCollector.report() |
memory_collector |
JSON | MemoryCollector.report() |
cpu_collector |
JSON | CPUCollector.report() |
file_io_collector |
JSON | FileIOCollector.report() |
garbage_collector |
JSON | GarbageCollector.report() |
thread_context_collector |
JSON | ThreadContextCollector.report() |
network_activity_collector |
JSON | NetworkActivityCollector.report() |
exception_collector |
JSON | ExceptionCollector.report() |
When upgrading PerfTrace, new columns are added automatically via ALTER TABLE โฆ ADD COLUMN IF NOT EXISTS. Existing rows get NULL for new columns โ no manual migration needed.
# duckdb/schema.py
DUCKDB_MIGRATION = f"""
ALTER TABLE {DB_TABLE_NAME} ADD COLUMN IF NOT EXISTS new_col JSON;
"""The storager runs this after CREATE TABLE IF NOT EXISTS and silently ignores "column already exists" errors.
The example/ directory contains a ready-to-run sample that exercises every collector type.
from perftrace import perf_trace_metrics, perf_trace_metrics_cl, PerfTraceContextManager
# Class decorator โ instruments every method
@perf_trace_metrics_cl(profilers=["cpu", "memory"])
class MyProcessor:
def step1(self, x): return x + 1
def step2(self, y): return y * 2
# Function decorator โ all profilers
@perf_trace_metrics(profilers="all")
def list_comprehensive():
return [i for i in range(100_000)]
# File I/O collector
@perf_trace_metrics(profilers=["cpu", "file"])
def normal_loop():
with open("sample_io.txt", "w") as f:
f.write("hello\n" * 100)
# Read the file โ captured by the "file" collector
@perf_trace_metrics(profilers=["cpu", "memory", "file"])
def read_sample_file():
with open("sample_io.txt") as f:
return f.readlines()
# Context manager โ include "file" to capture I/O inside the block
with PerfTraceContextManager(
context_tag="work", cls_collectors=["cpu", "memory", "file"]
) as ctx:
with open("sample_io.txt", "a") as f:
f.write("appended\n")Run it, then explore the data:
python example/test_data.py
perftrace summary
perftrace io-report
perftrace show-function normal_loop
perftrace compare-function normal_loop list_comprehensive
perftrace show-context workFile I/O tip: The profiler key for
FileIOCollectoris"file"โ not"file_io".
Always include"file"inprofilersfor any function that reads or writes files.
PerfTrace ships a pytest suite under tests/. Run the full suite with:
pip install pytest
pytest tests/| File | Module(s) covered | Tests |
|---|---|---|
test_system_monitor.py |
system_monitor |
42 |
test_io_report.py |
io_report |
25 |
test_compare_data.py |
compare_data (function + context) |
31 |
test_top_commands.py |
top_commands (top_memory + top_cpu) |
28 |
test_exceptions_cmd.py |
exceptions |
20 |
test_show_data.py |
show_data (show_function + show_context) |
19 |
test_recent_data.py |
recent_data (recent_function + recent_context) |
22 |
test_stats.py |
stats (stats_function + stats_context) |
20 |
test_frequency_count.py |
frequency_count (count_function + count_context) |
36 |
test_summary.py |
summary |
28 |
test_version_fastest_slowest.py |
version, fastest_execution, slowest_execution |
32 |
Total: 303 tests ยท 0 failures
- Each test file patches
check_retrieve_datawith an in-memory pandas DataFrame โ no real database required. - Rich
Consoleoutput is captured by patching the module-levelconsoleobject with aConsole(file=StringIO())instance, enabling full output assertions. - Table builders in
system_monitorare tested with@patchon allpsutilcalls so tests are deterministic and fast. - Helper functions (
_fmt_bytes,_pct_color,_progress_bar,_sparkline,_bar,_avg_metrics) are unit-tested directly, independently of the CLI layer.
| PerfTrace | APM (Datadog, New Relicโฆ) | |
|---|---|---|
| Setup | pip install |
Agent + cloud account |
| Granularity | Function / block level | Service / request level |
| Storage | Local DuckDB or PostgreSQL | Cloud |
| Exception capture | Automatic | Yes |
| Export | CSV / JSON / HTML | Dashboards / API |
| Always-on sampling | No โ on demand | Yes |
| Cost | Free, open-source | Subscription |
PerfTrace is developer-local, on-demand, and free โ purpose-built for CI profiling, performance regression testing, and targeted optimization work where cloud APM is too heavyweight.
Contributions are welcome. The repo includes inline documentation covering the architecture, collector contracts, schema migration pattern, value-formatting helpers, and the checklist to follow when adding commands, collectors, or export formats.