Attach to a running Python process¶
Kolo can start tracing a CPython process that is already running, without restarting it or having enabled Kolo in advance. This uses Python 3.14’s safe remote debugger interface.
Run kolo attach with the target process ID:
kolo attach 12345 --duration 10 --name "slow checkout"
Kolo activates inside the target process, loads that process’s Kolo config,
and writes the trace to its normal .kolo store. After saving, the command
prints the trace ID and the matching kolo cat command. Omit --duration to
trace until you press Ctrl-C.
Duration versus timeout¶
--duration controls how long Kolo records. --timeout only controls how long
the command waits for each attach, start, or save handshake. It does not stop a
healthy recording early.
Option |
Meaning |
Example |
|---|---|---|
|
Record for this long, then save. Omit it to record until Ctrl-C. |
|
|
Give each attach/start/save handshake this long to complete. Defaults to 10 seconds. |
|
For example, this records for 30 seconds and allows up to 60 seconds for each handshake around that recording:
kolo attach 12345 --duration 30 --timeout 60
The attaching command and target must:
run CPython 3.14 or newer, using the same Python major and minor version;
have Kolo installed in the target’s Python environment;
run on the same host, with a shared view of the temporary directory and the same network namespace; and
use Kolo’s default
sys.monitoringbackend. Removeuse_monitoring = falsefrom the target’s config before attaching.
The target’s current working directory and KOLO_PATH determine which
.kolo/config.toml and trace store are used. Filters, plugins, trace points,
value capture, auto-emit, and other capture settings therefore behave the same
way as when Kolo is enabled at process startup.
Note
The OS applies its normal native-debugger permissions. Linux may require
CAP_SYS_PTRACE or a less restrictive Yama policy, containers may need
--cap-add=SYS_PTRACE, macOS commonly requires sudo, and Windows may require
an Administrator terminal. See Python’s remote debugging permission
guide.
The injected code runs when the target next reaches a safe Python execution
point. A process blocked indefinitely in a system call or native extension may
not attach until it resumes executing Python bytecode. Kolo reports a timeout
instead of waiting forever when that does not happen. Remote debugging can also
be disabled in the target with PYTHON_DISABLE_REMOTE_DEBUG,
-X disable-remote-debug, or CPython’s --without-remote-debug build option.
Kolo keeps a private local control connection open while tracing. Python’s
sys.remote_exec() only queues a script; it does not return a result or provide
a way to stop that script later. The local connection lets the target confirm
that tracing really started, receive the stop request, confirm that the trace
was saved, and notice if the attaching command disappears. It is bound to this
machine only and protected by a one-use random secret. Normal target shutdown
also saves the active attached trace.
For prefork servers such as Gunicorn or Celery, attach to the worker PID that is handling the workload. Each worker is a separate Python process and needs its own attach session.
Useful debugging workflows¶
Attach without
--duration, reproduce a bad request or background job, then press Ctrl-C immediately after it finishes. This keeps an incident trace tightly scoped without restarting the process.Put high-volume framework or application paths in the target’s Kolo filters, then attach to a busy long-lived worker for a short sample. The attached trace uses those filters and the target’s value-capture settings.
Attach to one worker at a time when a problem is isolated to a particular process. The command prints the target’s Kolo store, so the resulting trace remains discoverable even when the controller was run from another directory.