Implement OpenTelemetry middleware initialization and auto-start logic

This commit is contained in:
Adish M 2025-12-30 19:58:45 +05:30
parent 8ae3924968
commit 4f5d186455
3 changed files with 94 additions and 11 deletions

View file

@ -84,30 +84,41 @@ export async function handleLicensingInit(app: NestExpressApplication, logger: a
}
/**
* Handles OTEL initialization for Enterprise Edition
* Applies OTEL middleware to Express app
* Note: The OTEL SDK is started at import time in src/otel/tracing.ts
* This function only applies the middleware after the app is created
*/
export async function initializeOtel(app: NestExpressApplication, logger: any) {
// Check if OTEL is enabled
if (process.env.ENABLE_OTEL !== 'true') {
logger.log('OTEL disabled (ENABLE_OTEL not set to true)');
logger.log('⏭️ OTEL disabled (ENABLE_OTEL not set to true)');
return;
}
try {
logger.log('Initializing OpenTelemetry...');
const tooljetEdition = getTooljetEdition() as TOOLJET_EDITIONS;
const importPath = await getImportPath(false, tooljetEdition);
// Dynamically import the edition-specific listener
const { otelListener } = await import(`${importPath}/otel/listener`);
if (tooljetEdition !== TOOLJET_EDITIONS.EE) {
logger.log('⏭️ OTEL skipped - not Enterprise Edition');
return;
}
// Initialize the listener (CE will no-op, EE will set up OTEL)
await otelListener.initialize(app);
logger.log('🔭 Applying OpenTelemetry middleware...');
logger.log('✅ OpenTelemetry initialization completed');
// Import otelMiddleware from tracing.ts (use relative path for runtime compatibility)
const { otelMiddleware } = await import('../otel/tracing');
// Apply OTEL middleware to Express app
const expressApp = app.getHttpAdapter().getInstance();
expressApp.use(otelMiddleware);
logger.log('✅ OpenTelemetry middleware applied successfully');
logger.log(' - SDK: Already started at import time');
logger.log(' - Tracing: Enabled');
logger.log(' - Metrics: Enabled');
logger.log(' - Auto-instrumentation: Active');
} catch (error) {
logger.error('❌ Failed to initialize OpenTelemetry:', error);
logger.error('❌ Failed to apply OpenTelemetry middleware:', error);
// Don't throw - observability should never break the app
}
}

View file

@ -1,3 +1,4 @@
import './otel/tracing'; // CRITICAL: This MUST be the first import to ensure OTEL patches modules before they load
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { WsAdapter } from '@nestjs/platform-ws';

View file

@ -581,3 +581,74 @@ export const decrementActiveSessions = (attributes: {
activeSessionsCounter.add(-1, metricAttributes);
}
};
// ============================================================================
// AUTO-START: Initialize OTEL SDK immediately when this module is imported
// This MUST run before any other modules (http, pg, express, etc.) are loaded
// ============================================================================
// Load .env file before checking ENABLE_OTEL
// ConfigModule hasn't loaded yet, so we need to manually load .env
// Use the same pattern as the rest of the codebase (from database-config-utils.ts)
const fs = require('fs');
const path = require('path');
const dotenv = require('dotenv');
function loadEnvVars() {
const envFilePath = process.env.NODE_ENV === 'test'
? path.resolve(process.cwd(), '../.env.test')
: path.resolve(process.cwd(), '../.env');
if (fs.existsSync(envFilePath)) {
const envConfig = dotenv.parse(fs.readFileSync(envFilePath));
// Merge with existing process.env (existing env vars take precedence)
Object.assign(process.env, envConfig, process.env);
}
}
// Load environment variables
loadEnvVars();
console.log('[OTEL] Auto-start code reached');
console.log('[OTEL] ENABLE_OTEL:', process.env.ENABLE_OTEL);
let isInitialized = false;
if (process.env.ENABLE_OTEL === 'true' && !isInitialized) {
console.log('[OTEL] Condition met, checking edition...');
try {
// Check edition - only EE supports OTEL
// Use relative paths instead of TypeScript aliases for runtime compatibility
const { getTooljetEdition } = require('../helpers/utils.helper');
const { TOOLJET_EDITIONS } = require('../modules/app/constants');
const tooljetEdition = getTooljetEdition();
console.log('[OTEL] Edition:', tooljetEdition);
if (tooljetEdition === TOOLJET_EDITIONS.EE) {
console.log('[OTEL] Starting SDK at import time (before any modules load)...');
// Start OTEL SDK - this registers instrumentations immediately
// The patches are applied synchronously when instrumentations are registered
startOpenTelemetry()
.then(() => {
isInitialized = true;
console.log('[OTEL] ✅ SDK started successfully');
})
.catch((err) => {
console.error('[OTEL] ❌ Failed to start SDK:', err);
// Log the error but don't throw - observability should never break the app
});
// Mark as initializing to prevent double initialization
isInitialized = true;
} else {
console.log('[OTEL] ⏭️ Skipping OTEL - not Enterprise Edition');
}
} catch (error) {
console.error('[OTEL] Error during auto-start:', error);
}
} else {
console.log('[OTEL] Condition NOT met. ENABLE_OTEL:', process.env.ENABLE_OTEL, 'isInitialized:', isInitialized);
}