Skip to content

Angelodaniel/otel-logs-traces

Repository files navigation

πŸ”­ OpenTelemetry β†’ Collector β†’ Sentry Demo

A complete end-to-end demonstration of sending OpenTelemetry traces and logs from a frontend and backend application through an OpenTelemetry Collector to Sentry.

πŸ“‹ Overview

This demo shows the complete telemetry flow:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Frontend  │────────▢│   Backend   │────────▢│ OTEL Collector   │────────▢│ Sentry  β”‚
β”‚  (React)    β”‚  HTTP   β”‚  (Express)  β”‚  OTLP   β”‚  (Aggregator)    β”‚  OTLP   β”‚  (APM)  β”‚
β”‚   + OTEL    β”‚         β”‚   + OTEL    β”‚         β”‚  (Exporter)      β”‚         β”‚         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚                                                    β–²
      β”‚                                                    β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         OTLP (traces)

What This Demo Includes

  • βœ… Frontend tracing: React SPA with OpenTelemetry browser instrumentation
  • βœ… Backend tracing: Node.js/Express with automatic HTTP instrumentation
  • βœ… Backend logging: Structured logs via OpenTelemetry Logs API
  • βœ… Distributed tracing: W3C Trace Context propagation between frontend and backend
  • βœ… OTLP export: All telemetry sent via OTLP/HTTP protocol
  • βœ… Collector pipeline: OpenTelemetry Collector receives, processes, and exports to Sentry
  • βœ… No Sentry SDKs: Pure OpenTelemetry implementation (vendor-neutral)
  • βœ… Docker Compose: One command to run everything

πŸ—οΈ Architecture

Components

  1. Frontend (frontend/)

    • React 18 + TypeScript + Vite
    • OpenTelemetry browser SDK
    • Automatic instrumentation: document load, fetch API
    • Exports traces to collector via OTLP/HTTP
  2. Backend (backend/)

    • Node.js 20 + Express + TypeScript
    • OpenTelemetry Node.js SDK
    • Automatic instrumentation: HTTP, Express
    • Exports traces and logs to collector via OTLP/HTTP
  3. OpenTelemetry Collector (otel-collector/)

    • Official otel/opentelemetry-collector image
    • Receives: OTLP (HTTP on port 4318, gRPC on port 4317)
    • Processes: Batching, resource attributes
    • Exports: Sentry via OTLP/HTTP

Telemetry Flow

  1. Page Load: Frontend generates a document load span
  2. User Action: User clicks a button to call the backend
  3. Frontend Span: Fetch instrumentation creates a span with trace context
  4. Context Propagation: W3C headers (traceparent, tracestate) sent to backend
  5. Backend Span: Express instrumentation creates a child span linked to frontend trace
  6. Backend Logs: Structured logs emitted with trace correlation
  7. OTLP Export: Both apps send telemetry to collector
  8. Collector Processing: Batches and enriches telemetry
  9. Sentry Export: Collector sends everything to Sentry via OTLP

πŸš€ Quick Start

Prerequisites

  • Docker and Docker Compose
  • A Sentry account with a project configured for OpenTelemetry

Step 1: Configure Sentry

  1. Get your Sentry OTLP credentials:

    • Go to Sentry Project Settings β†’ Client Keys (DSN)
    • Look for the "OpenTelemetry (OTLP)" section
    • Copy your endpoint and authentication header
  2. Copy the environment template:

    cp env.example .env
  3. Edit .env and add your Sentry configuration:

    SENTRY_OTLP_ENDPOINT=https://o123456.ingest.sentry.io/api/7891011/integration/otlp
    SENTRY_AUTH_HEADER=sentry sentry_key=abc123def456

    Finding Your Sentry Credentials:

    • OTLP Endpoint:

      • Format: https://o[ORG_ID].ingest.sentry.io/api/[PROJECT_ID]/integration/otlp
      • Don't include /v1/logs or /v1/traces - the collector adds these automatically
    • Authentication Header:

      • Format: sentry sentry_key=YOUR_PUBLIC_KEY
      • Extract the public key from your DSN (the part before the @)
      • DSN format: https://PUBLIC_KEY@oORG_ID.ingest.sentry.io/PROJECT_ID

    Reference:

Step 2: Build and Run

# Build and start all services
docker-compose up --build

# Or run in detached mode
docker-compose up --build -d

This will start:

Step 3: Generate Telemetry

  1. Open http://localhost:5173 in your browser

  2. Click the test buttons to test Node.js backend:

    • Health Check: Simple successful request
    • Fetch Data: Successful API call with data
    • Slow Request: 1-second delay to see span timing
    • Trigger Error: Intentional error to test error tracking
  3. Test Rails backend endpoints:

    # Ruby data
    curl http://localhost:5000/api/ruby-data
    
    # Slow Ruby endpoint
    curl http://localhost:5000/api/ruby-slow
    
    # Distributed tracing: Rails β†’ Node.js
    curl http://localhost:5000/api/call-node

Step 4: View in Sentry

  1. Go to your Sentry project(s)
  2. Navigate to Performance (or Traces) to see distributed traces
  3. Look for traces with spans from:
    • demo-frontend service (React)
    • demo-backend service (Node.js)
    • demo-rails-backend service (Ruby) - polyglot tracing!
  4. Distributed Trace Example: When Rails calls Node.js (/api/call-node), you'll see a single trace spanning both:
    Trace abc123:
    β”œβ”€ [Rails] GET /api/call-node
    β”‚  └─ [Rails] call-node-backend
    β”‚     └─ [Node] HTTP GET /api/data
    β”‚        └─ [Node] fetch-data
    
  5. Check Logs (if available) to see correlated backend logs
  6. Verify trace IDs match across all services

πŸ“ Project Structure

otel-sentry-demo/
β”œβ”€β”€ docker-compose.yml              # Orchestrates all services
β”œβ”€β”€ env.example                     # Sentry configuration template
β”œβ”€β”€ README.md                       # This file
β”‚
β”œβ”€β”€ backend/                        # Node.js backend
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ tsconfig.json
β”‚   └── src/
β”‚       β”œβ”€β”€ index.ts               # Entry point (imports tracing first)
β”‚       β”œβ”€β”€ tracing.ts             # OpenTelemetry tracing setup
β”‚       β”œβ”€β”€ logging.ts             # OpenTelemetry logging setup
β”‚       └── app.ts                 # Express app with instrumented routes
β”‚
β”œβ”€β”€ frontend/                       # React frontend
β”‚   β”œβ”€β”€ Dockerfile
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ tsconfig.json
β”‚   β”œβ”€β”€ vite.config.ts
β”‚   β”œβ”€β”€ index.html
β”‚   └── src/
β”‚       β”œβ”€β”€ main.tsx               # Entry point (initializes OTEL)
β”‚       β”œβ”€β”€ otel-setup.ts          # OpenTelemetry browser setup
β”‚       └── App.tsx                # UI with API call buttons
β”‚
└── otel-collector/                 # OpenTelemetry Collector
    └── otel-collector-config.yaml # Collector configuration

πŸ”§ Development

Running Services Individually

Backend (local development):

cd backend
npm install
npm run dev

Frontend (local development):

cd frontend
npm install
npm run dev

Collector (local):

docker run -p 4317:4317 -p 4318:4318 \
  -v $(pwd)/otel-collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
  -e SENTRY_OTLP_ENDPOINT="your-endpoint" \
  -e SENTRY_AUTH_HEADER="Bearer your-token" \
  otel/opentelemetry-collector:latest \
  --config=/etc/otel-collector-config.yaml

Viewing Collector Logs

# View collector logs to see telemetry flowing through
docker-compose logs -f otel-collector

# View backend logs
docker-compose logs -f backend

# View all logs
docker-compose logs -f

Debugging Tips

  1. Check collector health:

    curl http://localhost:13133/
  2. Verify backend is sending telemetry:

    • Check backend logs for "βœ… OpenTelemetry initialized"
    • Look for OTLP export messages in collector logs
  3. Verify frontend is sending telemetry:

    • Open browser DevTools β†’ Console
    • Look for "βœ… OpenTelemetry instrumentation initialized"
    • Check Network tab for requests to localhost:4318/v1/traces
  4. Test without Sentry:

    • The collector config includes a logging exporter
    • Telemetry will be printed to collector stdout for debugging
    • This works even if Sentry credentials are invalid

πŸ” What You'll See in Sentry

Traces

Each user action creates a distributed trace with spans like:

πŸ“Š Trace: user-action: /api/slow
β”œβ”€ 🌐 fetch GET http://localhost:4000/api/slow  [demo-frontend]
β”‚  β”œβ”€ πŸ“‘ HTTP GET /api/slow                     [demo-backend]
β”‚  β”‚  └─ ⚑ slow-operation                      [demo-backend]
└─ ⏱️ Total: ~1.2s

Span Attributes you'll see:

  • service.name: demo-frontend or demo-backend
  • service.environment: demo
  • http.method, http.url, http.status_code
  • Custom attributes like demo.sleep_duration_ms

Logs

Backend logs appear in Sentry with attributes like:

  • severityText: INFO, WARN, ERROR
  • body: Log message
  • service.name: demo-backend
  • Custom attributes: endpoint, duration_ms, etc.
  • Trace correlation: Logs include trace IDs so you can jump from log β†’ trace

Error Tracking

When you click "Trigger Error":

  • An error span is created with status: ERROR
  • An exception is recorded on the span
  • An error-level log is emitted
  • All three are correlated by trace ID
  • You'll see the full stack trace in Sentry

🎯 Key Implementation Details

Context Propagation

The frontend's FetchInstrumentation is configured to propagate trace context:

new FetchInstrumentation({
  propagateTraceHeaderCorsUrls: [/localhost:4000/],
})

This injects traceparent and tracestate headers into fetch requests. The backend's auto-instrumentation extracts these headers and creates child spans.

Resource Attributes

Both apps define identical resource attributes so Sentry can group them:

{
  'service.name': 'demo-frontend',  // or 'demo-backend'
  'service.environment': 'demo',
  'service.version': '1.0.0',
}

OTLP Endpoints

  • Backend β†’ Collector: http://otel-collector:4318 (internal Docker network)
  • Frontend β†’ Collector: http://localhost:4318 (browser β†’ host β†’ container)
  • Collector β†’ Sentry: Configured via SENTRY_OTLP_ENDPOINT env var

No Sentry SDKs

This demo intentionally does not use @sentry/browser or @sentry/node. Everything is pure OpenTelemetry. This demonstrates:

  • Vendor neutrality (can switch from Sentry to Honeycomb/Datadog/etc. by just changing collector config)
  • Standard OTLP protocol
  • Collector-based architecture (centralized config)

πŸ› Troubleshooting

Telemetry not appearing in Sentry

  1. Check Sentry credentials:

    • Verify SENTRY_OTLP_ENDPOINT format
    • Verify SENTRY_AUTH_HEADER has correct auth token
    • Check Sentry docs for your specific plan's OTLP endpoint format
  2. Check collector logs:

    docker-compose logs otel-collector
    • Look for export errors
    • Verify data is being received (should see detailed logs)
  3. Verify Sentry project supports OTLP:

    • Some Sentry plans may have limited OTLP support
    • Check your plan's features
    • Make sure project is configured for OpenTelemetry (not just error tracking)

Frontend can't reach backend

  • Make sure backend is running: curl http://localhost:4000/health
  • Check CORS settings in backend/src/app.ts
  • Verify VITE_BACKEND_URL is correct in frontend environment

Collector not receiving data

  • Check collector health: curl http://localhost:13133/
  • Verify ports 4317 and 4318 are exposed
  • Check firewall/network settings

Traces not linking frontend + backend

  • Verify FetchInstrumentation has correct propagateTraceHeaderCorsUrls
  • Check browser DevTools β†’ Network β†’ request headers for traceparent
  • Ensure backend auto-instrumentation is enabled (imported in index.ts before app)

πŸ“š Learn More

OpenTelemetry

Sentry + OpenTelemetry

W3C Trace Context

🀝 Contributing

This is a demo project, but feel free to:

  • Report issues
  • Suggest improvements
  • Add additional instrumentation examples
  • Improve documentation

πŸ“„ License

MIT License - feel free to use this demo for learning and teaching.


Questions? Check the inline comments in the code for detailed explanations of how each part works.

Next Steps:

  1. Explore the code to understand the OTEL setup
  2. Modify backend/src/app.ts to add custom spans/logs
  3. Experiment with different collector exporters (Jaeger, Prometheus, etc.)
  4. Add metrics collection (currently only traces and logs)
  5. Implement sampling strategies in the collector
Screenshot 2025-12-02 at 12 58 11 Screenshot 2025-12-02 at 12 58 01 Screenshot 2025-12-02 at 12 57 55

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors