UNPKG

@erickluis00/otelviewer

Version:

Shared OpenTelemetry tracing utilities, types, and batch processor for Realtime OpenTelemetry Viewer [WIP]

204 lines 8.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.startRemoteExecutorServer = startRemoteExecutorServer; exports.stopRemoteExecutorServer = stopRemoteExecutorServer; exports.getRemoteExecutorPort = getRemoteExecutorPort; exports.autoStartRemoteExecutorIfNeeded = autoStartRemoteExecutorIfNeeded; const http_1 = require("http"); const tracing_utils_1 = require("./tracing-utils"); const api_1 = require("@opentelemetry/api"); let remoteExecutorServer = null; let serverPort = null; /** * Parse request body (JSON or form-encoded) */ function parseBody(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', (chunk) => { body += chunk.toString(); }); req.on('end', () => { try { const contentType = req.headers['content-type'] || ''; if (contentType.includes('application/x-www-form-urlencoded')) { // Parse form-encoded data const params = new URLSearchParams(body); const key = params.get('key'); const argsString = params.get('args'); const args = argsString ? JSON.parse(argsString) : []; resolve({ key, args }); } else { // Parse JSON (fallback) resolve(JSON.parse(body)); } } catch (error) { reject(new Error('Invalid request body')); } }); req.on('error', reject); }); } /** * Send JSON response */ function sendJSON(res, statusCode, data) { res.writeHead(statusCode, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }); res.end(JSON.stringify(data)); } /** * Start the remote executor server (DEV ONLY) */ function startRemoteExecutorServer(port = 3033) { return new Promise((resolve, reject) => { if (process.env.NODE_ENV === 'production') { console.warn('[OTEL Remote] Remote executor is disabled in production'); reject(new Error('Remote executor is disabled in production')); return; } if (remoteExecutorServer) { resolve(serverPort); return; } remoteExecutorServer = (0, http_1.createServer)(async (req, res) => { // Handle CORS preflight if (req.method === 'OPTIONS') { sendJSON(res, 200, { ok: true }); return; } const url = req.url || ''; if (url === '/' && req.method === 'GET') { sendJSON(res, 200, { ok: true }); return; } // List all executable methods if (url === '/remote-exec/list' && req.method === 'GET') { const executables = (0, tracing_utils_1.getRemoteExecutables)(); const list = Array.from(executables.entries()).map(([key, method]) => ({ key, className: method.className, methodName: method.methodName, isStatic: method.isStatic, spanPrefix: method.spanPrefix, icon: method.icon, iconColor: method.iconColor, })); sendJSON(res, 200, { success: true, data: list }); return; } // Execute a method if (url === '/remote-exec/execute' && req.method === 'POST') { try { const body = await parseBody(req); const { key, args = [] } = body; if (!key || typeof key !== 'string') { sendJSON(res, 400, { success: false, error: 'Missing or invalid key parameter' }); return; } if (!Array.isArray(args)) { sendJSON(res, 400, { success: false, error: 'args must be an array' }); return; } // Get traceId from the HTTP span (automatically created by OpenTelemetry) const activeSpan = api_1.trace.getActiveSpan(); const traceId = activeSpan?.spanContext().traceId; // Return traceId immediately sendJSON(res, 200, { success: true, traceId }); // Execute method in background (fire and forget) (0, tracing_utils_1.executeRemoteMethod)(key, args).catch((error) => { console.error('[OTEL Remote] Background execution error:', error); }); } catch (error) { // Only handle validation errors (before getting traceId) const errorMessage = error instanceof Error ? error.message : String(error); sendJSON(res, 500, { success: false, error: errorMessage }); } return; } // Health check if (url === '/remote-exec/health' && req.method === 'GET') { sendJSON(res, 200, { success: true, data: { status: 'ok', registeredMethods: (0, tracing_utils_1.getRemoteExecutables)().size } }); return; } // Not found sendJSON(res, 404, { success: false, error: 'Not found' }); }); remoteExecutorServer.on('error', (error) => { if (error.code === 'EADDRINUSE') { console.warn(`[OTEL Remote] Port ${port} is in use, trying ${port + 1}`); startRemoteExecutorServer(port + 1) .then(resolve) .catch(reject); } else { reject(error); } }); remoteExecutorServer.listen(port, '127.0.0.1', () => { serverPort = port; console.log(`\n${'='.repeat(60)}`); console.log(`[OTEL Remote] ✅ Executor server running on http://127.0.0.1:${port}`); console.log(`[OTEL Remote] 🌐 Tunnel URL: https://remote-runner-server.localto.net`); console.log(`[OTEL Remote] Available endpoints:`); console.log(` - GET /remote-exec/list - List all executable methods`); console.log(` - POST /remote-exec/execute - Execute a method`); console.log(` - GET /remote-exec/health - Health check`); console.log(`${'='.repeat(60)}\n`); resolve(port); }); }); } /** * Stop the remote executor server */ function stopRemoteExecutorServer() { return new Promise((resolve) => { if (!remoteExecutorServer) { resolve(); return; } remoteExecutorServer.close(() => { remoteExecutorServer = null; serverPort = null; resolve(); }); }); } /** * Get the current server port */ function getRemoteExecutorPort() { return serverPort; } /** * Auto-start server if there are any registered executables */ function autoStartRemoteExecutorIfNeeded(port = 3033) { if (process.env.NODE_ENV === 'production') { return; } // Delay to allow classes to be registered first setTimeout(() => { const executables = (0, tracing_utils_1.getRemoteExecutables)(); if (executables.size > 0 && !remoteExecutorServer) { startRemoteExecutorServer(port).catch((error) => { console.error('[OTEL Remote] Failed to start executor server:', error); }); } }, 1000); } //# sourceMappingURL=remote-executor.js.map