Custom Python Collectors
A collector lets you add a new metric group to the thread or
subprocess_python monitors without touching the monitor itself — register a
CollectorBackend + StorageHandler pair in collectors.yaml and it is loaded
automatically.
Note
This guide covers the Python monitor backends (thread, subprocess_python).
For the native C binary see Custom C Collectors.
Step 1 — Create the collector module
By convention each metric lives in its own subdirectory under
jumper_extension/monitor/metrics/:
jumper_extension/monitor/metrics/
└── your_metric/
├── common.py # YourCollectorBackend — ABC with typed collect() signature
├── psutil.py # PsutilYourCollector — concrete implementation
└── __init__.py # re-exports YourCollectorBackend
Example — NetworkCollector
metrics/network/common.py
from abc import abstractmethod
from jumper_extension.monitor.metrics.common import CollectorBackend
from jumper_extension.monitor.metrics.context import CollectionContext
class NetworkCollectorBackend(CollectorBackend):
name = "network-base"
@abstractmethod
def collect(self, level: str, context: CollectionContext) -> list[int]: ...
metrics/network/psutil.py
import psutil
from jumper_extension.monitor.metrics.context import CollectionContext
from jumper_extension.monitor.metrics.network.common import NetworkCollectorBackend
class PsutilNetworkCollector(NetworkCollectorBackend):
"""System-wide network I/O via psutil.
psutil does not expose per-process network counters on Linux without root,
so only the 'system' level returns real data; other levels report zeros.
"""
name = "network-psutil"
def collect(self, level: str, context: CollectionContext) -> list[int]:
if level == "system":
net = psutil.net_io_counters()
if net:
return [net.bytes_sent, net.bytes_recv,
net.packets_sent, net.packets_recv]
return [0, 0, 0, 0]
metrics/network/__init__.py
from jumper_extension.monitor.metrics.network.common import NetworkCollectorBackend
Step 2 — Pick a StorageHandler
A handler converts the value returned by collect() into DataFrame columns.
Built-in handlers live in jumper_extension/monitor/metrics/handlers.py:
| Handler | Raw type | Output columns |
|---|---|---|
ScalarHandler(column="x") |
float |
{"x": v} |
PerDeviceAggregateHandler(prefix="p_") |
list[float] |
p_0, p_1, …, p_avg, p_min, p_max |
PerDeviceMultiAggregateHandler(prefix="p_", metrics=[…]) |
tuple[list[float], …] |
fan-out of PerDeviceAggregate per metric |
CumulativeRateHandler(columns=[…]) |
list[int] |
per-column delta/second rates |
NoOpHandler() |
None |
{} |
NetworkCollector returns cumulative byte/packet counters → pair it with
CumulativeRateHandler to get rates automatically.
Step 3 — Register in collectors.yaml
# jumper_extension/config/collectors/python/collectors.yaml
collectors:
# ... existing collectors ...
network:
_target_: jumper_extension.monitor.metrics.network.psutil.PsutilNetworkCollector
inject: []
handler:
_target_: jumper_extension.monitor.metrics.handlers.CumulativeRateHandler
columns: [net_bytes_sent, net_bytes_recv, net_packets_sent, net_packets_recv]
The inject: key
inject: lists PerformanceMonitor attributes passed as constructor arguments:
| Value | Type | What you get |
|---|---|---|
[] |
— | no injected dependencies |
[node_info] |
NodeInfo |
CPU/GPU count, memory limits, CPU handles |
[uid, slurm_job] |
int, str\|int |
current user ID and SLURM job ID |
[pid, process, uid, slurm_job] |
mixed | full process context (used by built-in process collector) |
With the collector registered, its columns are available in
%perfmonitor_plot --metrics. To surface them in the default plot widget see
Visualizing Custom Collector Metrics.