Send Spans from a Product
You need to record trace spans from within a product -- what happened, how long it took, whether it succeeded -- and trust that those spans are stored and queryable afterward. This page walks through the bounded task of connecting to the trace service, building a span, sending it, and querying it back to confirm the round trip.
For the full setup including architecture context, the query interface, and tree reconstruction, see Collect Trace Spans from Any Product.
Prerequisites
-
Completed the
Collect Trace Spans from Any Product
guide -- you have
@forwardimpact/librpcand@forwardimpact/libtypeinstalled, and the trace service is running.
Connect
import { createClient } from "@forwardimpact/librpc";
import { createLogger } from "@forwardimpact/libtelemetry";
import { trace } from "@forwardimpact/libtype";
const logger = createLogger("my-product");
const traceClient = await createClient("trace", logger);
Send a span
Build a trace.Span and call RecordSpan.
Every span needs a trace_id (grouping related spans)
and a span_id (unique to this span):
const span = trace.Span.fromObject({
trace_id: "eval-run-042",
span_id: "step-01",
name: "generate-output",
kind: 1, // INTERNAL
start_time_unix_nano: BigInt(Date.now()) * 1_000_000n,
end_time_unix_nano: BigInt(Date.now() + 2000) * 1_000_000n,
attributes: {
"agent.name": "release-engineer",
"step.type": "generation",
},
status: { code: 1, message: "" }, // OK
resource: {
attributes: { "service.name": "my-product" },
},
});
const result = await traceClient.RecordSpan(span);
console.log("Sent:", result.success);
Expected output:
Sent: true
Send a child span
Link a child span to its parent using parent_span_id:
const childSpan = trace.Span.fromObject({
trace_id: "eval-run-042",
span_id: "step-02",
parent_span_id: "step-01",
name: "verify-output",
kind: 1,
start_time_unix_nano: BigInt(Date.now()) * 1_000_000n,
end_time_unix_nano: BigInt(Date.now() + 800) * 1_000_000n,
attributes: {
"agent.name": "release-engineer",
"step.type": "verification",
"verdict": "pass",
},
status: { code: 1, message: "" },
resource: {
attributes: { "service.name": "my-product" },
},
});
await traceClient.RecordSpan(childSpan);
Send an error span
When a step fails, set the status code to ERROR (2)
with a message:
const errorSpan = trace.Span.fromObject({
trace_id: "eval-run-042",
span_id: "step-03",
parent_span_id: "step-01",
name: "publish-results",
kind: 1,
start_time_unix_nano: BigInt(Date.now()) * 1_000_000n,
end_time_unix_nano: BigInt(Date.now() + 500) * 1_000_000n,
attributes: {
"agent.name": "release-engineer",
},
status: { code: 2, message: "Connection refused on port 3005" },
resource: {
attributes: { "service.name": "my-product" },
},
});
await traceClient.RecordSpan(errorSpan);
Query to confirm
After sending spans, query them back by trace ID to confirm storage:
const query = trace.QueryRequest.fromObject({
filter: { trace_id: "eval-run-042" },
});
const result = await traceClient.QuerySpans(query);
console.log("Stored spans:", result.spans?.length ?? 0);
for (const s of result.spans ?? []) {
const status = s.status?.code === 2 ? "ERROR" : "OK";
console.log(` ${s.name} [${status}]`);
}
Expected output:
Stored spans: 3
generate-output [OK]
verify-output [OK]
publish-results [ERROR]
Handle send failures
RecordSpan validates that trace_id and
span_id are present. Missing either produces a gRPC
error:
try {
const bad = trace.Span.fromObject({
trace_id: "",
span_id: "orphan",
name: "missing-trace-id",
});
await traceClient.RecordSpan(bad);
} catch (err) {
console.error(err.message);
// "trace_id is required"
}
If the trace service is unreachable, the client retries up to 10 times with a 1-second delay before surfacing the connection error.
Verify
You have reached the outcome of this guide when:
-
RecordSpanwith a validtrace_idandspan_idreturns{ success: true }. - Child spans reference their parent and appear in the same trace query.
- Error spans preserve their status code and message.
-
QuerySpansreturns all spans sent under the sametrace_id.
Related
- Collect Trace Spans from Any Product -- return to the full guide for architecture context, query options, and tree reconstruction.
-
Trace Analysis --
analyze stored traces as qualitative research with
fit-trace.