Tracing
Trace context propagation, custom spans, and distributed tracing.
What is Tracing?
Tracing helps you understand the flow of a request or operation through your application. A trace consists of one or more spans, each representing a unit of work. Together, they form a timeline showing exactly what happened and how long each step took.
# Example trace for a checkout flow:
#
# [Trace: checkout-flow-abc123]
# |
# |-- [Span: validate-cart] 12ms
# |-- [Span: calculate-totals] 3ms
# |-- [Span: process-payment] 245ms
# | |-- [Span: stripe-api-call] 230ms
# |-- [Span: create-order] 18ms
# |-- [Span: send-confirmation] 45ms
#
# Total: 323msTrace Context
Apperio supports trace context via the SDK's context system. You can attach a trace ID and additional context to groups of related log entries:
import Apperio from class="syntax-string">"apperio";
class="syntax-comment">// Generate a unique trace ID
const traceId = crypto.randomUUID();
class="syntax-comment">// Log entries with trace context
Apperio.info(class="syntax-string">"Checkout started", {
traceId,
userId: class="syntax-string">"user_123",
cartItems: class="syntax-number">3,
});
class="syntax-comment">// Later in the flow...
Apperio.info(class="syntax-string">"Payment processed", {
traceId, class="syntax-comment">// Same trace ID links these events
gateway: class="syntax-string">"stripe",
amount: class="syntax-number">49.99,
});
Apperio.info(class="syntax-string">"Order confirmed", {
traceId,
orderId: class="syntax-string">"order_456",
});Info
traceId can be correlated in the dashboard, giving you a complete picture of the operation.Custom Spans
Use the context field to create span-like entries that measure the duration of specific operations:
class="syntax-comment">// Measure an async operation
async function processOrder(orderId: string) {
const traceId = crypto.randomUUID();
const start = performance.now();
Apperio.debug(class="syntax-string">"Order processing started", {
traceId,
orderId,
spanName: class="syntax-string">"process-order",
spanPhase: class="syntax-string">"start",
});
try {
await validateInventory(orderId);
await chargePayment(orderId);
await createShipment(orderId);
const duration = performance.now() - start;
Apperio.info(class="syntax-string">"Order processing completed", {
traceId,
orderId,
spanName: class="syntax-string">"process-order",
spanPhase: class="syntax-string">"end",
duration: Math.round(duration),
});
} catch (err) {
const duration = performance.now() - start;
Apperio.error(class="syntax-string">"Order processing failed", {
traceId,
orderId,
error: err,
spanName: class="syntax-string">"process-order",
spanPhase: class="syntax-string">"error",
duration: Math.round(duration),
});
throw err;
}
}
class="syntax-comment">// Helper for timing individual steps
async function withSpan(name: string, traceId: string, fn: () => Promise<void>) {
const start = performance.now();
try {
await fn();
Apperio.debug(name + class="syntax-string">" completed", {
traceId,
spanName: name,
duration: Math.round(performance.now() - start),
});
} catch (err) {
Apperio.error(name + class="syntax-string">" failed", {
traceId,
spanName: name,
duration: Math.round(performance.now() - start),
error: err,
});
throw err;
}
}Automatic Tracing
The SDK automatically includes trace-like context with auto-instrumented events:
- Network requests include request duration and response timing
- Page loads include navigation timing data
- Performance entries include measurement timestamps
// Auto-captured network request with timing:
{
"level": "info",
"message": "POST /api/orders 201",
"eventType": "network",
"responseTime": 245,
"data": {
"method": "POST",
"url": "/api/orders",
"status": 201,
"duration": 245,
"initiator": "fetch"
}
}Distributed Tracing
For applications with a backend that also uses Apperio, you can propagate trace context across the network boundary:
class="syntax-comment">// Frontend: Pass trace ID in request headers
const traceId = crypto.randomUUID();
Apperio.info(class="syntax-string">"Initiating API call", { traceId });
const response = await fetch(class="syntax-string">"/api/process", {
headers: {
class="syntax-string">"X-Trace-Id": traceId,
class="syntax-string">"Content-Type": class="syntax-string">"application/json",
},
body: JSON.stringify({ data: class="syntax-string">"..." }),
});
Apperio.info(class="syntax-string">"API call completed", {
traceId,
status: response.status,
});class="syntax-comment">// Backend: Read trace ID from incoming request
app.post(class="syntax-string">"/api/process", (req, res) => {
const traceId = req.headers[class="syntax-string">"x-trace-id"];
class="syntax-comment">// Use the same trace ID in backend logs
logger.info(class="syntax-string">"Processing request", {
traceId,
step: class="syntax-string">"validation",
});
class="syntax-comment">// Continue propagating to downstream services...
});Viewing Traces
In the Apperio dashboard, you can filter logs by trace ID to see all events in a single trace. Use the log detail view to examine timing data and identify bottlenecks.
- Search by
traceIdin the log stream filter - Sort by timestamp to see the chronological flow
- Compare
durationvalues across spans to find slow operations
Tip