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);
}