Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/getsentry/sentry-javascript/llms.txt

Use this file to discover all available pages before exploring further.

The OpenAI integration automatically instruments OpenAI SDK calls to capture performance data and errors.

Installation

The integration is enabled by default:
import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: 'your-dsn',
  // openAIIntegration is included by default
});

Basic Usage

Just use the OpenAI SDK normally:
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// Automatically instrumented
const completion = await openai.chat.completions.create({
  model: 'gpt-4',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is the capital of France?' },
  ],
});

Configuration

Default Behavior

By default, prompts and responses are not captured:
Sentry.init({
  dsn: 'your-dsn',
  sendDefaultPii: false, // Default: no prompts/responses
});

Capture Prompts and Responses

Enable for all AI integrations:
Sentry.init({
  dsn: 'your-dsn',
  sendDefaultPii: true, // Captures all inputs/outputs
});

Integration Options

recordInputs
boolean
default:"sendDefaultPii"
Capture prompt messages
recordOutputs
boolean
default:"sendDefaultPii"
Capture completion responses

Captured Data

Always Captured

These attributes are always included:
{
  'gen_ai.operation.name': 'chat',
  'gen_ai.request.model': 'gpt-4',
  'gen_ai.system': 'openai',
  'gen_ai.usage.input_tokens': 25,
  'gen_ai.usage.output_tokens': 100,
  'gen_ai.response.finish_reasons': ['stop'],
  'gen_ai.request.temperature': 1.0,
  'gen_ai.request.max_tokens': 256,
}

When recordInputs: true

Prompt messages are captured:
{
  'gen_ai.prompt.0.role': 'system',
  'gen_ai.prompt.0.content': 'You are a helpful assistant.',
  'gen_ai.prompt.1.role': 'user',
  'gen_ai.prompt.1.content': 'What is the capital of France?',
}

When recordOutputs: true

Completion responses are captured:
{
  'gen_ai.completion.0.role': 'assistant',
  'gen_ai.completion.0.content': 'The capital of France is Paris.',
  'gen_ai.completion.0.finish_reason': 'stop',
}

Supported Operations

The integration instruments all OpenAI operations:

Chat Completions

const completion = await openai.chat.completions.create({
  model: 'gpt-4',
  messages: [{ role: 'user', content: 'Hello!' }],
});
// Span: gen_ai.chat.completions

Streaming Completions

const stream = await openai.chat.completions.create({
  model: 'gpt-4',
  messages: [{ role: 'user', content: 'Hello!' }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content || '');
}
// Span includes full streaming response

Embeddings

const embedding = await openai.embeddings.create({
  model: 'text-embedding-ada-002',
  input: 'Your text here',
});
// Span: gen_ai.embeddings.create

Function Calling

const completion = await openai.chat.completions.create({
  model: 'gpt-4',
  messages: [{ role: 'user', content: 'What is the weather?' }],
  functions: [
    {
      name: 'get_weather',
      description: 'Get current weather',
      parameters: {
        type: 'object',
        properties: {
          location: { type: 'string' },
        },
      },
    },
  ],
});
// Tool calls captured in span attributes

Practical Examples

Customer Support Bot

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

const openai = new OpenAI();

async function answerSupportQuery(userId, query) {
  return await Sentry.startSpan(
    {
      name: 'Answer Support Query',
      op: 'ai.support',
      attributes: {
        'user.id': userId,
        'support.category': 'general',
      },
    },
    async () => {
      const completion = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [
          {
            role: 'system',
            content: 'You are a helpful customer support agent.',
          },
          { role: 'user', content: query },
        ],
      });
      
      return completion.choices[0].message.content;
    }
  );
}

Content Generation

async function generateBlogPost(topic, keywords) {
  return await Sentry.startSpan(
    {
      name: 'Generate Blog Post',
      op: 'ai.content_generation',
      attributes: {
        'content.topic': topic,
        'content.keywords': keywords.join(', '),
      },
    },
    async () => {
      const completion = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [
          {
            role: 'system',
            content: 'You are a professional content writer.',
          },
          {
            role: 'user',
            content: `Write a blog post about ${topic} including these keywords: ${keywords.join(', ')}`,
          },
        ],
        temperature: 0.8,
        max_tokens: 2000,
      });
      
      return completion.choices[0].message.content;
    }
  );
}

Semantic Search with Embeddings

async function searchDocuments(query, documents) {
  return await Sentry.startSpan(
    { name: 'Semantic Search', op: 'ai.search' },
    async () => {
      // Get query embedding
      const queryEmbedding = await openai.embeddings.create({
        model: 'text-embedding-ada-002',
        input: query,
      });
      
      // Compare with document embeddings
      const results = findSimilarDocuments(
        queryEmbedding.data[0].embedding,
        documents
      );
      
      return results;
    }
  );
}

Error Handling

async function generateWithRetry(prompt, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const completion = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: prompt }],
      });
      
      return completion.choices[0].message.content;
    } catch (error) {
      Sentry.captureException(error, {
        contexts: {
          openai: {
            attempt,
            max_retries: maxRetries,
            model: 'gpt-4',
          },
        },
      });
      
      if (attempt === maxRetries) {
        throw error;
      }
      
      // Exponential backoff
      await new Promise(resolve => setTimeout(resolve, 2 ** attempt * 1000));
    }
  }
}

Performance Monitoring

View OpenAI performance in Sentry:
  • Response Times: Track API latency
  • Token Usage: Monitor prompt and completion tokens
  • Error Rates: Identify rate limits and failures
  • Model Comparison: Compare performance across models

Source Code

The OpenAI integration is implemented in: packages/node/src/integrations/tracing/openai/index.ts:11

Privacy Best Practices

Be careful about capturing sensitive user data in prompts and responses.

Filter Sensitive Content

Sentry.init({
  dsn: 'your-dsn',
  sendDefaultPii: true,
  
  beforeSendSpan(span) {
    // Remove PII from prompts
    const promptContent = span.attributes?.['gen_ai.prompt.1.content'];
    if (promptContent && typeof promptContent === 'string') {
      span.attributes['gen_ai.prompt.1.content'] = promptContent
        .replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[EMAIL]')
        .replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]');
    }
    
    return span;
  },
});

Conditional Capture

const isSensitiveQuery = query.toLowerCase().includes('password');

const integration = isSensitiveQuery
  ? Sentry.openAIIntegration({ recordInputs: false, recordOutputs: false })
  : Sentry.openAIIntegration({ recordInputs: true, recordOutputs: true });

Troubleshooting

Spans Not Appearing

Ensure tracing is enabled:
Sentry.init({
  dsn: 'your-dsn',
  tracesSampleRate: 1.0, // Capture 100% of traces
});

Missing Token Data

Token usage is always captured from OpenAI’s response:
// Check the span attributes in Sentry for:
// - gen_ai.usage.input_tokens
// - gen_ai.usage.output_tokens

Build docs developers (and LLMs) love