Transactional

Context

Enrich errors with user info, tags, request data, and custom context for better debugging.

Overview

Context is additional information attached to errors that helps with debugging. It includes:

  • User Context - Who experienced the error
  • Tags - Key-value metadata for filtering
  • Extra Data - Arbitrary debugging information
  • Request Context - HTTP request details
  • Device/OS Context - Environment information

User Context

Track which users are affected by errors:

Setting User Context

import { getObservability } from '@transactional/observability';
 
const obs = getObservability();
 
// Set user after authentication
obs.setUser({
  id: user.id,                    // Required: unique identifier
  email: user.email,              // Optional
  username: user.username,        // Optional
  name: user.displayName,         // Optional
});

Clearing User Context

// Clear on logout
obs.setUser(null);

User Context on Capture

Override global user context per-error:

obs.captureException(error, {
  user: {
    id: 'user-456',
    email: 'specific@example.com',
  },
});

User Count Tracking

When user context is set, issues track unique affected users:

Issue: "TypeError: Cannot read property 'name' of undefined"
├── Occurrences: 156
└── Users Affected: 23  ← Unique users

Tags

Tags are key-value pairs for filtering and searching errors:

Setting Global Tags

// Set tags that apply to all errors
obs.setTags({
  environment: 'production',
  version: '1.2.3',
  region: 'us-east-1',
  team: 'payments',
});

Setting Tags on Capture

obs.captureException(error, {
  tags: {
    feature: 'checkout',
    paymentProvider: 'stripe',
    retryCount: '3',
  },
});

Common Tags

TagDescriptionExample
environmentDeploy environmentproduction, staging
versionApp version1.2.3
featureFeature areacheckout, auth
teamOwning teampayments, growth
regionServer regionus-east-1
browserBrowser namechrome, safari
osOperating systemwindows, macos

Tag Limits

LimitValue
Max tags per error50
Max key length32 characters
Max value length200 characters

Extra Data

Extra data is arbitrary JSON for debugging:

Setting Global Extra

obs.setExtra({
  featureFlags: {
    newCheckout: true,
    darkMode: false,
  },
  abTests: {
    pricing: 'variant-b',
  },
});

Setting Extra on Capture

obs.captureException(error, {
  extra: {
    orderId: order.id,
    cartItems: cart.items,
    totalAmount: cart.total,
    paymentAttempts: 3,
    lastError: previousError.message,
  },
});

Extra Data Best Practices

// Good - relevant debugging info
obs.captureException(error, {
  extra: {
    userId: user.id,
    action: 'checkout',
    cartTotal: 99.99,
  },
});
 
// Bad - too much data, sensitive info
obs.captureException(error, {
  extra: {
    fullUserObject: user,           // Too much
    creditCardNumber: card.number,  // Sensitive!
    entireResponse: response,       // Too large
  },
});

Extra Limits

LimitValue
Max extra size16 KB
Max depth10 levels
Max keys100

Request Context

Capture HTTP request details for server-side errors:

Automatic Request Context

For Express/Hono, request context is captured automatically:

// Express middleware
app.use((req, res, next) => {
  const obs = getObservability();
  obs.setRequestContext(req);
  next();
});

Manual Request Context

obs.captureException(error, {
  request: {
    url: request.url,
    method: request.method,
    headers: {
      'content-type': request.headers['content-type'],
      'user-agent': request.headers['user-agent'],
      // Don't include Authorization header!
    },
    queryString: request.query,
    data: request.body, // Sanitize sensitive fields
  },
});

Request Context Structure

interface RequestContext {
  url?: string;           // Full URL or path
  method?: string;        // HTTP method
  headers?: Record<string, string>;
  queryString?: string | Record<string, string>;
  data?: unknown;         // Request body
  cookies?: Record<string, string>;  // Sanitize!
  env?: Record<string, string>;      // Server env vars
}

Sensitive Data Scrubbing

initObservability({
  dsn: 'your-dsn',
  enableErrorTracking: true,
 
  // Headers to exclude
  scrubHeaders: [
    'Authorization',
    'Cookie',
    'X-API-Key',
  ],
 
  // Body fields to scrub
  scrubFields: [
    'password',
    'token',
    'secret',
    'creditCard',
  ],
});

Device/OS Context

Environment information about the client:

Automatic Device Context

The SDK automatically captures:

{
  device: {
    name: 'iPhone 14 Pro',
    family: 'iPhone',
    model: 'iPhone14,2',
  },
  os: {
    name: 'iOS',
    version: '17.0',
  },
  browser: {
    name: 'Safari',
    version: '17.0',
  },
  runtime: {
    name: 'node',
    version: '20.0.0',
  },
}

Manual Device Context

obs.setContext('device', {
  name: 'Custom Device',
  model: 'Model X',
});
 
obs.setContext('os', {
  name: 'CustomOS',
  version: '1.0',
});

Custom Contexts

Create custom context types for your application:

Setting Custom Contexts

// Set application-specific context
obs.setContext('subscription', {
  plan: 'pro',
  billingCycle: 'monthly',
  trialEndsAt: '2024-02-15',
});
 
obs.setContext('organization', {
  id: 'org-123',
  name: 'Acme Corp',
  tier: 'enterprise',
});
 
obs.setContext('feature', {
  name: 'checkout-v2',
  variant: 'treatment',
  enabled: true,
});

Context on Capture

obs.captureException(error, {
  contexts: {
    order: {
      id: order.id,
      status: order.status,
      total: order.total,
    },
    payment: {
      provider: 'stripe',
      method: 'card',
      last4: '4242',
    },
  },
});

Environment and Release

Track deployment information:

Environment

initObservability({
  dsn: 'your-dsn',
  environment: process.env.NODE_ENV, // 'production', 'staging', 'development'
});

Release

initObservability({
  dsn: 'your-dsn',
  release: `${process.env.APP_NAME}@${process.env.APP_VERSION}`,
  // Example: "my-app@1.2.3"
});

Deployment Context

obs.setContext('deployment', {
  release: '1.2.3',
  commit: 'abc123',
  deployedAt: '2024-01-15T10:00:00Z',
  deployedBy: 'ci/cd',
});

Setting Context Globally vs Per-Error

Global Context

Set once, applies to all errors:

// At app initialization
obs.setUser({ id: user.id, email: user.email });
obs.setTags({ environment: 'production', version: '1.2.3' });
obs.setExtra({ featureFlags: flags });
obs.setContext('subscription', subscription);

Per-Error Context

Override global context for specific errors:

obs.captureException(error, {
  // Overrides global user
  user: { id: 'different-user' },
 
  // Merges with global tags
  tags: { specific: 'tag' },
 
  // Overrides global extra
  extra: { specificData: 'value' },
 
  // Adds to global contexts
  contexts: {
    specific: { data: 'value' },
  },
});

Clearing Context

Clear All

obs.clearContext();

Clear Specific

obs.setUser(null);
obs.setTags({});
obs.setExtra({});
obs.setContext('subscription', null);

Viewing Context in Dashboard

Issue List

Filter issues by tags:

  • Environment: production/staging
  • Version: 1.2.3
  • Feature: checkout

Issue Detail

View full context:

  • User tab - User info and affected count
  • Tags tab - All tags
  • Context tab - Extra data and custom contexts
  • Request tab - HTTP request details
  • Device tab - Browser, OS, device info

Best Practices

1. Set User Context Early

// After authentication
onAuthSuccess((user) => {
  obs.setUser({
    id: user.id,
    email: user.email,
  });
});

2. Use Tags for Filtering

// Tags you'll filter by in dashboard
obs.setTags({
  environment: process.env.NODE_ENV,
  version: process.env.APP_VERSION,
  team: 'payments',
});

3. Add Context at Key Points

// When user enters a flow
function startCheckout(cart: Cart) {
  obs.setContext('cart', {
    itemCount: cart.items.length,
    total: cart.total,
  });
}

4. Don't Include Sensitive Data

// Bad
obs.setExtra({ creditCard: card.number });
 
// Good
obs.setExtra({ cardLast4: card.number.slice(-4) });

5. Keep Context Relevant

// Good - relevant to debugging
obs.captureException(error, {
  extra: {
    userId: user.id,
    action: 'purchase',
  },
});
 
// Bad - irrelevant noise
obs.captureException(error, {
  extra: {
    randomNumber: Math.random(),
    currentTime: Date.now(),
  },
});

Next Steps