Simple, powerful Python function monitoring with beautiful terminal output.
Track CPU, memory, and performance of any Python function with a simple decorator. Perfect for finding memory leaks, bottlenecks, and understanding your code's resource usage.
- π― Function-level tracking - Monitor any function with a simple
@monitor()decorator - π Real-time metrics - CPU, memory, execution time, and throughput
- π¨ Beautiful output - Color-coded, visual terminal display
- π Memory leak detection - Automatic detection of memory growth
- π Nested function support - Track call hierarchies with visual indentation
- π Zero configuration - Works out of the box
- π Aggregated statistics - See summaries across all function calls
- π§΅ Thread-safe - Works with multi-threaded applications
pip install taskmonOr install from source:
git clone https://github.com/kadirtutenn/taskmon
cd taskmon
pip install -e .from taskmon import monitor
@monitor()
def process_data(items):
result = []
for item in items:
result.append(item * 2)
return result
# Call your function normally
data = process_data([1, 2, 3, 4, 5])Output:
ββ π process_data() started [call_1_1738574400123]
β π Baseline: CPU 2.3% | MEM 156.2MB | THR 4
ββ β
process_data() completed in 0.02s
π CPU: 3.1% [ββββββββββ] | MEM: π’ +0.8MB (peak: 157.0MB)
β‘ Processed: 5 items (250.0 items/s)
from taskmon import monitor
@monitor()
def calculate_fibonacci(n):
if n <= 1:
return n
return calculate_fibonacci(n-1) + calculate_fibonacci(n-2)
result = calculate_fibonacci(10)from taskmon import monitor, track
@monitor()
def data_pipeline():
with track("load_data"):
data = load_from_database()
with track("process"):
processed = process_data(data)
with track("save"):
save_to_database(processed)
data_pipeline()Output shows nested structure:
ββ π data_pipeline() started
β π Baseline: CPU 2.1% | MEM 145.3MB | THR 4
ββ π load_data started
β π Baseline: CPU 2.3% | MEM 145.5MB | THR 4
ββ β
load_data completed in 1.23s
π CPU: 15.2% [ββββββββββ] | MEM: π’ +12.3MB (peak: 157.8MB)
ββ π process started
β π Baseline: CPU 3.1% | MEM 157.8MB | THR 4
ββ β
process completed in 2.45s
π CPU: 45.6% [ββββββββββ] | MEM: π‘ +45.2MB (peak: 203.0MB)
ββ β
data_pipeline() completed in 3.89s
π CPU: 21.3% [ββββββββββ] | MEM: π’ +9.1MB (peak: 203.0MB)
from taskmon import monitor
@monitor()
def process_batch(items):
results = []
for item in items:
results.append(transform_item(item))
return results
# Automatically tracks len(results)
items = ["item1", "item2", "item3"]
process_batch(items)Output:
ββ β
process_batch() completed in 5.23s
π CPU: 12.3% [ββββββββββ] | MEM: π’ +2.1MB (peak: 158.4MB)
β‘ Processed: 3 items (0.6 items/s)
from taskmon import monitor, print_summary
@monitor()
def expensive_function(n):
time.sleep(n)
return n * 2
# Call multiple times
for i in range(5):
expensive_function(i)
# Print summary
print_summary()Output:
================================================================================
π FUNCTION MONITORING SUMMARY
================================================================================
Function Calls Total Time Avg Time Failures
--------------------------------------------------------------------------------
__main__.expensive_function 5 10.00s 2.000s 0 (0.0%)
================================================================================
from taskmon import monitor
@monitor()
def potential_leak():
data = []
for i in range(1000000):
data.append(i)
# Oops, forgot to clean up!
return len(data)
potential_leak()Output (notice the π΄ red indicator):
ββ β
potential_leak() completed in 0.15s
π CPU: 85.2% [ββββββββββ] | MEM: π΄ +156.3MB (peak: 312.5MB)
β‘ Processed: 1,000,000 items (6666666.7 items/s)
from taskmon import monitor, track
@monitor()
def plan_mission_data_collection():
with track("Load Configuration"):
satellites = get_satellite_config_cached()
with track("Count Signals"):
total_signals = get_total_signal_count()
# Process in chunks
chunk_size = 50000
for offset in range(0, total_signals, chunk_size):
with track(f"Process Chunk {offset//chunk_size + 1}"):
process_signal_chunk(offset, chunk_size, satellites)
plan_mission_data_collection()Output shows where memory leaks occur:
ββ π plan_mission_data_collection() started
ββ π Load Configuration started
ββ β
Load Configuration completed in 0.05s
π CPU: 2.1% | MEM: π’ +1.2MB
ββ π Count Signals started
ββ β
Count Signals completed in 0.23s
π CPU: 5.3% | MEM: π’ +0.1MB
ββ π Process Chunk 1 started
ββ β
Process Chunk 1 completed in 12.34s
π CPU: 45.2% | MEM: π’ +2.3MB
ββ π Process Chunk 2 started
ββ β
Process Chunk 2 completed in 11.89s
π CPU: 43.1% | MEM: π΄ +125.6MB <-- Memory leak here!
ββ β
plan_mission_data_collection() completed in 45.67s
π CPU: 38.5% | MEM: π΄ +128.4MB
- π Function started
- β Successfully completed
- β Failed with error
β οΈ Warning
- π’ Green: < 10MB change (normal)
- π‘ Yellow: 10-100MB change (monitor)
- π΄ Red: > 100MB change (memory leak!)
CPU: 15.2% [ββββββββββ] <- Visual bar
CPU: 45.6% [ββββββββββ]
CPU: 85.2% [ββββββββββ]
Decorator to monitor function performance.
Parameters:
items_attr(str, optional): Attribute name to track as items_processedprint_summary(bool): Print summary after function completes
Example:
@monitor(items_attr='count', print_summary=True)
def process_data():
return {"count": 100, "data": [...]}Context manager for monitoring code sections.
Parameters:
section_name(str): Name of the sectionitems(int): Number of items processed
Example:
with track("database_query", items=100):
results = db.query(...)Get function statistics.
Parameters:
function_name(str, optional): Specific function name, or None for all
Returns: Dict with statistics
Example:
stats = get_stats("my_module.my_function")
print(f"Total calls: {stats['total_calls']}")
print(f"Avg CPU: {stats['avg_cpu']:.1f}%")Print summary of all monitored functions.
Clear all statistics.
Get list of currently active function calls.
Returns: List of FunctionMetrics objects
@monitor()
def process_batch(items):
count = 0
for item in items:
process(item)
count += 1
return count # Automatically tracked@monitor()
def parent_function():
child_function_1()
child_function_2()
@monitor()
def child_function_1():
pass
@monitor()
def child_function_2():
passOutput shows hierarchy:
ββ π parent_function() started
ββ π child_function_1() started
ββ β
child_function_1() completed
ββ π child_function_2() started
ββ β
child_function_2() completed
ββ β
parent_function() completed
from celery import shared_task
from taskmon import monitor
@shared_task
@monitor()
def celery_task(data):
process(data)from taskmon import get_active_calls, get_stats
# Get currently running functions
active = get_active_calls()
for call in active:
print(f"{call.function_name}: {call.memory_delta_mb:.1f}MB")
# Get historical stats
stats = get_stats()
for func_name, metrics in stats.items():
if metrics['avg_cpu'] > 50:
print(f"High CPU function: {func_name}")@monitor()
def suspect_function():
# Your code
passLook for π΄ indicators showing large memory growth.
@monitor()
def pipeline():
with track("step1"):
slow_step()
with track("step2"):
fast_step()See which sections take the most time.
@monitor()
def process_chunks():
for chunk in chunks:
with track(f"chunk_{i}"):
process(chunk)Identify which chunks are slow or leak memory.
@monitor()
def critical_api_endpoint():
# Monitor in production
passTrack performance metrics over time.
taskmon is designed to be lightweight:
- CPU overhead: < 1% in most cases
- Memory overhead: < 1MB
- Sampling: Background thread samples every 1 second
- Thread-safe: Safe for multi-threaded applications
Set your terminal to support ANSI colors:
export TERM=xterm-256colorDisable monitoring for specific functions:
# Just remove the decorator
def my_function():
passThe library respects standard output - redirect if needed:
import sys
sys.stdout = open('monitor.log', 'w')Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
MIT License - see LICENSE file for details.
Built with:
- psutil - Process and system utilities
- π§ Email: kadirsmdb@gmail.com
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
Made with β€οΈ for Python developers who care about performance