Spans
Create and manage spans to instrument your application code.
What is a Span?#
A span represents a single unit of work within a trace. Spans have:
- Name -- what operation was performed (e.g.,
GET /api/users) - Duration -- how long the operation took
- Status -- success or error
- Attributes -- key-value metadata
- Events -- timestamped annotations within the span
- Parent -- the span that initiated this work (if any)
Creating Spans#
Basic Span#
import JA from '@justanalyticsapp/node';
const result = JA.startSpan('process-data', (span) => {
span.setAttribute('items.count', data.length);
return transformData(data);
});
Async Span#
const user = await JA.startSpan('fetch-user', async (span) => {
span.setAttribute('user.id', userId);
const result = await db.users.findUnique({ where: { id: userId } });
if (!result) {
span.setStatus('not_found');
}
return result;
});
Span with Options#
const response = await JA.startSpan(
'external-api-call',
{ kind: 'client', attributes: { 'http.method': 'POST' } },
async (span) => {
const res = await fetch('https://api.stripe.com/charges', {
method: 'POST',
body: JSON.stringify(payload),
});
span.setAttribute('http.status_code', res.status);
return res;
}
);
Span Kinds#
| Kind | Description | Example |
|------|-------------|---------|
| internal | Default. Internal operation | Business logic, data transformation |
| server | Server handling a request | HTTP request handler |
| client | Client making a request | Database query, HTTP call |
Attributes#
Attach metadata to spans for filtering and analysis:
JA.startSpan('checkout', (span) => {
span.setAttribute('cart.items', 3);
span.setAttribute('cart.total', 149.99);
span.setAttribute('user.plan', 'pro');
span.setAttribute('payment.method', 'stripe');
return processCheckout();
});
Semantic Conventions#
Use standard attribute names for consistency:
| Attribute | Description |
|-----------|-------------|
| http.method | HTTP method (GET, POST, etc.) |
| http.status_code | HTTP response status code |
| http.url | Full request URL |
| db.system | Database type (postgresql, redis) |
| db.statement | Database query |
| user.id | User identifier |
| error.type | Error class name |
| error.message | Error message |
Span Events#
Add timestamped events within a span:
JA.startSpan('process-payment', async (span) => {
span.addEvent('validation_start');
await validateCard(card);
span.addEvent('validation_complete');
span.addEvent('charge_start', { amount: 99.99 });
const result = await chargeCard(card, 99.99);
span.addEvent('charge_complete', { transactionId: result.id });
return result;
});
Nested Spans#
Spans automatically nest when created inside other spans:
await JA.startSpan('handle-request', async () => {
// Child span 1
const user = await JA.startSpan('auth.verify-token', async () => {
return await verifyJWT(token);
});
// Child span 2
const data = await JA.startSpan('db.fetch-orders', async () => {
return await db.orders.findMany({ where: { userId: user.id } });
});
// Child span 3
return JA.startSpan('response.serialize', () => {
return serializeOrders(data);
});
});
Error Handling#
Spans automatically record errors thrown within them:
try {
await JA.startSpan('risky-operation', async (span) => {
const result = await dangerousFunction();
return result;
// If dangerousFunction throws, the span is marked as error
// and the exception details are attached
});
} catch (error) {
// Error is re-thrown after being captured
console.error('Operation failed:', error);
}