From an execution to a graph
Consider a checkout that calculates the price of two line items, applies a discount and adds tax. This recording captures the entry point and five functions involved in that calculation. One function runs twice, producing seven recorded calls:checkout is the root call, the entry point at the top of the graph. price_order is its
recorded child. Below that, price_lines calls line_total for each order item, while
discount_for and tax_for calculate the adjustments to the subtotal.
Calls sharing the same recorded parent are siblings. That relationship alone does not imply
that they ran simultaneously or in completion order.
An edge connects a call to its nearest recorded parent. It does not prove that the parent
called it directly in the source. In another captured execution, an instrumented operation
called an uninstrumented helper, which called instrumented read_part. The recording contains
operation and read_part, with the latter directly beneath the root. The helper still ran;
it was outside the instrumentation boundary.
This is an observed execution graph. A static call graph describes relationships inferred
from code, including paths that a particular request did not take. Vexlo’s recordings provide
the complementary evidence of instrumented paths that executed and the values they saw.
Traces, recorded calls and call identity
A trace is the recording of one instrumented entry-point execution. Its identifier,trace_id, lets a reader find the same recording through the UI, HTTP API or MCP. A step
is a recorded function call within that trace. Its path identifies the call, and
parent_path identifies the recorded parent. The root has no parent, and its own path is
the empty string.
The checkout recording contains two line_total steps. They have the same function name but
different paths because the application invoked the function for two line items. A function
name identifies the function within its service; a step path identifies a call in this execution.
Recorded names are module-qualified, such as order_service.line_total. Copy the full name
when querying a function.
Paths are opaque. Use them unchanged to select values or relate a child to its parent; do not
decode them to infer order, source locations or function identity. Structural tree order helps
navigate a recording, while timestamps describe when its calls ran.
An entry-point marker inside an existing trace adds a step to that recording instead of
starting another trace. A step marker outside an active trace does not start a recording.
The data attached to a call
Each completed attempt can have captured inputs, output, error and logs. Inputs describe arguments received by the function. Output describes its return value. Error data describes an exception, while log entries belong to the attempt during which they were captured. Log capture is opt-in. In this recording,price_order received these arguments, decoded from its recorded
JSON input:
Attempts and retries
An attempt is one execution of a recorded call. A retrying call has several attempts, each with its own status, timing and capture metadata. A more extensively instrumented checkout recording also captures payment. That call failed on the primary processor and succeeded on the backup processor:
The application performs retries. Vexlo records and groups them. The SDK supports declared
retry grouping and inference from qualifying repeated calls; the exact matching rules are in
Python API. Declaring a retry does not cause the
SDK to rerun a function after an error.
The two successful
line_total calls remain separate steps. The two executions of
authorize_payment appear as attempts of one step. This preserves the distinction between
repeated work for different items and repeated attempts to complete a logical operation.
A successful recovery does not erase the earlier failure.
Per-attempt previews, status and timing retain attempt identity. The trace-values route,
/v1/traces/{id}/values, selects paths and slots without an attempt selector or attempt
identity in its results. Do not assign an arbitrary returned value to a numbered retry
attempt. The recorded-values guide explains this current interface limitation.
Timing and concurrent work
Each attempt records a start offset from the trace’s start and, when completed, its elapsed duration. These describe elapsed intervals measured by a monotonic clock, not exclusive CPU time. A parent can remain active while its children run, and concurrent children can overlap. A separate captured operation started two reads concurrently. The profile read started first, but the preferences read finished first:
Adding the two child durations would overstate the operation’s elapsed time because part of
their work overlapped. Structural call order is also different from completion order. Use the
Timeline view to inspect intervals and the Table view to examine
individual calls.
A step’s total duration is the sum of its completed attempts. It excludes unfinished attempts
and does not measure the entire interval between the first attempt’s start and a later retry’s
completion. For an ongoing retry, that total is a lower bound rather than a final duration.
An attempt with no reported completion has no completed duration. It can still be running,
but a lost completion record leaves the same evidence.
Execution status and recording completeness
The entry point’s status, a call attempt’s status and the recording’s completeness answer different questions. Two captured executions illustrate the distinction:
In
failed-root, the entry point raised an error after a successful read. In handled-error,
the entry point caught a child’s exception and returned a rejection response normally. Both
recordings arrived completely. Neither business success nor failure determines completeness.
A running recording has no root completion and has received recent data. An abandoned one
still has no root completion after the inactivity window. A complete recording has a root
completion without reported loss, shedding, truncation or instance mismatch; a partial one has
a root completion and at least one of those conditions. These states are derived when data is
read, so later arrivals can change the result.
Completeness does not establish that every possible function was instrumented or that every
branch ran. Inspect completeness and loss information
before interpreting a missing call or unfinished attempt.
Connect recordings to code and other requests
Function metadata aggregates observed callers, callees and examples across recordings. Use these relationships to find executions relevant to a function and inspect how it was used. Use application-revision provenance when relating a recording to source or comparing runs around a change. A user-defined revision tag can identify the application version. Thesdk_version field identifies the SDK, not the application’s code revision. A captured stack
line does not establish that the reader’s current checkout contains the same code at that line.
A tag is a key/value pair attached to a trace. Shared identifiers, such as an order or
request ID, can find recordings across services. Those recordings remain separate traces.
Sharing a tag relates the records; it does not prove one service called another or supply an
automatic cross-service call edge.
