Back to Blog Home

Contents

Share

Share on Twitter
Share on Bluesky
Share on HackerNews
Share on LinkedIn

Routing OpenTelemetry logs to Sentry using OTLP

James W. image

James W. -

Routing OpenTelemetry logs to Sentry using OTLP

Routing OpenTelemetry logs to Sentry using OTLP

If you've already instrumented your app with OpenTelemetry, you don't have to rip it out to use Sentry. Two environment variables and your logs start flowing into Sentry, no SDK changes, no re-instrumentation. Here's how to set it up in a sample app, and when the native Sentry SDK might be the better call.

Why you'd use OTLP instead of the native SDK

The main advantage of OTLP is that your logging code stays decoupled from any specific observability backend. You can switch where logs go by changing a few config lines. That's useful if you:

  • Already have OpenTelemetry logging in place

  • Want to send logs to multiple backends

  • Need vendor-neutral instrumentation

  • Work with AI or LLM frameworks that use OpenTelemetry by default

  • Want to use the broader OpenTelemetry ecosystem

If you're starting from scratch and only need Sentry, the native Sentry SDK is probably the better call. With the native SDK, you get issue creation from logs, session replay integration, automatic breadcrumbs, and built-in error correlation. Ingesting OpenTelemetry traces and logs with Sentry via OTLP endpoints is still in beta and currently lacks these integrated features.

Guide prerequisites

Before we start, you need:

  • A Sentry account (the free tier works)

  • Node.js 18 or later installed

  • Basic familiarity with Express.js

If you don't have a Sentry project yet, create one now. Select Express as the platform. You can skip the DSN setup instructions because you'll use the OTLP endpoint instead.

Get your Sentry OTLP credentials

Sentry exposes separate OTLP endpoints for logs and traces. In this guide, we're focusing on the Logs endpoint. To find your OTLP credentials:

  • Click Settings in the left sidebar.

  • Under the Organization section in the Settings sidebar, click Projects.

  • Find your project in the list and click on it to open the project settings.

  • In the project settings sidebar, click Client Keys (DSN) under the SDK Setup section.

  • Select the OpenTelemetry tab. Click the Expand button to see all OTLP endpoint values.

Keep this tab open. We'll use the following values in the next step:

  • OTLP Logs Endpoint: The URL where Sentry receives logs (which looks like https://o{ORG_ID}.ingest.us.sentry.io/api/{PROJECT_ID}/integration/otlp/v1/logs)

  • OTLP Logs Endpoint Headers: The authentication header (which looks like x-sentry-auth=sentry sentry_key={YOUR_PUBLIC_KEY})

One thing worth knowing: most OTLP exporters expect headers as raw key/value pairs, not full header strings. You'll need to parse the header in your app. We'll handle this in the setup below.

Connect your OpenTelemetry app to Sentry

We'll use a sample payment processing service that already has OpenTelemetry logging instrumentation. You don't need to touch the logging code itself. Just point it at Sentry's OTLP endpoint.

Clone the starter app

Run the following commands to clone the payment processing app:

Click to Copy
git clone https://github.com/getsentry/otlp-logging-sentry.git
cd otlp-logging-sentry
npm install

This app includes the OpenTelemetry SDK already configured, structured logging throughout, multiple log severity levels (INFO, DEBUG, WARN, and ERROR), and rich log attributes for every entry.

Configure Sentry as the OTLP destination

Create a .env file in the project root:

Click to Copy
cp .env.example .env

Now edit .env and add your Sentry OTLP credentials from the previous step:

Click to Copy
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://o{YOUR_ORG_ID}.ingest.us.sentry.io/api/{YOUR_PROJECT_ID}/integration/otlp/v1/logs
OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-sentry-auth=sentry sentry_key={YOUR_PUBLIC_KEY}
OTEL_SERVICE_NAME=payment-processing-service
PORT=3000

Replace the placeholders with your actual Sentry credentials. The OTEL_SERVICE_NAME will let you filter logs by service in Sentry later.

That's it. Two config lines and OpenTelemetry logs are flowing to Sentry.

Test the integration

Start the app:

Click to Copy
npm start

You should see:

Click to Copy
OpenTelemetry logging initialized
Service: payment-processing-service
Payment Processing Service running on http://localhost:3000

Generate some logs

In a new terminal window, send a request to process a payment:

Click to Copy
curl -X POST http://localhost:3000/process-payment \
  -H "Content-Type: application/json" \
  -d '{"userId": "user123", "amount": 99.99, "paymentMethod": "credit_card"}'

You'll get a JSON response confirming the payment:

Click to Copy
{
  "success": true,
  "transactionId": "txn_1730123456789_abc123def",
  "amount": 99.99,
  "currency": "USD",
  "status": "completed"
}

View the logs in Sentry

Now let's see what this looks like in Sentry's Logs view:

  • Go to your Sentry project.

  • Navigate to Explore in the left sidebar, then click Logs.

You'll see a list of log entries from your payment processing workflow. Each log shows a timestamp, severity indicator (colored dot), and message.

Explore log attributes

Click on any log entry to expand it and see all its attributes.

For example, the High-risk transaction detected log includes attributes like the following:

  • fraud_check.score: 97.98

  • fraud_check.threshold: 70

  • fraud_check.reason: unusual_amount_pattern

  • user.id: user123

  • transaction.id: txn_1762164637756_0hscczobm

  • severity: warn

All of these are searchable. To add any attribute as a filter, hover over it, click the overflow menu (three dots), and select Add to filter.

How OpenTelemetry logging works

Here's what's happening under the hood, in case you're applying these patterns to your own app.

OpenTelemetry SDK initialization

The instrument.js file configures the OTLP exporter and wires up the logger provider:

Click to Copy
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { Resource } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';

// Configure the OTLP log exporter
const logExporter = new OTLPLogExporter({
  url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
  headers: {
    'x-sentry-auth': process.env.OTEL_EXPORTER_OTLP_LOGS_HEADERS?.replace('x-sentry-auth=', '') || '',
  },
});

// Create logger provider
const loggerProvider = new LoggerProvider({
  resource: new Resource({
    [ATTR_SERVICE_NAME]: 'payment-processing-service',
  }),
});

loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));

// Make logger provider available globally
global.loggerProvider = loggerProvider;

These are the key parts:

Emitting structured logs

The index.js file imports instrument.js first, then creates a logger and emits records:

Click to Copy
import './instrument.js';
import { logs, SeverityNumber } from '@opentelemetry/api-logs';
const logger = logs.getLogger('payment-processing-service', '1.0.0');

Here's how we emit a structured log:

Click to Copy
function log(severity, severityNumber, message, attributes = {}) {
  logger.emit({
    severityNumber,
    severityText: severity,
    body: message,
    attributes,
  });
}

// Example usage
log('INFO', SeverityNumber.INFO, 'Payment request received', {
  'user.id': userId,
  'payment.amount': amount,
  'payment.method': paymentMethod,
  'transaction.id': transactionId,
});

Each call to logger.emit() takes a severity level, a message body, and a set of attributes. The attributes are what make logs searchable — the more context you add here, the easier it is to find specific events later.

Log severity levels

OpenTelemetry supports six severity levels:

Click to Copy
import { SeverityNumber } from '@opentelemetry/api-logs';
// TRACE (most detailed)
log('TRACE', SeverityNumber.TRACE, 'Function entry', {...});
// DEBUG (debugging info)
log('DEBUG', SeverityNumber.DEBUG, 'Validating payment', {...});
// INFO (informational)
log('INFO', SeverityNumber.INFO, 'Payment received', {...});
// WARN (warnings)
log('WARN', SeverityNumber.WARN, 'High-risk transaction', {...});
// ERROR (errors)
log('ERROR', SeverityNumber.ERROR, 'Payment failed', {...});
// FATAL (critical)
log('FATAL', SeverityNumber.FATAL, 'System failure', {...});

Adding rich attributes

The more attributes you add, the easier it is to debug issues. Here's an example from the fraud detection path:

Click to Copy
log('WARN', SeverityNumber.WARN, 'High-risk transaction detected', {
  'user.id': userId,
  'transaction.id': transactionId,
  'fraud_check.score': 85.2,
  'fraud_check.threshold': 70,
  'fraud_check.reason': 'unusual_amount_pattern',
});

All these attributes are searchable in Sentry, so you can find specific transactions quickly without scanning log text.

OTLP vs native Sentry SDK

Both approaches send logs to Sentry. The difference is in what you get automatically.

Setup and configuration

OTLP

Click to Copy
// instrument.js
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
import { LoggerProvider, BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
const logExporter = new OTLPLogExporter({
  url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
  headers: {
    'x-sentry-auth': process.env.OTEL_EXPORTER_OTLP_LOGS_HEADERS
  },
});
const loggerProvider = new LoggerProvider({...});
loggerProvider.addLogRecordProcessor(new BatchLogRecordProcessor(logExporter));

Native Sentry SDK

Click to Copy
// instrument.js
import * as Sentry from '@sentry/node';
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  enableLogs: true, // Required for structured logging
});

Note that Sentry.logger requires Sentry JavaScript SDK v9.41.0 or above.

Emitting logs

OTLP

Click to Copy
import { logs, SeverityNumber } from '@opentelemetry/api-logs';
const logger = logs.getLogger('my-service', '1.0.0');
logger.emit({
  severityNumber: SeverityNumber.INFO,
  severityText: 'INFO',
  body: 'Payment request received',
  attributes: {
    'user.id': userId,
    'payment.amount': amount,
  },
});

Native Sentry SDK

Click to Copy
import * as Sentry from '@sentry/node';
Sentry.logger.info('Payment request received', {
  'user.id': userId,
  'payment.amount': amount,
});

With OpenTelemetry, you specify both severityNumber and severityText manually. The Sentry SDK infers both from the method you call (info(), warn(), and so on). The SDK also associates logs with errors, transactions, and user sessions automatically, without any extra setup.

Log levels

OTLP

Click to Copy
import { SeverityNumber } from '@opentelemetry/api-logs';
logger.emit({ severityNumber: SeverityNumber.DEBUG, ... });
logger.emit({ severityNumber: SeverityNumber.INFO, ... });
logger.emit({ severityNumber: SeverityNumber.WARN, ... });
logger.emit({ severityNumber: SeverityNumber.ERROR, ... });

Native Sentry SDK

Click to Copy
Sentry.logger.debug('message', {...});
Sentry.logger.info('message', {...});
Sentry.logger.warn('message', {...});
Sentry.logger.error('message', {...});

What's next

You now have OpenTelemetry logs flowing into Sentry. A few ways to get more value from here:

  • Add context to your logs. The more attributes you add, the easier it is to debug issues. Add user IDs, request IDs, transaction IDs, feature flags, or any relevant business context to every log entry.

  • Use consistent attribute naming. Follow OpenTelemetry Semantic Conventions for standardized attribute names. This keeps your logs consistent and easier to search across services.

  • Set up alerts. Configure Sentry alerts to notify you when certain log patterns appear — ERROR logs exceeding a threshold, or high-risk transactions crossing a fraud score cutoff.

  • Combine logs with traces. If you're also sending traces to Sentry, you can correlate them with logs to get a complete picture of your application's behavior.

OTLP logging support is still in open beta. If you run into a limitation not listed here, open an issue on GitHub. That's the fastest way to get it on our radar.

OTLP Logging FAQs

Syntax.fm logo

Listen to the Syntax Podcast

Of course we sponsor a developer podcast. Check it out on your favorite listening platform.

Listen To Syntax
© 2026 • Sentry is a registered Trademark of Functional Software, Inc.