Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example-apps/collector/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# All default settings defined here can be overridden by environment variables.

# MODE=npm
MODE=local
APP_PORT=2807
SENSOR_ENABLED=true
TRACING_ENABLED=true
Expand Down
3 changes: 3 additions & 0 deletions example-apps/collector/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ if (config.mode === 'npm') {
packageToRequire = '@instana/collector';
}

process.env.INSTANA_METRICS_TRANSMISSION_DELAY = 5000;
process.env.INSTANA_OTLP_FORMAT = 'true';

if (config.collectorEnabled) {
console.log(`enabling @instana/collector (requiring ${packageToRequire})`);
require(packageToRequire)({
Expand Down
4 changes: 4 additions & 0 deletions example-apps/otel-exporter-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
data.txt
npm-debug.log
.DS_Store
240 changes: 240 additions & 0 deletions example-apps/otel-exporter-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# OpenTelemetry Exporter Test App

A comprehensive Express.js application for testing OpenTelemetry tracing with Instana backend, including HTTP, PostgreSQL, and Kafka instrumentation.

## Features

- **HTTP tracing**: Express.js REST API with external HTTP calls
- **PostgreSQL tracing**: Database queries with pg driver
- **Kafka tracing**: Message producer and consumer
- **OpenTelemetry auto-instrumentation**: Automatic tracing for all operations
- **OTLP HTTP exporter**: Configured for Instana backend
- **Debug logging**: Console output for spans

## Prerequisites

1. **PostgreSQL** running on `localhost:5432`
- Database: `nodedb`
- User: `node`
- Password: `nodepw`

2. **Kafka** running on `localhost:9092`
- Topic: `test-topic` (will be created automatically)

3. **Instana** account with OTLP endpoint access

## Setup

### 1. Install Dependencies

```bash
npm install
```

### 2. Configure PostgreSQL

```bash
# Create database and user
psql -U postgres
CREATE DATABASE nodedb;
CREATE USER node WITH PASSWORD 'nodepw';
GRANT ALL PRIVILEGES ON DATABASE nodedb TO node;
```

### 3. Configure Kafka

Make sure Kafka is running on `localhost:9092`. If using Docker:

```bash
docker run -d --name kafka \
-p 9092:9092 \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
confluentinc/cp-kafka:latest
```

### 4. Update Instana Configuration

Edit `tracing.js` and update:
- `url`: Your Instana OTLP endpoint
- `x-instana-key`: Your Instana API key

### 5. Start the Application

```bash
npm start
```

Or for development with auto-reload:

```bash
npm run dev
```

## API Endpoints

### 1. HTTP Entry + HTTP Exit

Tests HTTP client instrumentation with external API call.

```bash
curl http://localhost:3000/external-api
```

**Expected trace:**
- HTTP server span (Express)
- HTTP client span (fetch to jsonplaceholder.typicode.com)

### 2. HTTP Entry + PostgreSQL Exit

Tests PostgreSQL database instrumentation.

```bash
curl http://localhost:3000/db
```

**Expected trace:**
- HTTP server span (Express)
- PostgreSQL query span

### 3. HTTP Entry + Kafka Exit

Tests Kafka producer instrumentation.

```bash
curl -X POST http://localhost:3000/kafka \
-H "Content-Type: application/json" \
-d '{"message":"Hello from OpenTelemetry!"}'
```

**Expected trace:**
- HTTP server span (Express)
- Kafka producer span
- Kafka consumer span (async, separate trace)

## Tracing Details

The application automatically traces:

### HTTP Operations
- Express route handlers
- Outgoing HTTP requests (fetch/axios)
- Request/response details

### PostgreSQL Operations
- SQL queries
- Connection details
- Query parameters

### Kafka Operations
- Message production
- Message consumption
- Topic and partition information
- Custom attributes via hooks

## Viewing Traces

1. Make requests to the API endpoints
2. Check console output for span details
3. View traces in your Instana dashboard

## Troubleshooting

### PostgreSQL Connection Issues

```bash
# Check if PostgreSQL is running
pg_isready -h localhost -p 5432

# Test connection
psql -h localhost -U node -d nodedb
```

### Kafka Connection Issues

```bash
# Check if Kafka is running
nc -zv localhost 9092

# List topics
kafka-topics --list --bootstrap-server localhost:9092
```

### OpenTelemetry Issues

- Check console output for initialization messages
- Verify OTLP endpoint is accessible
- Ensure API key is correct
- Set log level to `DiagLogLevel.DEBUG` in `tracing.js` for more details

## Configuration

### Disable Console Exporter

In `tracing.js`, comment out:

```javascript
// provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter));
```

### Change Service Name

In `tracing.js`, update:

```javascript
[SemanticResourceAttributes.SERVICE_NAME]: 'your-service-name'
```

### Disable Specific Instrumentations

In `tracing.js`, modify the `getNodeAutoInstrumentations` options:

```javascript
getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-fs': { enabled: false },
'@opentelemetry/instrumentation-dns': { enabled: false }
})
```

## Docker Setup (Optional)

For running PostgreSQL and Kafka in Docker:

```bash
# PostgreSQL
docker run -d --name postgres \
-e POSTGRES_USER=node \
-e POSTGRES_PASSWORD=nodepw \
-e POSTGRES_DB=nodedb \
-p 5432:5432 \
postgres:15

# Kafka (requires Zookeeper)
docker-compose up -d
```

Create a `docker-compose.yml`:

```yaml
version: '3'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181

kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
```

## License

MIT
109 changes: 109 additions & 0 deletions example-apps/otel-exporter-test/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* (c) Copyright IBM Corp. 2026
*/

'use strict';

// Load Instana collector from local repository
require('@instana/collector')();

const express = require('express');
const { Pool } = require('pg');
const { sendKafkaMessage, startKafkaConsumer } = require('./kafka');

const app = express();
app.use(express.json());

const PORT = 3000;

const pool = new Pool({
host: 'localhost',
port: 5432,
user: 'node',
password: 'nodepw',
database: 'nodedb'
});

// ---------------------------------------------------
// 1. HTTP ENTRY + HTTP EXIT
// ---------------------------------------------------

app.get('/external-api', async (req, res) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();

res.json({
success: true,
data
});
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);

res.status(500).json({
error: err.message
});
}
});

// ---------------------------------------------------
// 2. HTTP ENTRY + PG EXIT
// ---------------------------------------------------

app.get('/db', async (req, res) => {
try {
const result = await pool.query('SELECT NOW() as current_time');

res.json({
success: true,
rows: result.rows
});
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);

res.status(500).json({
error: err.message
});
}
});

// ---------------------------------------------------
// 3. HTTP ENTRY + KAFKA EXIT
// ---------------------------------------------------

app.post('/kafka', async (req, res) => {
try {
const payload = req.body || {
hello: 'world'
};

await sendKafkaMessage(payload);

res.json({
success: true,
sent: payload
});
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);

res.status(500).json({
error: err.message
});
}
});

// ---------------------------------------------------
// START
// ---------------------------------------------------

app.listen(PORT, async () => {
// eslint-disable-next-line no-console
console.log(`Server running on port ${PORT}`);

await startKafkaConsumer();
});

// Made with Bob
Loading