Requirements and installation
Use Python 3.10 or newer and Git. Install the SDK into the environment that runs your service. For a new application environment, run these commands from the application directory:Initialize the application
Run the server and relay before checking delivery. SetVEXLO_SPOOL_DIR in the application’s environment to the absolute queue directory used by
the relay. If you used the local installation procedure, load its saved vexlo-env.sh file
to restore the path. For another deployment, use the relay’s configured absolute queue path.
In the shell that runs the example, replace this path with that directory:
checkout.py to verify the integration. In an existing service, initialize at
startup and apply the decorators to its handler and helpers.
service identifies the application in recordings. tag_keys names entry-point arguments to
harvest for search; it does not harvest that argument from every helper. The explicit queue
path keeps initialization independent of the process’s home directory.
Initialize once before handling requests. Reinitialization replaces the writer and instance
identity; doing so during recorded work can leave calls without completion data. See
Python API for configuration and lifecycle boundaries.
Select entry points and helpers
@vexlo.trace starts a trace only when none is active. Inside an existing trace it behaves
as a step. @vexlo.step records within an active trace; without one, the function executes
without creating a recording. Instrumenting helpers alone therefore produces no trace.
The graph connects each call to its nearest recorded parent. An uninstrumented function
between two recorded calls does not appear as an intermediate node. Select the boundaries
needed to explain the operation, rather than treating the graph as a complete source call graph.
Both markers accept retries=True for known retries. Consecutive recorded calls of the same
function after a failure can then share one step, including a successful recovery with changed
arguments. See retry grouping for inferred matching and
its limits.
Add searchable identifiers
Declare known entry-point arguments withtag_keys, as the example does for order_id.
For a runtime identifier, call vexlo.tag(region="west") inside the active entry point.
Tags are indexed in clear text; choose identifiers suitable for that use.
Runtime tags reach the opening record only before the background writer serializes it.
Late tags in a long-running function can be lost without a counter. Prefer declared argument
tags for identifiers that must be present. A name declared in both tag_keys and
redact_keys is rejected at initialization; runtime pairs under redacted names are dropped
and counted.
For code-change investigations, provide your application revision as a declared argument tag
or set it early enough to be recorded. sdk_version identifies the Vexlo SDK, not the
application source revision. Vexlo does not automatically establish that a recording matches
the current checkout.
Async functions
The same markers supportasync def functions and preserve their coroutine-function shape
for framework dispatch. Capture begins when the coroutine first runs, not when it is created.
A coroutine that is never awaited produces no record. Context follows await, with separate
context for concurrent asyncio tasks.
Save and run async_example.py:
ada. Check diagnostics() for internal_errors, adapter_errors and
steps_outside_trace; all were zero in this run. These counters describe capture inside the
process. Verify server delivery separately.
Generators and early exit
A recorded generator or async generator spans its first resumption through exhaustion, close or error. Its duration includes the consumer’s time between resumptions. Yielded items and generator return values are not captured as an output. Close an async generator inside the trace if iteration ends early. Save this example asgenerator_example.py; aclosing() closes it before the entry point returns:
first with drives_lost_to_trace_end: 0. Without closing or
exhausting the generator inside the trace, later finalization can leave its recorded call
without a reported completion. That increments drives_lost_to_trace_end.
Carry context into threads
Thread pools and bare threads do not inherit the active trace. Take aCarrier snapshot
before submitting work, then restore it within each worker using attach(). Save this example
as threads.py:
rank returns, so both score calls complete inside
the trace. Join or await child work before the entry point ends if you need its recorded
completion. Calls entered after trace closure record nothing; calls finishing afterward can
leave an open step with no result. Diagnostics distinguish those cases.
A carrier is an in-process snapshot. For separate processes or services, use shared tags to
find related traces; do not treat a carrier as a network propagation format.
Verify instrumentation
Run the standalone example and retain its unique request identifier:order_id in the UI’s home search, open its trace and check that the
checkout root has both helpers. Select lookup_inventory, open Output and choose
fetch full value. Confirm that the returned inventory contains available: 0.
Alternatively, set VEXLO_ENDPOINT to your server URL
and VEXLO_TOKEN to a read token, then query the same identifier:
checkout-result.json file.
Inside the application, inspect vexlo.diagnostics() for
capture faults or calls outside a trace. Do not require every counter to be zero: intentionally
withheld tags and child work that outlives a trace have their own non-fault counters.
Application success, a local drain and server delivery are separate checks. flush() requests
a local drain with a nominal deadline; disk I/O can exceed it, and the call returns no
delivery acknowledgment. A server-side trace with the
expected helpers and readable values establishes the complete path for that execution.
Use Troubleshooting if any boundary fails.
Configure Capture controls before recording sensitive data.
Use Python API for exact signatures, configuration, limits and diagnostics.