Transactional

Breadcrumbs

Track the sequence of user actions and events that lead to errors.

Overview

Breadcrumbs are a trail of events that happened before an error occurred. They help you understand the user journey and reproduce issues.

User Journey:
├── [navigation] Loaded /dashboard
├── [ui] Clicked "Settings" button
├── [http] GET /api/user/settings (200)
├── [ui] Toggled "Dark Mode" switch
├── [http] POST /api/user/settings (500) ← API failed
└── [error] TypeError: Cannot read 'theme' of undefined
interface Breadcrumb {
  type?: 'default' | 'http' | 'navigation' | 'ui' | 'user' | 'console';
  category?: string;      // Subcategory (e.g., 'fetch', 'xhr', 'click')
  message?: string;       // Human-readable description
  data?: Record<string, unknown>;  // Additional data
  level?: 'fatal' | 'error' | 'warning' | 'info' | 'debug';
  timestamp?: string;     // ISO timestamp
}
TypeDescriptionExamples
defaultGeneral eventsCustom application events
httpNetwork requestsAPI calls, fetch, XHR
navigationPage navigationRoute changes, page loads
uiUser interactionsClicks, form inputs
userUser actionsLogin, signup, custom actions
consoleConsole outputconsole.log, console.error

Automatic Breadcrumbs

The SDK captures breadcrumbs automatically:

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  breadcrumbs: {
    // Capture console.log/warn/error (default: true)
    console: true,
 
    // Capture DOM clicks (default: true)
    dom: true,
 
    // Capture fetch/XHR requests (default: true)
    fetch: true,
    xhr: true,
 
    // Capture history/location changes (default: true)
    history: true,
  },
});

HTTP Breadcrumbs

Automatically captured for fetch and XHR:

// Captured automatically:
{
  type: 'http',
  category: 'fetch',
  message: 'GET /api/users',
  data: {
    method: 'GET',
    url: '/api/users',
    statusCode: 200,
    duration: 145,
  },
  level: 'info',
  timestamp: '2024-01-15T10:30:00.000Z',
}

Captured on route changes:

// Captured automatically:
{
  type: 'navigation',
  category: 'navigation',
  message: 'Navigation to /dashboard/settings',
  data: {
    from: '/dashboard',
    to: '/dashboard/settings',
  },
  level: 'info',
  timestamp: '2024-01-15T10:30:01.000Z',
}

UI Breadcrumbs

Captured on DOM interactions:

// Captured automatically:
{
  type: 'ui',
  category: 'ui.click',
  message: 'button.submit-btn',
  data: {
    target: 'button.submit-btn',
    text: 'Submit',
  },
  level: 'info',
  timestamp: '2024-01-15T10:30:02.000Z',
}

Console Breadcrumbs

Captured from console methods:

console.log('User selected plan:', selectedPlan);
 
// Captured automatically:
{
  type: 'console',
  category: 'console',
  message: "User selected plan: pro",
  level: 'info',
  timestamp: '2024-01-15T10:30:03.000Z',
}

Manual Breadcrumbs

Add custom breadcrumbs for important application events:

addBreadcrumb()

import { getObservability } from '@transactional/observability';
 
const obs = getObservability();
 
// Simple breadcrumb
obs.addBreadcrumb({
  message: 'User started checkout flow',
  category: 'checkout',
});
 
// Full breadcrumb with all options
obs.addBreadcrumb({
  type: 'user',
  category: 'checkout',
  message: 'User added item to cart',
  level: 'info',
  data: {
    productId: 'prod-123',
    productName: 'Widget Pro',
    quantity: 2,
    price: 29.99,
  },
});

Common Use Cases

User Authentication

function onLogin(user: User) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'auth',
    message: 'User logged in',
    level: 'info',
    data: {
      userId: user.id,
      method: 'email',
    },
  });
}
 
function onLogout() {
  obs.addBreadcrumb({
    type: 'user',
    category: 'auth',
    message: 'User logged out',
    level: 'info',
  });
}

E-commerce Flow

function addToCart(product: Product) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'cart',
    message: `Added ${product.name} to cart`,
    data: {
      productId: product.id,
      price: product.price,
    },
  });
}
 
function startCheckout(cart: Cart) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'checkout',
    message: 'Started checkout',
    data: {
      itemCount: cart.items.length,
      total: cart.total,
    },
  });
}
 
function completePayment(orderId: string) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'checkout',
    message: 'Payment completed',
    data: { orderId },
  });
}

Feature Usage

function onFeatureEnabled(feature: string) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'feature',
    message: `Enabled feature: ${feature}`,
    data: { feature },
  });
}
 
function onSettingChanged(setting: string, value: unknown) {
  obs.addBreadcrumb({
    type: 'user',
    category: 'settings',
    message: `Changed ${setting}`,
    data: { setting, value },
  });
}

Form Interactions

function onFormStep(step: number, totalSteps: number) {
  obs.addBreadcrumb({
    type: 'ui',
    category: 'form',
    message: `Form step ${step}/${totalSteps}`,
    data: { step, totalSteps },
  });
}
 
function onFormValidationError(field: string, error: string) {
  obs.addBreadcrumb({
    type: 'ui',
    category: 'form.validation',
    message: `Validation error on ${field}`,
    level: 'warning',
    data: { field, error },
  });
}

To prevent excessive data, breadcrumbs have limits:

SettingDefaultMax
Max breadcrumbs per event100250
Max message length10002000
Max data size4KB16KB

Configuring Limits

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  breadcrumbs: {
    maxBreadcrumbs: 150,
    maxMessageLength: 1500,
  },
});

When the limit is reached, oldest breadcrumbs are removed.

Filtering Breadcrumbs

Before Breadcrumb Hook

Filter or modify breadcrumbs before they're recorded:

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  beforeBreadcrumb: (breadcrumb) => {
    // Don't record console.debug breadcrumbs
    if (breadcrumb.category === 'console' && breadcrumb.level === 'debug') {
      return null;
    }
 
    // Scrub sensitive data from URLs
    if (breadcrumb.type === 'http' && breadcrumb.data?.url) {
      breadcrumb.data.url = scrubSensitiveParams(breadcrumb.data.url);
    }
 
    // Don't record health check requests
    if (breadcrumb.data?.url?.includes('/health')) {
      return null;
    }
 
    return breadcrumb;
  },
});

Disabling Specific Types

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  breadcrumbs: {
    console: false,  // Disable console breadcrumbs
    dom: true,
    fetch: true,
    xhr: true,
    history: true,
  },
});

Sensitive Data

Be careful not to include sensitive data in breadcrumbs:

// Bad - includes password
obs.addBreadcrumb({
  message: 'Login attempt',
  data: {
    email: user.email,
    password: user.password, // Don't do this!
  },
});
 
// Good - sanitized
obs.addBreadcrumb({
  message: 'Login attempt',
  data: {
    email: user.email,
    // Password omitted
  },
});

Automatic Scrubbing

The SDK automatically scrubs common sensitive fields:

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  // Fields to automatically scrub (default list)
  sensitiveFields: [
    'password',
    'secret',
    'token',
    'apiKey',
    'api_key',
    'authorization',
    'creditCard',
    'credit_card',
    'ssn',
  ],
});

Viewing Breadcrumbs

In the Dashboard

  1. Go to Issues and click an issue
  2. Click the Breadcrumbs tab
  3. View the timeline of events before the error

Timeline View

Timeline:
10:30:00.000  [navigation] Loaded /checkout
10:30:01.234  [http] GET /api/cart (200)
10:30:02.456  [ui] Clicked "Apply Coupon"
10:30:03.789  [http] POST /api/coupon/validate (400)
10:30:04.012  [ui] Clicked "Complete Purchase"
10:30:04.567  [http] POST /api/checkout (500) ← Error
10:30:04.890  [error] PaymentError: Card declined

Filtering

Filter breadcrumbs by:

  • Type (http, navigation, ui, etc.)
  • Level (error, warning, info)
  • Category
  • Time range

Best Practices

1. Add Breadcrumbs at Key Points

// User journey milestones
obs.addBreadcrumb({ message: 'Signed up' });
obs.addBreadcrumb({ message: 'Completed onboarding' });
obs.addBreadcrumb({ message: 'First purchase' });

2. Include Relevant Data

// Include data that helps debugging
obs.addBreadcrumb({
  message: 'Search performed',
  data: {
    query: searchQuery,
    resultsCount: results.length,
    filters: activeFilters,
  },
});

3. Use Appropriate Levels

// Info for normal events
obs.addBreadcrumb({ message: 'Page loaded', level: 'info' });
 
// Warning for potential issues
obs.addBreadcrumb({ message: 'Retry attempt 2/3', level: 'warning' });
 
// Error for failures (before the main error)
obs.addBreadcrumb({ message: 'API call failed', level: 'error' });

4. Be Consistent with Categories

// Use consistent category naming
const CATEGORIES = {
  AUTH: 'auth',
  CART: 'cart',
  CHECKOUT: 'checkout',
  NAVIGATION: 'navigation',
  API: 'api',
};
 
obs.addBreadcrumb({
  category: CATEGORIES.AUTH,
  message: 'Login successful',
});

Next Steps