Skip to main content
Spans represent units of work in your application and are the building blocks of distributed tracing.

Overview

Spans track:
  • Operation timing (start and end)
  • Operation metadata (name, description)
  • Attributes for filtering and analysis
  • Parent-child relationships
  • Status and outcome

Creating Spans

startSpan

Create an active span that automatically becomes the parent of nested spans.
import * as Sentry from '@sentry/node';

const result = Sentry.startSpan(
  { name: 'Operation', op: 'function' },
  (span) => {
    // Span is automatically finished when callback completes
    return performOperation();
  }
);
options
StartSpanOptions
required
Configuration for the span.
name
string
required
The span description.
op
string
The span operation (e.g., 'http.client', 'db.query').
attributes
SpanAttributes
Additional span attributes.
startTime
SpanTimeInput
Custom start time.
scope
Scope
Custom scope to use.
parentSpan
Span
Explicit parent span.
onlyIfParent
boolean
Only create span if there’s a parent span.
callback
function
required
Function to execute within the span context. Receives the span as argument.
Example:
import * as Sentry from '@sentry/node';

const data = await Sentry.startSpan(
  {
    name: 'fetchUserData',
    op: 'http.client',
    attributes: {
      'http.method': 'GET',
      'http.url': '/api/users'
    }
  },
  async (span) => {
    const response = await fetch('/api/users');
    span.setAttribute('http.status_code', response.status);
    return response.json();
  }
);

startInactiveSpan

Create an inactive span that doesn’t automatically become the active parent.
import * as Sentry from '@sentry/node';

const span = Sentry.startInactiveSpan({
  name: 'Background Task',
  op: 'task'
});

try {
  await performTask();
  span.setStatus({ code: 1 }); // OK
} catch (error) {
  span.setStatus({ code: 2 }); // ERROR
  throw error;
} finally {
  span.end();
}

startSpanManual

Create a span with manual control over when it finishes.
import * as Sentry from '@sentry/node';

const result = Sentry.startSpanManual(
  { name: 'Long Running Operation', op: 'task' },
  (span, finish) => {
    someAsyncOperation().then(() => {
      finish(); // Manually finish the span
    });
    return result;
  }
);

Span Interface

From packages/core/src/types-hoist/span.ts:
export interface Span {
  spanContext(): SpanContextData;
  end(endTimestamp?: SpanTimeInput): void;
  setAttribute(key: string, value: SpanAttributeValue | undefined): this;
  setAttributes(attributes: SpanAttributes): this;
  setStatus(status: SpanStatus): this;
  updateName(name: string): this;
  isRecording(): boolean;
}

spanContext()

Get context data for the span.
const context = span.spanContext();
console.log(context.traceId); // Trace ID
console.log(context.spanId);  // Span ID
traceId
string
32-character hex string representing the trace.
spanId
string
16-character hex string representing the span.
traceFlags
number
Trace flags (1 = sampled, 0 = not sampled).

setAttribute()

Set a single attribute on the span.
span.setAttribute('http.method', 'POST');
span.setAttribute('http.status_code', 200);
span.setAttribute('db.system', 'postgresql');

setAttributes()

Set multiple attributes at once.
span.setAttributes({
  'http.method': 'GET',
  'http.url': '/api/users',
  'http.status_code': 200,
  'user.id': '12345'
});

setStatus()

Set the span status.
import { SPAN_STATUS_OK, SPAN_STATUS_ERROR } from '@sentry/core';

// Success
span.setStatus({ code: SPAN_STATUS_OK });

// Error
span.setStatus({ 
  code: SPAN_STATUS_ERROR, 
  message: 'Connection timeout' 
});
Status Codes:
  • 0 - Unset
  • 1 - OK
  • 2 - Error

updateName()

Update the span name.
span.updateName('GET /api/users/123');
Note: Use Sentry.updateSpanName(span, name) to ensure the name persists through instrumentation.

end()

End the span.
// End with current timestamp
span.end();

// End with custom timestamp
span.end(Date.now());

isRecording()

Check if the span is recording.
if (span.isRecording()) {
  span.setAttribute('debug', 'true');
}

Span Attributes

export type SpanAttributeValue =
  | string
  | number
  | boolean
  | Array<null | undefined | string>
  | Array<null | undefined | number>
  | Array<null | undefined | boolean>;

export type SpanAttributes = Partial<{
  'sentry.origin': string;
  'sentry.op': string;
  'sentry.source': TransactionSource;
  'sentry.sample_rate': number;
}> & Record<string, SpanAttributeValue | undefined>;

Semantic Attributes

Use semantic conventions for consistency: HTTP Attributes:
span.setAttributes({
  'http.method': 'GET',
  'http.url': 'https://api.example.com/users',
  'http.status_code': 200,
  'http.request.body.size': 1024,
  'http.response.body.size': 4096
});
Database Attributes:
span.setAttributes({
  'db.system': 'postgresql',
  'db.name': 'myapp',
  'db.statement': 'SELECT * FROM users WHERE id = $1',
  'db.operation': 'SELECT'
});
Custom Attributes:
span.setAttributes({
  'user.id': '123',
  'feature.flag': 'new-ui',
  'cache.hit': true,
  'retry.count': 3
});

Nested Spans

Create parent-child relationships:
import * as Sentry from '@sentry/node';

await Sentry.startSpan(
  { name: 'Parent Operation', op: 'task' },
  async (parentSpan) => {
    // This span is automatically a child of parentSpan
    await Sentry.startSpan(
      { name: 'Child Operation 1', op: 'http' },
      async () => {
        await fetch('/api/endpoint1');
      }
    );
    
    await Sentry.startSpan(
      { name: 'Child Operation 2', op: 'db' },
      async () => {
        await db.query('SELECT * FROM users');
      }
    );
  }
);

Error Handling

import * as Sentry from '@sentry/node';

await Sentry.startSpan(
  { name: 'Operation', op: 'task' },
  async (span) => {
    try {
      await riskyOperation();
      span.setStatus({ code: 1 }); // OK
    } catch (error) {
      span.setStatus({ 
        code: 2, 
        message: error.message 
      });
      // Error is automatically captured
      throw error;
    }
  }
);

Async Operations

import * as Sentry from '@sentry/node';

// Sequential operations
await Sentry.startSpan(
  { name: 'Process Order', op: 'task' },
  async () => {
    await Sentry.startSpan({ name: 'Validate', op: 'validation' }, () => 
      validateOrder(order)
    );
    
    await Sentry.startSpan({ name: 'Process Payment', op: 'payment' }, () => 
      processPayment(order)
    );
    
    await Sentry.startSpan({ name: 'Send Confirmation', op: 'email' }, () => 
      sendEmail(order)
    );
  }
);

// Parallel operations
await Sentry.startSpan(
  { name: 'Fetch Data', op: 'task' },
  async () => {
    await Promise.all([
      Sentry.startSpan({ name: 'Fetch Users', op: 'http' }, () => 
        fetchUsers()
      ),
      Sentry.startSpan({ name: 'Fetch Products', op: 'http' }, () => 
        fetchProducts()
      ),
      Sentry.startSpan({ name: 'Fetch Orders', op: 'http' }, () => 
        fetchOrders()
      )
    ]);
  }
);

Getting Active Span

import * as Sentry from '@sentry/node';

Sentry.startSpan({ name: 'Parent', op: 'task' }, () => {
  const activeSpan = Sentry.getActiveSpan();
  
  if (activeSpan) {
    console.log('Active span:', activeSpan.spanContext().spanId);
    activeSpan.setAttribute('custom', 'value');
  }
  
  // Nested operation
  Sentry.startSpan({ name: 'Child', op: 'subtask' }, () => {
    const childSpan = Sentry.getActiveSpan();
    // childSpan is now the active span
  });
});

Span JSON

Spans are serialized as:
export interface SpanJSON {
  data: SpanAttributes;
  description?: string;
  op?: string;
  parent_span_id?: string;
  span_id: string;
  start_timestamp: number;
  status?: string;
  timestamp?: number;
  trace_id: string;
  origin?: SpanOrigin;
  profile_id?: string;
  exclusive_time?: number;
  measurements?: Measurements;
  is_segment?: boolean;
  segment_id?: string;
}

Best Practices

1. Use Descriptive Names

// Bad
Sentry.startSpan({ name: 'op', op: 'task' }, () => {});

// Good
Sentry.startSpan({ name: 'Process Payment', op: 'payment' }, () => {});

2. Set Appropriate Operations

// HTTP requests
{ name: 'GET /api/users', op: 'http.client' }

// Database queries  
{ name: 'SELECT users', op: 'db.query' }

// Function calls
{ name: 'processOrder', op: 'function' }

// Background tasks
{ name: 'Send Emails', op: 'task' }

3. Add Relevant Attributes

Sentry.startSpan(
  {
    name: 'Database Query',
    op: 'db.query',
    attributes: {
      'db.system': 'postgresql',
      'db.statement': query,
      'db.name': 'production'
    }
  },
  () => executeQuery()
);

4. Handle Errors Properly

await Sentry.startSpan({ name: 'Operation' }, async (span) => {
  try {
    await operation();
  } catch (error) {
    span.setStatus({ code: 2, message: error.message });
    throw error;
  }
});

Build docs developers (and LLMs) love