Skip to content

Direct OTEL SDK-to-Sentry

There are multiple ways to send OTLP data to Sentry:

  • Direct OTEL SDK → Sentry (this guide) - Simplest setup, SDK sends directly to Sentry
  • OTEL SDK → OpenTelemetry Collector → Sentry - More control, can process/filter data
  • Platform Drains → Sentry - No SDK needed, cloud platforms automatically forward logs and traces (Vercel, Cloudflare, etc.)

We’ll start with the direct approach since it’s the fastest way to get data flowing.

Getting Your Sentry OTLP Credentials

Sentry provides all the OTLP configuration you need directly in the UI

  1. Open your Sentry project

  2. Navigate to Client Keys (DSN)

    Go to Settings > Projects > [Your Project] > Client Keys (DSN)

  3. Click the OpenTelemetry (OTLP) tab

    Copy the following values (click the copy icon next to each):

    • OTLP Logs Endpoint - The full URL for sending logs
    • OTLP Logs Endpoint Headers - The authentication header for logs
    • OTLP Traces Endpoint - The full URL for sending traces
    • OTLP Traces Endpoint Headers - The authentication header for traces
Sentry OTLP Endpoints

Configuring SDK Direct Mode

Now let’s configure the backend API to send data directly to Sentry’s OTLP endpoints.

  1. Add your Sentry OTLP configuration to the api/.env file

    Terminal window
    # Sentry OTLP Logs Endpoint & Auth Header (paste from Sentry UI)
    OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=<paste OTLP Logs Endpoint>
    OTEL_EXPORTER_OTLP_LOGS_HEADERS=<paste OTLP Logs Endpoint Header>
    # Sentry OTLP Traces Endpoint & Auth Header (paste from Sentry UI)
    OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=<paste OTLP Traces Endpoint>
    OTEL_EXPORTER_OTLP_TRACES_HEADERS=<paste OTLP Traces Endpoint Header>

    Note: leave off the quotes around the headers values. This is nuance for this demo we’ll talk about later.

  2. Restart (if running from earlier) the backend server in direct mode

    Terminal window
    npm run demo:direct

    You should see output like this:

    📡 Mode: DIRECT (SDK → Sentry)
    📡 Exporting to: https://o4509013641854976.ingest.us.sentry.io/api/...
    🔭 OpenTelemetry instrumentation initialized
    📊 Service: sentry-build-otlp-workshop-api
    ... more output ...
    ✅ All services initialized successfully

What’s Happening Behind the Scenes

The application’s backend is already configured with OpenTelemetry instrumentation (in api/instrument-otel.js). Here’s what’s happening:

1. OTLP Exporters Configuration

The environment variables you set in .env are loaded and used to create OTLP exporters that send data to Sentry:

// From api/instrument-otel.js
// Direct mode: App sends directly to Sentry (single project)
// Headers are automatically parsed from OTEL_EXPORTER_OTLP_TRACES_HEADERS env var by the exporter
const traceExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
});
const logExporter = new OTLPLogExporter({
url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
});

Both the trace exporter and log exporter are configured in the NodeSDK:

// From api/instrument-otel.js
// Initialize OpenTelemetry SDK
const sdk = new NodeSDK({
resource,
traceExporter,
logRecordProcessors: [new BatchLogRecordProcessor(logExporter)],
instrumentations: [
getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-fs': {
enabled: false,
},
'@opentelemetry/instrumentation-http': {
enabled: true,
ignoreIncomingPaths: ['/health'],
},
'@opentelemetry/instrumentation-express': {
enabled: true,
},
'@opentelemetry/instrumentation-pg': {
enabled: true,
enhancedDatabaseReporting: true,
},
'@opentelemetry/instrumentation-redis-4': {
enabled: true,
},
}),
],
});
sdk.start();

Once the SDK starts, all instrumented operations (HTTP requests, database queries, etc.) will automatically send their trace and log data to your Sentry project!

2. Automatic Instrumentation

OpenTelemetry automatically captures:

  • HTTP (instrumentation-http): Incoming requests to your API AND outgoing HTTP calls (like the payment gateway). Health checks and CORS preflight requests are ignored to reduce noise.
  • Express (instrumentation-express): Route handlers and middleware
  • PostgreSQL (instrumentation-pg): Database queries with query text and duration (enhanced reporting enabled)
  • Redis (instrumentation-redis-4): Cache get/set operations

Note: File system instrumentation is disabled by default as it generates too much noise.

3. Manual Instrumentation

The application also includes custom spans for business logic:

// Example from api/src/services/payment.js
import { withSpan } from '../utils/tracer.js';
async function processPayment(orderId, amount) {
return withSpan('payment.process', async (span) => {
span.setAttribute('order.id', orderId);
span.setAttribute('payment.amount', amount);
// Simulate payment processing
const success = Math.random() > 0.1; // 90% success rate
if (!success) {
span.addEvent('payment-failed');
throw new Error('Payment declined');
}
return { success: true, transactionId: 'tx_' + Date.now() };
});
}

These custom spans give you business-specific context in your traces.

4. Resource Attributes

Every span includes resource attributes that identify your service:

{
"service.name": "otel-ecommerce-api",
"service.version": "1.0.0",
"deployment.environment": "development"
}

These help you filter and organize traces in Sentry, especially when you have multiple services.

Testing the Direct Integration

Now that you understand how the backend is configured for direct mode, let’s verify it’s sending data to Sentry.

  1. Generate some traffic

    Terminal window
    # Open a new terminal window (keep the backend server running in the other terminal)
    # Simple health check
    curl http://localhost:3000/health
    # Fetch products (triggers database and cache operations)
    curl http://localhost:3000/api/products
    # Or run the load test to generate realistic traffic
    npm run test:api

    The load test will generate ~40 traces with various scenarios:

    • Product fetching (cache hits and misses)
    • Order creation
    • Payment processing (including failures)
    • Inventory checks
  2. Verify data in Sentry

    1. Go to your Sentry project
    2. Navigate to ExploreTraces
    3. You should see traces appearing from your API!

    Look for traces with operations like:

    • GET /api/products
    • POST /api/orders
    • Database queries (PostgreSQL)
    • Cache operations (Redis)

What to Look For in Sentry

Once you’ve generated some traffic, here’s what you should see in Sentry:

Traces Tab

Navigate to ExploreTraces and you’ll see the Span Samples tab displaying:

  • Span Name: The operation type (e.g., GET, cache.get, pg-pool.connect)
  • Span Description: Full operation details (e.g., GET /api/products)
  • Span Duration: How long each operation took
  • Transaction: Parent transaction (may show “(no value)” for standalone spans)
  • Timestamp: When the span occurred

Trace Details

Click on any span in the Span Samples tab to open the trace waterfall view, where you’ll see:

  • Waterfall view (left): Visual timeline showing the hierarchy of spans with their durations
  • Span Details panel (right): Opens when you click on a span, showing:
    • Span ID: Unique identifier for the span
    • Span attributes: category, description, duration, name, op (operation), self_time, status
    • Context data: Browser info, device details, client_sample_rate
    • Custom attributes: Any business data you’ve added to spans

Learn more about the Trace Explorer view in the Sentry documentation.