Integrations and Connectors/Connectors/Setup Guides/Analytics

Open Telemetry Connector

This guide explains how to implement Descope's Open Telemetry connector. Open Telemetry is an observability framework for instrumenting, generating, collecting, and exporting telemetry data.

Descope enables you to automatically collect authentication logs and audit events and forward them to your Open Telemetry Collector instance for centralized analysis and monitoring.

Configure Open Telemetry Connector

Open Telemetry connector setup

Navigate to the Connectors page in the Descope Console and select OpenTelemetry to create a new Open Telemetry connector.

The following parameters are required:

  • Connector Name: Provide a unique name for your connector. This helps distinguish it, especially when multiple connectors are derived from the same template.
  • Connector Description: Briefly explain the purpose of this connector (optional).
  • Collector Endpoint: The endpoint URL of your Open Telemetry Collector instance.
  • Protocol: The protocol to use when sending data to your Open Telemetry Collector (e.g., HTTP, gRPC).
  • Authentication Type: Choose the authentication method required by your Open Telemetry Collector (if any).
  • Request Headers: Specify any additional headers required for authentication or configuration when sending data to your Open Telemetry Collector.
  • Trust Any Certificate: Enable this option if your Open Telemetry Collector uses a self-signed certificate.
  • Stream Audit Events: Select which events are sent to Open Telemetry. You can allow all audit events or filter them based on specific actions or tenants in the project.
  • Stream Troubleshooting Events: Decide whether troubleshooting events are also sent to Open Telemetry.

Configuring Your Open Telemetry Collector

In your OpenTelemetry setup, ensure that the Collector is properly configured to receive logs and events from Descope.

Here is an example of an OpenTelemetry Collector client using the OTLP exporter in a Node.js environment:

import express, { Express, Request, Response } from 'express';
 
const PORT: number = parseInt(process.env.PORT || '8080');
const app: Express = express();
 
// Add middleware for both JSON and protobuf
app.use(express.json());
app.use(express.raw({ type: 'application/x-protobuf', limit: '10mb' }));
 
function getRandomNumber(min: number, max: number) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
 
// OpenTelemetry Collector compatible endpoints
app.post('/v1/traces', (req: Request, res: Response) => {
  try {
    console.log('Received traces data');
    console.log('Content-Type:', req.headers['content-type']);
    console.log('Data length:', req.body.length);
    
    // For protobuf data, you'd need to decode it
    // This is a simplified response for now
    res.status(200).send();
  } catch (error) {
    console.error('Error processing traces:', error);
    res.status(500).send();
  }
});
 
app.post('/v1/metrics', (req: Request, res: Response) => {
  try {
    console.log('Received metrics data');
    console.log('Content-Type:', req.headers['content-type']);
    console.log('Data length:', req.body.length);
    
    res.status(200).send();
  } catch (error) {
    console.error('Error processing metrics:', error);
    res.status(500).send();
  }
});
 
app.post('/v1/logs', (req: Request, res: Response) => {
  try {
    console.log('Received logs data');
    console.log('Content-Type:', req.headers['content-type']);
    console.log('Data length:', req.body.length);
    
    res.status(200).send();
  } catch (error) {
    console.error('Error processing logs:', error);
    res.status(500).send();
  }
});
 
// Keep your original webhook endpoint for JSON data
app.post('/webhook/otel', (req: Request, res: Response) => {
  try {
    const webhookData = req.body;
    
    // Log the incoming webhook data
    console.log('Received webhook event:', JSON.stringify(webhookData, null, 2));
    
    // Process OpenTelemetry compatible events
    if (webhookData.resourceSpans) {
      // Handle trace data
      console.log('Processing trace spans...');
      webhookData.resourceSpans.forEach((resourceSpan: any) => {
        resourceSpan.scopeSpans?.forEach((scopeSpan: any) => {
          scopeSpan.spans?.forEach((span: any) => {
            console.log(`Span: ${span.name}, TraceID: ${span.traceId}, SpanID: ${span.spanId}`);
          });
        });
      });
    }
    
    if (webhookData.resourceMetrics) {
      // Handle metrics data
      console.log('Processing metrics...');
      webhookData.resourceMetrics.forEach((resourceMetric: any) => {
        resourceMetric.scopeMetrics?.forEach((scopeMetric: any) => {
          scopeMetric.metrics?.forEach((metric: any) => {
            console.log(`Metric: ${metric.name}, Type: ${metric.unit}`);
          });
        });
      });
    }
    
    if (webhookData.resourceLogs) {
      // Handle log data
      console.log('Processing logs...');
      webhookData.resourceLogs.forEach((resourceLog: any) => {
        resourceLog.scopeLogs?.forEach((scopeLog: any) => {
          scopeLog.logRecords?.forEach((logRecord: any) => {
            console.log(`Log: ${logRecord.body?.stringValue}, Severity: ${logRecord.severityText}`);
          });
        });
      });
    }
    
    // Send success response
    res.status(200).json({ 
      status: 'success', 
      message: 'Webhook event processed',
      timestamp: new Date().toISOString()
    });
    
  } catch (error) {
    console.error('Error processing webhook:', error);
    res.status(500).json({ 
      status: 'error', 
      message: 'Failed to process webhook event' 
    });
  }
});
 
app.listen(PORT, () => {
  console.log(`Listening for requests on http://localhost:${PORT}`);
});

Optionally, specify the appropriate endpoint and authentication details in your Open Telemetry configuration file or interface.

Viewing Audit Logs

Audit logs are now viewable in Open Telemetry. You can test the connector during configuration to ensure logs are being sent and collected properly.

This span represents the handling of an HTTP request to /v1/logs inside an Express.js application. It was captured by OpenTelemetry’s Express instrumentation, tied to a larger trace, executed on a Node.js process, and finished successfully in ~3 milliseconds:

{
  "resource": {
    "attributes": {
      "host.name": "example-host",
      "host.arch": "arm64",
      "host.id": "host-id-1234",
      "process.pid": 12345,
      "process.executable.name": "/usr/local/bin/node",
"process.executable.path": "/usr/local/bin/node",
      "process.command_args": [
        "/usr/local/bin/node",
        "--require",
        "/app/node_modules/tsx/dist/preflight.cjs",
        "--import",
        "file:///app/node_modules/tsx/dist/loader.mjs",
        "--import",
        "./instrumentation.ts",
        "/app/app.ts"
      ],
      "process.executable.path": "/usr/local/bin/node",
      "process.command_args": [
        "/usr/local/bin/node",
        "--require",
        "/app/node_modules/tsx/dist/preflight.cjs",
        "--import",
        "file:///app/node_modules/tsx/dist/loader.mjs",
        "--import",
        "./instrumentation.ts",
        "/app/app.ts"
      ],
      "process.runtime.version": "24.x.x",
      "process.runtime.name": "nodejs",
      "process.runtime.description": "Node.js",
      "process.command": "/app/app.ts",
      "process.owner": "example-user",
      "service.name": "example-service",
      "telemetry.sdk.language": "nodejs",
      "telemetry.sdk.name": "opentelemetry",
      "telemetry.sdk.version": "2.x.x""instrumentationScope": {
    "name": "@opentelemetry/instrumentation-express",
    "version": "0.52.0",
    "schemaUrl": null
  },

    }
  },
  "instrumentationScope": {
    "name": "@opentelemetry/instrumentation-express",
    "version": "0.52.0",
    "schemaUrl": null
  },
  "traceId": "11111111111111111111111111111111",
  "parentSpanContext": {
    "traceId": "11111111111111111111111111111111",
    "spanId": "2222222222222222",
    "traceFlags": 1,
    "traceState": null
  },
  "traceState": null,
  "name": "request handler - /v1/logs",
  "id": "3333333333333333",
  "kind": 0,
  "timestamp": 1757334070604000,
  "duration": 3106.667,
  "attributes": {
    "http.route": "/v1/logs",
    "express.name": "/v1/logs",
    "express.type": "request_handler"
  },
  "status": { "code": 0 },
  "events": [],
  "links": []
}

The logs can still be viewed in Descope under the Audit and Troubleshoot section of the Descope Console.

Was this helpful?

On this page