@omen.foundation/node-microservice-runtime 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collector-manager.cjs +56 -22
- package/dist/collector-manager.d.ts +14 -0
- package/dist/collector-manager.d.ts.map +1 -1
- package/dist/collector-manager.js +74 -28
- package/dist/collector-manager.js.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/collector-manager.ts +92 -33
- package/src/index.ts +2 -0
|
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.fetchClickHouseCredentials = fetchClickHouseCredentials;
|
|
7
7
|
exports.getClickHouseCredentialsStatus = getClickHouseCredentialsStatus;
|
|
8
8
|
exports.isCollectorRunning = isCollectorRunning;
|
|
9
|
+
exports.addAuthEnvironmentVars = addAuthEnvironmentVars;
|
|
9
10
|
exports.startCollector = startCollector;
|
|
11
|
+
exports.getCollectorProcessStatus = getCollectorProcessStatus;
|
|
10
12
|
exports.discoverOrStartCollector = discoverOrStartCollector;
|
|
11
13
|
const child_process_1 = require("child_process");
|
|
12
14
|
const fs_1 = require("fs");
|
|
@@ -19,6 +21,10 @@ const urls_js_1 = require("./utils/urls.js");
|
|
|
19
21
|
const COLLECTOR_VERSION = '1.0.1';
|
|
20
22
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
21
23
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
24
|
+
let globalCollectorProcess = null;
|
|
25
|
+
let globalCollectorStartError = null;
|
|
26
|
+
let globalCollectorExitCode = null;
|
|
27
|
+
let globalCollectorStderr = [];
|
|
22
28
|
const DISCOVERY_DELAY = 100;
|
|
23
29
|
const DISCOVERY_ATTEMPTS = 10;
|
|
24
30
|
function calculateSignature(pid, secret, uriPathAndQuery, body = null, version = '1') {
|
|
@@ -233,41 +239,34 @@ function addCollectorConfigurationToEnvironment() {
|
|
|
233
239
|
}
|
|
234
240
|
}
|
|
235
241
|
function addAuthEnvironmentVars(endpoint, username, password) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (!process.env.BEAM_CLICKHOUSE_USERNAME) {
|
|
240
|
-
process.env
|
|
241
|
-
}
|
|
242
|
-
if (!process.env.BEAM_CLICKHOUSE_PASSWORD) {
|
|
243
|
-
process.env.BEAM_CLICKHOUSE_PASSWORD = password;
|
|
242
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = endpoint;
|
|
243
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = username;
|
|
244
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = password;
|
|
245
|
+
if (!process.env.BEAM_CLICKHOUSE_ENDPOINT || !process.env.BEAM_CLICKHOUSE_USERNAME || !process.env.BEAM_CLICKHOUSE_PASSWORD) {
|
|
246
|
+
throw new Error(`Failed to set ClickHouse credentials in process.env - this should never happen in Node.js`);
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
249
|
async function startCollector(logger, otlpEndpoint, env) {
|
|
247
250
|
var _a, _b;
|
|
248
|
-
logger.info('[Collector] Resolving collector binary and config...');
|
|
249
|
-
const collectorInfo = await resolveCollector(true, logger);
|
|
250
|
-
if (!collectorInfo.binaryPath) {
|
|
251
|
-
logger.error('[Collector] Binary not found and download failed');
|
|
252
|
-
throw new Error('Collector binary not found and download failed');
|
|
253
|
-
}
|
|
254
|
-
if (!collectorInfo.configPath) {
|
|
255
|
-
logger.error('[Collector] Config not found and download failed');
|
|
256
|
-
throw new Error('Collector config not found and download failed');
|
|
257
|
-
}
|
|
258
|
-
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
259
|
-
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
260
|
-
addCollectorConfigurationToEnvironment();
|
|
261
251
|
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
262
252
|
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
263
253
|
let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
264
254
|
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
265
255
|
try {
|
|
256
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
266
257
|
const credentials = await fetchClickHouseCredentials(env);
|
|
267
258
|
clickhouseEndpoint = credentials.endpoint;
|
|
268
259
|
clickhouseUsername = credentials.username;
|
|
269
260
|
clickhousePassword = credentials.password;
|
|
270
261
|
addAuthEnvironmentVars(clickhouseEndpoint, clickhouseUsername, clickhousePassword);
|
|
262
|
+
const verifyEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
263
|
+
const verifyUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
264
|
+
const verifyPassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
265
|
+
if (!verifyEndpoint || !verifyUsername || !verifyPassword) {
|
|
266
|
+
logger.error(`[Collector] CRITICAL: Credentials were set but are missing from process.env! This should never happen.`);
|
|
267
|
+
throw new Error('Failed to persist ClickHouse credentials in process.env');
|
|
268
|
+
}
|
|
269
|
+
logger.info('[Collector] ClickHouse credentials fetched from API and verified in process.env');
|
|
271
270
|
}
|
|
272
271
|
catch (error) {
|
|
273
272
|
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -280,6 +279,19 @@ async function startCollector(logger, otlpEndpoint, env) {
|
|
|
280
279
|
logger.error(errorMsg);
|
|
281
280
|
throw new Error(errorMsg);
|
|
282
281
|
}
|
|
282
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
283
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
284
|
+
if (!collectorInfo.binaryPath) {
|
|
285
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
286
|
+
throw new Error('Collector binary not found and download failed');
|
|
287
|
+
}
|
|
288
|
+
if (!collectorInfo.configPath) {
|
|
289
|
+
logger.error('[Collector] Config not found and download failed');
|
|
290
|
+
throw new Error('Collector config not found and download failed');
|
|
291
|
+
}
|
|
292
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
293
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
294
|
+
addCollectorConfigurationToEnvironment();
|
|
283
295
|
let localEndpoint = otlpEndpoint;
|
|
284
296
|
if (!localEndpoint) {
|
|
285
297
|
localEndpoint = '0.0.0.0:4318';
|
|
@@ -295,16 +307,28 @@ async function startCollector(logger, otlpEndpoint, env) {
|
|
|
295
307
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
296
308
|
detached: false,
|
|
297
309
|
});
|
|
310
|
+
globalCollectorProcess = collectorProcess;
|
|
311
|
+
globalCollectorStartError = null;
|
|
312
|
+
globalCollectorExitCode = null;
|
|
313
|
+
globalCollectorStderr = [];
|
|
298
314
|
(_a = collectorProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
299
315
|
logger.debug(`[Collector] ${data.toString().trim()}`);
|
|
300
316
|
});
|
|
301
317
|
(_b = collectorProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
302
|
-
|
|
318
|
+
const errorLine = data.toString().trim();
|
|
319
|
+
globalCollectorStderr.push(errorLine);
|
|
320
|
+
if (globalCollectorStderr.length > 50) {
|
|
321
|
+
globalCollectorStderr.shift();
|
|
322
|
+
}
|
|
323
|
+
logger.debug(`[Collector ERR] ${errorLine}`);
|
|
303
324
|
});
|
|
304
325
|
collectorProcess.on('error', (err) => {
|
|
326
|
+
globalCollectorStartError = err.message;
|
|
305
327
|
logger.error(`[Collector] Failed to start: ${err.message}`);
|
|
306
328
|
});
|
|
307
329
|
collectorProcess.on('exit', (code) => {
|
|
330
|
+
globalCollectorExitCode = code;
|
|
331
|
+
globalCollectorProcess = null;
|
|
308
332
|
logger.warn(`[Collector] Process exited with code ${code}`);
|
|
309
333
|
});
|
|
310
334
|
logger.info(`[Collector] Started with PID ${collectorProcess.pid}, endpoint: ${localEndpoint}`);
|
|
@@ -313,6 +337,16 @@ async function startCollector(logger, otlpEndpoint, env) {
|
|
|
313
337
|
endpoint: `http://${localEndpoint}`,
|
|
314
338
|
};
|
|
315
339
|
}
|
|
340
|
+
function getCollectorProcessStatus() {
|
|
341
|
+
var _a;
|
|
342
|
+
return {
|
|
343
|
+
hasProcess: globalCollectorProcess !== null,
|
|
344
|
+
pid: (_a = globalCollectorProcess === null || globalCollectorProcess === void 0 ? void 0 : globalCollectorProcess.pid) !== null && _a !== void 0 ? _a : null,
|
|
345
|
+
exitCode: globalCollectorExitCode,
|
|
346
|
+
startError: globalCollectorStartError,
|
|
347
|
+
stderr: [...globalCollectorStderr],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
316
350
|
async function discoverOrStartCollector(logger, standardOtelEnabled, env) {
|
|
317
351
|
if (!standardOtelEnabled) {
|
|
318
352
|
return null;
|
|
@@ -36,6 +36,10 @@ export declare function getClickHouseCredentialsStatus(): {
|
|
|
36
36
|
* Checks if collector is already running via UDP discovery
|
|
37
37
|
*/
|
|
38
38
|
export declare function isCollectorRunning(): Promise<CollectorStatus>;
|
|
39
|
+
/**
|
|
40
|
+
* Sets ClickHouse credentials in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
41
|
+
*/
|
|
42
|
+
export declare function addAuthEnvironmentVars(endpoint: string, username: string, password: string): void;
|
|
39
43
|
/**
|
|
40
44
|
* Starts the OpenTelemetry collector process
|
|
41
45
|
*/
|
|
@@ -43,6 +47,16 @@ export declare function startCollector(logger: Logger, otlpEndpoint?: string, en
|
|
|
43
47
|
process: ChildProcess;
|
|
44
48
|
endpoint: string;
|
|
45
49
|
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Gets detailed collector status including process info and errors
|
|
52
|
+
*/
|
|
53
|
+
export declare function getCollectorProcessStatus(): {
|
|
54
|
+
hasProcess: boolean;
|
|
55
|
+
pid: number | null;
|
|
56
|
+
exitCode: number | null;
|
|
57
|
+
startError: string | null;
|
|
58
|
+
stderr: string[];
|
|
59
|
+
};
|
|
46
60
|
/**
|
|
47
61
|
* Discovers or starts the collector and returns the OTLP endpoint
|
|
48
62
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-manager.d.ts","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAC;AAMpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"collector-manager.d.ts","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAC;AAMpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAcD;;GAEG;AACH,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqCD;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,qBAAqB,CAAC,CAwChC;AA+ID;;GAEG;AACH,wBAAgB,8BAA8B,IAAI;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,aAAa,GAAG,KAAK,GAAG,SAAS,CAAC;CAC3C,CAoBA;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAqBnE;AA0BD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYjG;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,GAAG,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAuItD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAQA;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,OAAO,EAC5B,GAAG,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmCxB"}
|
|
@@ -9,6 +9,11 @@ import { hostToHttpUrl } from './utils/urls.js';
|
|
|
9
9
|
const COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)
|
|
10
10
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
11
11
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
12
|
+
// Global storage for collector process and errors (to prevent GC and track status)
|
|
13
|
+
let globalCollectorProcess = null;
|
|
14
|
+
let globalCollectorStartError = null;
|
|
15
|
+
let globalCollectorExitCode = null;
|
|
16
|
+
let globalCollectorStderr = [];
|
|
12
17
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
13
18
|
const DISCOVERY_ATTEMPTS = 10;
|
|
14
19
|
/**
|
|
@@ -276,51 +281,49 @@ function addCollectorConfigurationToEnvironment() {
|
|
|
276
281
|
/**
|
|
277
282
|
* Sets ClickHouse credentials in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
278
283
|
*/
|
|
279
|
-
function addAuthEnvironmentVars(endpoint, username, password) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
284
|
+
export function addAuthEnvironmentVars(endpoint, username, password) {
|
|
285
|
+
// Always set the values when explicitly provided (matching C# behavior)
|
|
286
|
+
// This ensures credentials fetched from API override any existing (potentially empty) values
|
|
287
|
+
// In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process
|
|
288
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = endpoint;
|
|
289
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = username;
|
|
290
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = password;
|
|
291
|
+
// Verify they were actually set (for debugging)
|
|
292
|
+
if (!process.env.BEAM_CLICKHOUSE_ENDPOINT || !process.env.BEAM_CLICKHOUSE_USERNAME || !process.env.BEAM_CLICKHOUSE_PASSWORD) {
|
|
293
|
+
throw new Error(`Failed to set ClickHouse credentials in process.env - this should never happen in Node.js`);
|
|
288
294
|
}
|
|
289
295
|
}
|
|
290
296
|
/**
|
|
291
297
|
* Starts the OpenTelemetry collector process
|
|
292
298
|
*/
|
|
293
299
|
export async function startCollector(logger, otlpEndpoint, env) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
logger.error('[Collector] Binary not found and download failed');
|
|
298
|
-
throw new Error('Collector binary not found and download failed');
|
|
299
|
-
}
|
|
300
|
-
if (!collectorInfo.configPath) {
|
|
301
|
-
logger.error('[Collector] Config not found and download failed');
|
|
302
|
-
throw new Error('Collector config not found and download failed');
|
|
303
|
-
}
|
|
304
|
-
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
305
|
-
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
306
|
-
// Set default collector configuration environment variables in process.env
|
|
307
|
-
// This ensures they're available to both the Node.js process and the collector child process
|
|
308
|
-
// Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()
|
|
309
|
-
addCollectorConfigurationToEnvironment();
|
|
310
|
-
// Fetch ClickHouse credentials from Beamable API if not in environment variables
|
|
300
|
+
// CRITICAL: Fetch credentials FIRST, before resolving collector binary/config
|
|
301
|
+
// This ensures credentials are in process.env even if collector download fails
|
|
302
|
+
// The credentials will persist in process.env for the lifetime of the process.
|
|
311
303
|
// Per Gabriel: "these OTEL related ones you need to do it yourself at the beginning of the microservice startup"
|
|
312
304
|
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
313
305
|
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
314
306
|
let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
315
307
|
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
316
308
|
try {
|
|
309
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
317
310
|
const credentials = await fetchClickHouseCredentials(env);
|
|
318
311
|
clickhouseEndpoint = credentials.endpoint;
|
|
319
312
|
clickhouseUsername = credentials.username;
|
|
320
313
|
clickhousePassword = credentials.password;
|
|
321
|
-
// Set them in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
314
|
+
// CRITICAL: Set them in process.env IMMEDIATELY after fetching (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
315
|
+
// In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process
|
|
322
316
|
// This ensures they're available to both the Node.js process and inherited by the collector child process
|
|
323
317
|
addAuthEnvironmentVars(clickhouseEndpoint, clickhouseUsername, clickhousePassword);
|
|
318
|
+
// Verify they were set (defensive check - should never fail in Node.js)
|
|
319
|
+
const verifyEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
320
|
+
const verifyUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
321
|
+
const verifyPassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
322
|
+
if (!verifyEndpoint || !verifyUsername || !verifyPassword) {
|
|
323
|
+
logger.error(`[Collector] CRITICAL: Credentials were set but are missing from process.env! This should never happen.`);
|
|
324
|
+
throw new Error('Failed to persist ClickHouse credentials in process.env');
|
|
325
|
+
}
|
|
326
|
+
logger.info('[Collector] ClickHouse credentials fetched from API and verified in process.env');
|
|
324
327
|
}
|
|
325
328
|
catch (error) {
|
|
326
329
|
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -335,6 +338,23 @@ export async function startCollector(logger, otlpEndpoint, env) {
|
|
|
335
338
|
logger.error(errorMsg);
|
|
336
339
|
throw new Error(errorMsg);
|
|
337
340
|
}
|
|
341
|
+
// Now resolve collector binary and config (after credentials are fetched and set)
|
|
342
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
343
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
344
|
+
if (!collectorInfo.binaryPath) {
|
|
345
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
346
|
+
throw new Error('Collector binary not found and download failed');
|
|
347
|
+
}
|
|
348
|
+
if (!collectorInfo.configPath) {
|
|
349
|
+
logger.error('[Collector] Config not found and download failed');
|
|
350
|
+
throw new Error('Collector config not found and download failed');
|
|
351
|
+
}
|
|
352
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
353
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
354
|
+
// Set default collector configuration environment variables in process.env
|
|
355
|
+
// This ensures they're available to both the Node.js process and the collector child process
|
|
356
|
+
// Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()
|
|
357
|
+
addCollectorConfigurationToEnvironment();
|
|
338
358
|
// Determine OTLP endpoint
|
|
339
359
|
// Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())
|
|
340
360
|
let localEndpoint = otlpEndpoint;
|
|
@@ -360,16 +380,30 @@ export async function startCollector(logger, otlpEndpoint, env) {
|
|
|
360
380
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
361
381
|
detached: false,
|
|
362
382
|
});
|
|
383
|
+
// Store globally to prevent GC and track status
|
|
384
|
+
globalCollectorProcess = collectorProcess;
|
|
385
|
+
globalCollectorStartError = null;
|
|
386
|
+
globalCollectorExitCode = null;
|
|
387
|
+
globalCollectorStderr = [];
|
|
363
388
|
collectorProcess.stdout?.on('data', (data) => {
|
|
364
389
|
logger.debug(`[Collector] ${data.toString().trim()}`);
|
|
365
390
|
});
|
|
366
391
|
collectorProcess.stderr?.on('data', (data) => {
|
|
367
|
-
|
|
392
|
+
const errorLine = data.toString().trim();
|
|
393
|
+
globalCollectorStderr.push(errorLine);
|
|
394
|
+
// Keep only last 50 lines to prevent memory issues
|
|
395
|
+
if (globalCollectorStderr.length > 50) {
|
|
396
|
+
globalCollectorStderr.shift();
|
|
397
|
+
}
|
|
398
|
+
logger.debug(`[Collector ERR] ${errorLine}`);
|
|
368
399
|
});
|
|
369
400
|
collectorProcess.on('error', (err) => {
|
|
401
|
+
globalCollectorStartError = err.message;
|
|
370
402
|
logger.error(`[Collector] Failed to start: ${err.message}`);
|
|
371
403
|
});
|
|
372
404
|
collectorProcess.on('exit', (code) => {
|
|
405
|
+
globalCollectorExitCode = code;
|
|
406
|
+
globalCollectorProcess = null; // Clear reference when process exits
|
|
373
407
|
logger.warn(`[Collector] Process exited with code ${code}`);
|
|
374
408
|
});
|
|
375
409
|
logger.info(`[Collector] Started with PID ${collectorProcess.pid}, endpoint: ${localEndpoint}`);
|
|
@@ -378,6 +412,18 @@ export async function startCollector(logger, otlpEndpoint, env) {
|
|
|
378
412
|
endpoint: `http://${localEndpoint}`,
|
|
379
413
|
};
|
|
380
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Gets detailed collector status including process info and errors
|
|
417
|
+
*/
|
|
418
|
+
export function getCollectorProcessStatus() {
|
|
419
|
+
return {
|
|
420
|
+
hasProcess: globalCollectorProcess !== null,
|
|
421
|
+
pid: globalCollectorProcess?.pid ?? null,
|
|
422
|
+
exitCode: globalCollectorExitCode,
|
|
423
|
+
startError: globalCollectorStartError,
|
|
424
|
+
stderr: [...globalCollectorStderr], // Return a copy
|
|
425
|
+
};
|
|
426
|
+
}
|
|
381
427
|
/**
|
|
382
428
|
* Discovers or starts the collector and returns the OTLP endpoint
|
|
383
429
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-manager.js","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, ChildProcess } from 'child_process';\r\nimport { createWriteStream, existsSync, chmodSync, mkdirSync } from 'fs';\r\nimport { join, dirname } from 'path';\r\nimport { pipeline } from 'stream/promises';\r\nimport { createGunzip } from 'zlib';\r\nimport { createHash } from 'node:crypto';\r\nimport type { Logger } from 'pino';\r\nimport dgram from 'dgram';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { hostToHttpUrl } from './utils/urls.js';\r\n\r\n// Protocol is httpprotobuf \r\n\r\ninterface CollectorDiscoveryEntry {\r\n version: string;\r\n status: string;\r\n pid: number;\r\n otlpEndpoint: string;\r\n}\r\n\r\nexport interface CollectorStatus {\r\n isRunning: boolean;\r\n isReady: boolean;\r\n pid: number;\r\n otlpEndpoint?: string;\r\n version?: string;\r\n}\r\n\r\nconst COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)\r\nconst COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;\r\nconst DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);\r\nconst DISCOVERY_DELAY = 100; // milliseconds\r\nconst DISCOVERY_ATTEMPTS = 10;\r\n\r\n/**\r\n * ClickHouse credentials response from Beamable API\r\n */\r\ninterface ClickHouseCredentials {\r\n endpoint: string;\r\n expiresAt?: string;\r\n password: string;\r\n username: string;\r\n}\r\n\r\n/**\r\n * Calculates Beamable signature for signed requests\r\n * Signature format: MD5(secret + pid + version + uriPathAndQuery + body) as Base64\r\n * Matches C# SignedRequesterHelper.CalculateSignature\r\n */\r\nfunction calculateSignature(\r\n pid: string,\r\n secret: string,\r\n uriPathAndQuery: string,\r\n body: string | null = null,\r\n version: string = '1'\r\n): string {\r\n let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;\r\n if (body) {\r\n dataToSign += body;\r\n }\r\n \r\n // MD5 hash to Base64 (matching C# implementation)\r\n const hash = createHash('md5').update(dataToSign, 'utf8').digest('base64');\r\n return hash;\r\n}\r\n\r\n/**\r\n * Extracts path and query from a URL\r\n */\r\nfunction getPathAndQuery(url: string): string {\r\n try {\r\n const urlObj = new URL(url);\r\n return urlObj.pathname + urlObj.search;\r\n } catch {\r\n // If URL parsing fails, return as-is\r\n return url;\r\n }\r\n}\r\n\r\n/**\r\n * Fetches ClickHouse credentials from Beamable API\r\n * GET /api/beamo/otel/auth/writer/config\r\n * Requires signed request authentication (X-BEAM-SIGNATURE header)\r\n */\r\nexport async function fetchClickHouseCredentials(\r\n env: EnvironmentConfig\r\n): Promise<ClickHouseCredentials> {\r\n const apiUrl = hostToHttpUrl(env.host);\r\n const uriPath = '/api/beamo/otel/auth/writer/config';\r\n const configUrl = new URL(uriPath, apiUrl).toString();\r\n \r\n // Get secret from environment (required for signed requests)\r\n const secret = process.env.SECRET;\r\n if (!secret) {\r\n throw new Error('SECRET environment variable is required to fetch ClickHouse credentials');\r\n }\r\n \r\n // Calculate signature for signed request (matching C# HttpSignedRequester)\r\n const pathAndQuery = getPathAndQuery(uriPath);\r\n const signature = calculateSignature(env.pid, secret, pathAndQuery, null, '1');\r\n \r\n // Build headers with signed authentication (matching C# HttpSignedRequester)\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,\r\n 'X-BEAM-SIGNATURE': signature,\r\n };\r\n \r\n const response = await fetch(configUrl, {\r\n method: 'GET',\r\n headers,\r\n });\r\n \r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'Unknown error');\r\n throw new Error(`Failed to fetch ClickHouse credentials from ${configUrl}: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);\r\n }\r\n \r\n const credentials = await response.json() as ClickHouseCredentials;\r\n \r\n if (!credentials.endpoint || !credentials.username || !credentials.password) {\r\n throw new Error('Invalid ClickHouse credentials response: missing required fields');\r\n }\r\n \r\n return credentials;\r\n}\r\n\r\n/**\r\n * Gets the collector storage directory (similar to C# LocalApplicationData/beam/collectors/version)\r\n */\r\nfunction getCollectorStoragePath(): string {\r\n // Use temp directory for now - in containers this should be writable\r\n const tempDir = process.env.TMPDIR || process.env.TMP || '/tmp';\r\n return join(tempDir, 'beam', 'collectors', COLLECTOR_VERSION);\r\n}\r\n\r\n/**\r\n * Gets the collector binary name for the current platform\r\n */\r\nfunction getCollectorBinaryName(): string {\r\n const platform = process.platform;\r\n const arch = process.arch;\r\n \r\n // Match C# naming: collector-{osArchSuffix}\r\n // C# returns \"collector-linux-amd64\", not \"beamable-collector-linux-amd64\"\r\n if (platform === 'linux' && arch === 'x64') {\r\n return 'collector-linux-amd64';\r\n } else if (platform === 'linux' && arch === 'arm64') {\r\n return 'collector-linux-arm64';\r\n } else if (platform === 'darwin' && arch === 'x64') {\r\n return 'collector-darwin-amd64';\r\n } else if (platform === 'darwin' && arch === 'arm64') {\r\n return 'collector-darwin-arm64';\r\n } else if (platform === 'win32' && arch === 'x64') {\r\n return 'collector-windows-amd64.exe';\r\n }\r\n \r\n throw new Error(`Unsupported platform: ${platform} ${arch}`);\r\n}\r\n\r\n/**\r\n * Downloads and decompresses a gzipped file\r\n */\r\nasync function downloadAndDecompressGzip(url: string, outputPath: string, makeExecutable: boolean = false): Promise<void> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`Failed to download ${url}: ${response.statusText}`);\r\n }\r\n \r\n const dir = dirname(outputPath);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n \r\n const gunzip = createGunzip();\r\n const writeStream = createWriteStream(outputPath);\r\n \r\n await pipeline(response.body as any, gunzip, writeStream);\r\n \r\n if (makeExecutable && process.platform !== 'win32') {\r\n try {\r\n chmodSync(outputPath, 0o755);\r\n } catch (error) {\r\n console.error(`Failed to make ${outputPath} executable:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Resolves the collector binary and config, downloading if needed\r\n */\r\nasync function resolveCollector(allowDownload: boolean = true, logger?: Logger): Promise<{ binaryPath: string | null; configPath: string | null }> {\r\n const basePath = getCollectorStoragePath();\r\n const binaryName = getCollectorBinaryName();\r\n const configName = 'clickhouse-config.yaml';\r\n \r\n const binaryPath = join(basePath, binaryName);\r\n const configPath = join(basePath, configName);\r\n \r\n const itemsToDownload: Array<{ url: string; path: string; executable: boolean }> = [];\r\n \r\n if (!existsSync(binaryPath) && allowDownload) {\r\n const binaryUrl = `${COLLECTOR_DOWNLOAD_BASE}/${binaryName}.gz`;\r\n itemsToDownload.push({ url: binaryUrl, path: binaryPath, executable: true });\r\n logger?.info(`[Collector] Will download binary from: ${binaryUrl}`);\r\n } else if (existsSync(binaryPath)) {\r\n logger?.info(`[Collector] Binary found at: ${binaryPath}`);\r\n }\r\n \r\n if (!existsSync(configPath) && allowDownload) {\r\n const configUrl = `${COLLECTOR_DOWNLOAD_BASE}/${configName}.gz`;\r\n itemsToDownload.push({ url: configUrl, path: configPath, executable: false });\r\n logger?.info(`[Collector] Will download config from: ${configUrl}`);\r\n } else if (existsSync(configPath)) {\r\n logger?.info(`[Collector] Config found at: ${configPath}`);\r\n }\r\n \r\n for (const item of itemsToDownload) {\r\n logger?.info(`[Collector] Downloading ${item.url}...`);\r\n await downloadAndDecompressGzip(item.url, item.path, item.executable);\r\n logger?.info(`[Collector] Downloaded to ${item.path}`);\r\n }\r\n \r\n return {\r\n binaryPath: existsSync(binaryPath) ? binaryPath : null,\r\n configPath: existsSync(configPath) ? configPath : null,\r\n };\r\n}\r\n\r\n/**\r\n * Discovers collector via UDP broadcast\r\n */\r\nfunction discoverCollectorViaUDP(timeoutMs: number = 5000): Promise<CollectorDiscoveryEntry | null> {\r\n return new Promise((resolve) => {\r\n const socket = dgram.createSocket('udp4');\r\n const discovered: CollectorDiscoveryEntry[] = [];\r\n let timeout: NodeJS.Timeout;\r\n \r\n socket.on('message', (msg) => {\r\n try {\r\n const message = JSON.parse(msg.toString()) as CollectorDiscoveryEntry;\r\n // Check if version matches\r\n if (message.version === COLLECTOR_VERSION && message.status === 'READY') {\r\n discovered.push(message);\r\n }\r\n } catch (error) {\r\n // Ignore parse errors\r\n }\r\n });\r\n \r\n socket.on('error', () => {\r\n clearTimeout(timeout);\r\n socket.close();\r\n resolve(null);\r\n });\r\n \r\n socket.bind(() => {\r\n socket.setBroadcast(true);\r\n \r\n timeout = setTimeout(() => {\r\n socket.close();\r\n // Return the first discovered collector\r\n resolve(discovered.length > 0 ? discovered[0] : null);\r\n }, timeoutMs);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Gets the current status of ClickHouse credentials (from env or API)\r\n */\r\nexport function getClickHouseCredentialsStatus(): {\r\n hasEndpoint: boolean;\r\n hasUsername: boolean;\r\n hasPassword: boolean;\r\n source: 'environment' | 'api' | 'missing';\r\n} {\r\n const hasEndpoint = !!process.env.BEAM_CLICKHOUSE_ENDPOINT;\r\n const hasUsername = !!process.env.BEAM_CLICKHOUSE_USERNAME;\r\n const hasPassword = !!process.env.BEAM_CLICKHOUSE_PASSWORD;\r\n \r\n if (hasEndpoint && hasUsername && hasPassword) {\r\n return {\r\n hasEndpoint: true,\r\n hasUsername: true,\r\n hasPassword: true,\r\n source: 'environment',\r\n };\r\n }\r\n \r\n return {\r\n hasEndpoint,\r\n hasUsername,\r\n hasPassword,\r\n source: 'missing',\r\n };\r\n}\r\n\r\n/**\r\n * Checks if collector is already running via UDP discovery\r\n */\r\nexport async function isCollectorRunning(): Promise<CollectorStatus> {\r\n try {\r\n const discovered = await discoverCollectorViaUDP(2000);\r\n if (discovered) {\r\n return {\r\n isRunning: true,\r\n isReady: discovered.status === 'READY',\r\n pid: discovered.pid,\r\n otlpEndpoint: discovered.otlpEndpoint,\r\n version: discovered.version,\r\n };\r\n }\r\n } catch (error) {\r\n // Discovery failed, collector probably not running\r\n }\r\n \r\n return {\r\n isRunning: false,\r\n isReady: false,\r\n pid: 0,\r\n };\r\n}\r\n\r\n/**\r\n * Sets default collector configuration environment variables in process.env\r\n * Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()\r\n */\r\nfunction addCollectorConfigurationToEnvironment(): void {\r\n const defaults: Record<string, string> = {\r\n BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: '5s',\r\n BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: '5000',\r\n BEAM_CLICKHOUSE_EXPORTER_TIMEOUT: '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE: '1000',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED: 'true',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL: '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL: '30s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME: '300s',\r\n };\r\n \r\n // Only set if not already set (matching C# behavior)\r\n for (const [key, defaultValue] of Object.entries(defaults)) {\r\n if (!process.env[key]) {\r\n process.env[key] = defaultValue;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Sets ClickHouse credentials in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)\r\n */\r\nfunction addAuthEnvironmentVars(endpoint: string, username: string, password: string): void {\r\n if (!process.env.BEAM_CLICKHOUSE_ENDPOINT) {\r\n process.env.BEAM_CLICKHOUSE_ENDPOINT = endpoint;\r\n }\r\n if (!process.env.BEAM_CLICKHOUSE_USERNAME) {\r\n process.env.BEAM_CLICKHOUSE_USERNAME = username;\r\n }\r\n if (!process.env.BEAM_CLICKHOUSE_PASSWORD) {\r\n process.env.BEAM_CLICKHOUSE_PASSWORD = password;\r\n }\r\n}\r\n\r\n/**\r\n * Starts the OpenTelemetry collector process\r\n */\r\nexport async function startCollector(\r\n logger: Logger,\r\n otlpEndpoint?: string,\r\n env?: EnvironmentConfig\r\n): Promise<{ process: ChildProcess; endpoint: string }> {\r\n logger.info('[Collector] Resolving collector binary and config...');\r\n const collectorInfo = await resolveCollector(true, logger);\r\n \r\n if (!collectorInfo.binaryPath) {\r\n logger.error('[Collector] Binary not found and download failed');\r\n throw new Error('Collector binary not found and download failed');\r\n }\r\n \r\n if (!collectorInfo.configPath) {\r\n logger.error('[Collector] Config not found and download failed');\r\n throw new Error('Collector config not found and download failed');\r\n }\r\n \r\n logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);\r\n logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);\r\n \r\n // Set default collector configuration environment variables in process.env\r\n // This ensures they're available to both the Node.js process and the collector child process\r\n // Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()\r\n addCollectorConfigurationToEnvironment();\r\n \r\n // Fetch ClickHouse credentials from Beamable API if not in environment variables\r\n // Per Gabriel: \"these OTEL related ones you need to do it yourself at the beginning of the microservice startup\"\r\n let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;\r\n let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;\r\n let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;\r\n \r\n if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {\r\n try {\r\n const credentials = await fetchClickHouseCredentials(env);\r\n clickhouseEndpoint = credentials.endpoint;\r\n clickhouseUsername = credentials.username;\r\n clickhousePassword = credentials.password;\r\n \r\n // Set them in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)\r\n // This ensures they're available to both the Node.js process and inherited by the collector child process\r\n addAuthEnvironmentVars(clickhouseEndpoint, clickhouseUsername, clickhousePassword);\r\n } catch (error) {\r\n const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;\r\n logger.error(errorMsg);\r\n throw new Error(errorMsg);\r\n }\r\n }\r\n \r\n // Validate required environment variables (matching C# behavior)\r\n // These must be set before starting the collector, otherwise it will fail\r\n if (!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) {\r\n const errorMsg = `[Collector] Required ClickHouse credentials are missing. Set BEAM_CLICKHOUSE_ENDPOINT, BEAM_CLICKHOUSE_USERNAME, and BEAM_CLICKHOUSE_PASSWORD, or ensure the API endpoint is accessible.`;\r\n logger.error(errorMsg);\r\n throw new Error(errorMsg);\r\n }\r\n \r\n // Determine OTLP endpoint\r\n // Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())\r\n let localEndpoint = otlpEndpoint;\r\n if (!localEndpoint) {\r\n // For now, use default OTLP HTTP port\r\n // In production, this would be discovered or set by Beamable\r\n localEndpoint = '0.0.0.0:4318';\r\n }\r\n localEndpoint = localEndpoint.replace(/^http:\\/\\//, '').replace(/^https:\\/\\//, '');\r\n \r\n // Set environment variables for collector\r\n // Note: BEAM_CLICKHOUSE_ENDPOINT is for collector → ClickHouse communication\r\n // This is different from the OTLP endpoint (microservice → collector)\r\n // The collector process will inherit all environment variables from process.env automatically\r\n // We only need to set the OTLP endpoint and discovery port explicitly here\r\n const collectorEnv: NodeJS.ProcessEnv = {\r\n ...process.env, // Inherit all process.env (including credentials and config we just set)\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),\r\n };\r\n \r\n // Start collector process\r\n const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {\r\n env: collectorEnv,\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n detached: false,\r\n });\r\n \r\n collectorProcess.stdout?.on('data', (data) => {\r\n logger.debug(`[Collector] ${data.toString().trim()}`);\r\n });\r\n \r\n collectorProcess.stderr?.on('data', (data) => {\r\n logger.debug(`[Collector ERR] ${data.toString().trim()}`);\r\n });\r\n \r\n collectorProcess.on('error', (err) => {\r\n logger.error(`[Collector] Failed to start: ${err.message}`);\r\n });\r\n \r\n collectorProcess.on('exit', (code) => {\r\n logger.warn(`[Collector] Process exited with code ${code}`);\r\n });\r\n \r\n logger.info(`[Collector] Started with PID ${collectorProcess.pid}, endpoint: ${localEndpoint}`);\r\n \r\n return {\r\n process: collectorProcess,\r\n endpoint: `http://${localEndpoint}`,\r\n };\r\n}\r\n\r\n/**\r\n * Discovers or starts the collector and returns the OTLP endpoint\r\n */\r\nexport async function discoverOrStartCollector(\r\n logger: Logger,\r\n standardOtelEnabled: boolean,\r\n env?: EnvironmentConfig\r\n): Promise<string | null> {\r\n if (!standardOtelEnabled) {\r\n return null;\r\n }\r\n \r\n // First, check if collector is already running\r\n const status = await isCollectorRunning();\r\n if (status.isRunning && status.isReady && status.otlpEndpoint) {\r\n logger.info(`[Collector] Found running collector at ${status.otlpEndpoint}`);\r\n return `http://${status.otlpEndpoint}`;\r\n }\r\n \r\n // Collector not running - start it\r\n try {\r\n logger.info('[Collector] Starting OpenTelemetry collector...');\r\n const { endpoint } = await startCollector(logger, undefined, env);\r\n \r\n // Wait a bit for collector to start and become ready\r\n // Try to discover it\r\n for (let i = 0; i < DISCOVERY_ATTEMPTS; i++) {\r\n await new Promise(resolve => setTimeout(resolve, DISCOVERY_DELAY));\r\n const newStatus = await isCollectorRunning();\r\n if (newStatus.isRunning && newStatus.isReady) {\r\n logger.info(`[Collector] Collector is ready at ${newStatus.otlpEndpoint || endpoint}`);\r\n return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;\r\n }\r\n }\r\n \r\n // Return the endpoint we started with, even if discovery didn't find it yet\r\n logger.warn('[Collector] Collector started but not yet ready, using configured endpoint');\r\n return endpoint;\r\n } catch (err) {\r\n logger.error(`[Collector] Failed to start collector: ${err instanceof Error ? err.message : String(err)}`);\r\n return null;\r\n }\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"file":"collector-manager.js","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, ChildProcess } from 'child_process';\r\nimport { createWriteStream, existsSync, chmodSync, mkdirSync } from 'fs';\r\nimport { join, dirname } from 'path';\r\nimport { pipeline } from 'stream/promises';\r\nimport { createGunzip } from 'zlib';\r\nimport { createHash } from 'node:crypto';\r\nimport type { Logger } from 'pino';\r\nimport dgram from 'dgram';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { hostToHttpUrl } from './utils/urls.js';\r\n\r\n// Protocol is httpprotobuf \r\n\r\ninterface CollectorDiscoveryEntry {\r\n version: string;\r\n status: string;\r\n pid: number;\r\n otlpEndpoint: string;\r\n}\r\n\r\nexport interface CollectorStatus {\r\n isRunning: boolean;\r\n isReady: boolean;\r\n pid: number;\r\n otlpEndpoint?: string;\r\n version?: string;\r\n}\r\n\r\nconst COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)\r\nconst COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;\r\nconst DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);\r\n\r\n// Global storage for collector process and errors (to prevent GC and track status)\r\nlet globalCollectorProcess: ChildProcess | null = null;\r\nlet globalCollectorStartError: string | null = null;\r\nlet globalCollectorExitCode: number | null = null;\r\nlet globalCollectorStderr: string[] = [];\r\nconst DISCOVERY_DELAY = 100; // milliseconds\r\nconst DISCOVERY_ATTEMPTS = 10;\r\n\r\n/**\r\n * ClickHouse credentials response from Beamable API\r\n */\r\ninterface ClickHouseCredentials {\r\n endpoint: string;\r\n expiresAt?: string;\r\n password: string;\r\n username: string;\r\n}\r\n\r\n/**\r\n * Calculates Beamable signature for signed requests\r\n * Signature format: MD5(secret + pid + version + uriPathAndQuery + body) as Base64\r\n * Matches C# SignedRequesterHelper.CalculateSignature\r\n */\r\nfunction calculateSignature(\r\n pid: string,\r\n secret: string,\r\n uriPathAndQuery: string,\r\n body: string | null = null,\r\n version: string = '1'\r\n): string {\r\n let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;\r\n if (body) {\r\n dataToSign += body;\r\n }\r\n \r\n // MD5 hash to Base64 (matching C# implementation)\r\n const hash = createHash('md5').update(dataToSign, 'utf8').digest('base64');\r\n return hash;\r\n}\r\n\r\n/**\r\n * Extracts path and query from a URL\r\n */\r\nfunction getPathAndQuery(url: string): string {\r\n try {\r\n const urlObj = new URL(url);\r\n return urlObj.pathname + urlObj.search;\r\n } catch {\r\n // If URL parsing fails, return as-is\r\n return url;\r\n }\r\n}\r\n\r\n/**\r\n * Fetches ClickHouse credentials from Beamable API\r\n * GET /api/beamo/otel/auth/writer/config\r\n * Requires signed request authentication (X-BEAM-SIGNATURE header)\r\n */\r\nexport async function fetchClickHouseCredentials(\r\n env: EnvironmentConfig\r\n): Promise<ClickHouseCredentials> {\r\n const apiUrl = hostToHttpUrl(env.host);\r\n const uriPath = '/api/beamo/otel/auth/writer/config';\r\n const configUrl = new URL(uriPath, apiUrl).toString();\r\n \r\n // Get secret from environment (required for signed requests)\r\n const secret = process.env.SECRET;\r\n if (!secret) {\r\n throw new Error('SECRET environment variable is required to fetch ClickHouse credentials');\r\n }\r\n \r\n // Calculate signature for signed request (matching C# HttpSignedRequester)\r\n const pathAndQuery = getPathAndQuery(uriPath);\r\n const signature = calculateSignature(env.pid, secret, pathAndQuery, null, '1');\r\n \r\n // Build headers with signed authentication (matching C# HttpSignedRequester)\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,\r\n 'X-BEAM-SIGNATURE': signature,\r\n };\r\n \r\n const response = await fetch(configUrl, {\r\n method: 'GET',\r\n headers,\r\n });\r\n \r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'Unknown error');\r\n throw new Error(`Failed to fetch ClickHouse credentials from ${configUrl}: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);\r\n }\r\n \r\n const credentials = await response.json() as ClickHouseCredentials;\r\n \r\n if (!credentials.endpoint || !credentials.username || !credentials.password) {\r\n throw new Error('Invalid ClickHouse credentials response: missing required fields');\r\n }\r\n \r\n return credentials;\r\n}\r\n\r\n/**\r\n * Gets the collector storage directory (similar to C# LocalApplicationData/beam/collectors/version)\r\n */\r\nfunction getCollectorStoragePath(): string {\r\n // Use temp directory for now - in containers this should be writable\r\n const tempDir = process.env.TMPDIR || process.env.TMP || '/tmp';\r\n return join(tempDir, 'beam', 'collectors', COLLECTOR_VERSION);\r\n}\r\n\r\n/**\r\n * Gets the collector binary name for the current platform\r\n */\r\nfunction getCollectorBinaryName(): string {\r\n const platform = process.platform;\r\n const arch = process.arch;\r\n \r\n // Match C# naming: collector-{osArchSuffix}\r\n // C# returns \"collector-linux-amd64\", not \"beamable-collector-linux-amd64\"\r\n if (platform === 'linux' && arch === 'x64') {\r\n return 'collector-linux-amd64';\r\n } else if (platform === 'linux' && arch === 'arm64') {\r\n return 'collector-linux-arm64';\r\n } else if (platform === 'darwin' && arch === 'x64') {\r\n return 'collector-darwin-amd64';\r\n } else if (platform === 'darwin' && arch === 'arm64') {\r\n return 'collector-darwin-arm64';\r\n } else if (platform === 'win32' && arch === 'x64') {\r\n return 'collector-windows-amd64.exe';\r\n }\r\n \r\n throw new Error(`Unsupported platform: ${platform} ${arch}`);\r\n}\r\n\r\n/**\r\n * Downloads and decompresses a gzipped file\r\n */\r\nasync function downloadAndDecompressGzip(url: string, outputPath: string, makeExecutable: boolean = false): Promise<void> {\r\n const response = await fetch(url);\r\n if (!response.ok) {\r\n throw new Error(`Failed to download ${url}: ${response.statusText}`);\r\n }\r\n \r\n const dir = dirname(outputPath);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n \r\n const gunzip = createGunzip();\r\n const writeStream = createWriteStream(outputPath);\r\n \r\n await pipeline(response.body as any, gunzip, writeStream);\r\n \r\n if (makeExecutable && process.platform !== 'win32') {\r\n try {\r\n chmodSync(outputPath, 0o755);\r\n } catch (error) {\r\n console.error(`Failed to make ${outputPath} executable:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Resolves the collector binary and config, downloading if needed\r\n */\r\nasync function resolveCollector(allowDownload: boolean = true, logger?: Logger): Promise<{ binaryPath: string | null; configPath: string | null }> {\r\n const basePath = getCollectorStoragePath();\r\n const binaryName = getCollectorBinaryName();\r\n const configName = 'clickhouse-config.yaml';\r\n \r\n const binaryPath = join(basePath, binaryName);\r\n const configPath = join(basePath, configName);\r\n \r\n const itemsToDownload: Array<{ url: string; path: string; executable: boolean }> = [];\r\n \r\n if (!existsSync(binaryPath) && allowDownload) {\r\n const binaryUrl = `${COLLECTOR_DOWNLOAD_BASE}/${binaryName}.gz`;\r\n itemsToDownload.push({ url: binaryUrl, path: binaryPath, executable: true });\r\n logger?.info(`[Collector] Will download binary from: ${binaryUrl}`);\r\n } else if (existsSync(binaryPath)) {\r\n logger?.info(`[Collector] Binary found at: ${binaryPath}`);\r\n }\r\n \r\n if (!existsSync(configPath) && allowDownload) {\r\n const configUrl = `${COLLECTOR_DOWNLOAD_BASE}/${configName}.gz`;\r\n itemsToDownload.push({ url: configUrl, path: configPath, executable: false });\r\n logger?.info(`[Collector] Will download config from: ${configUrl}`);\r\n } else if (existsSync(configPath)) {\r\n logger?.info(`[Collector] Config found at: ${configPath}`);\r\n }\r\n \r\n for (const item of itemsToDownload) {\r\n logger?.info(`[Collector] Downloading ${item.url}...`);\r\n await downloadAndDecompressGzip(item.url, item.path, item.executable);\r\n logger?.info(`[Collector] Downloaded to ${item.path}`);\r\n }\r\n \r\n return {\r\n binaryPath: existsSync(binaryPath) ? binaryPath : null,\r\n configPath: existsSync(configPath) ? configPath : null,\r\n };\r\n}\r\n\r\n/**\r\n * Discovers collector via UDP broadcast\r\n */\r\nfunction discoverCollectorViaUDP(timeoutMs: number = 5000): Promise<CollectorDiscoveryEntry | null> {\r\n return new Promise((resolve) => {\r\n const socket = dgram.createSocket('udp4');\r\n const discovered: CollectorDiscoveryEntry[] = [];\r\n let timeout: NodeJS.Timeout;\r\n \r\n socket.on('message', (msg) => {\r\n try {\r\n const message = JSON.parse(msg.toString()) as CollectorDiscoveryEntry;\r\n // Check if version matches\r\n if (message.version === COLLECTOR_VERSION && message.status === 'READY') {\r\n discovered.push(message);\r\n }\r\n } catch (error) {\r\n // Ignore parse errors\r\n }\r\n });\r\n \r\n socket.on('error', () => {\r\n clearTimeout(timeout);\r\n socket.close();\r\n resolve(null);\r\n });\r\n \r\n socket.bind(() => {\r\n socket.setBroadcast(true);\r\n \r\n timeout = setTimeout(() => {\r\n socket.close();\r\n // Return the first discovered collector\r\n resolve(discovered.length > 0 ? discovered[0] : null);\r\n }, timeoutMs);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Gets the current status of ClickHouse credentials (from env or API)\r\n */\r\nexport function getClickHouseCredentialsStatus(): {\r\n hasEndpoint: boolean;\r\n hasUsername: boolean;\r\n hasPassword: boolean;\r\n source: 'environment' | 'api' | 'missing';\r\n} {\r\n const hasEndpoint = !!process.env.BEAM_CLICKHOUSE_ENDPOINT;\r\n const hasUsername = !!process.env.BEAM_CLICKHOUSE_USERNAME;\r\n const hasPassword = !!process.env.BEAM_CLICKHOUSE_PASSWORD;\r\n \r\n if (hasEndpoint && hasUsername && hasPassword) {\r\n return {\r\n hasEndpoint: true,\r\n hasUsername: true,\r\n hasPassword: true,\r\n source: 'environment',\r\n };\r\n }\r\n \r\n return {\r\n hasEndpoint,\r\n hasUsername,\r\n hasPassword,\r\n source: 'missing',\r\n };\r\n}\r\n\r\n/**\r\n * Checks if collector is already running via UDP discovery\r\n */\r\nexport async function isCollectorRunning(): Promise<CollectorStatus> {\r\n try {\r\n const discovered = await discoverCollectorViaUDP(2000);\r\n if (discovered) {\r\n return {\r\n isRunning: true,\r\n isReady: discovered.status === 'READY',\r\n pid: discovered.pid,\r\n otlpEndpoint: discovered.otlpEndpoint,\r\n version: discovered.version,\r\n };\r\n }\r\n } catch (error) {\r\n // Discovery failed, collector probably not running\r\n }\r\n \r\n return {\r\n isRunning: false,\r\n isReady: false,\r\n pid: 0,\r\n };\r\n}\r\n\r\n/**\r\n * Sets default collector configuration environment variables in process.env\r\n * Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()\r\n */\r\nfunction addCollectorConfigurationToEnvironment(): void {\r\n const defaults: Record<string, string> = {\r\n BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: '5s',\r\n BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: '5000',\r\n BEAM_CLICKHOUSE_EXPORTER_TIMEOUT: '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE: '1000',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED: 'true',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL: '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL: '30s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME: '300s',\r\n };\r\n \r\n // Only set if not already set (matching C# behavior)\r\n for (const [key, defaultValue] of Object.entries(defaults)) {\r\n if (!process.env[key]) {\r\n process.env[key] = defaultValue;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Sets ClickHouse credentials in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)\r\n */\r\nexport function addAuthEnvironmentVars(endpoint: string, username: string, password: string): void {\r\n // Always set the values when explicitly provided (matching C# behavior)\r\n // This ensures credentials fetched from API override any existing (potentially empty) values\r\n // In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process\r\n process.env.BEAM_CLICKHOUSE_ENDPOINT = endpoint;\r\n process.env.BEAM_CLICKHOUSE_USERNAME = username;\r\n process.env.BEAM_CLICKHOUSE_PASSWORD = password;\r\n \r\n // Verify they were actually set (for debugging)\r\n if (!process.env.BEAM_CLICKHOUSE_ENDPOINT || !process.env.BEAM_CLICKHOUSE_USERNAME || !process.env.BEAM_CLICKHOUSE_PASSWORD) {\r\n throw new Error(`Failed to set ClickHouse credentials in process.env - this should never happen in Node.js`);\r\n }\r\n}\r\n\r\n/**\r\n * Starts the OpenTelemetry collector process\r\n */\r\nexport async function startCollector(\r\n logger: Logger,\r\n otlpEndpoint?: string,\r\n env?: EnvironmentConfig\r\n): Promise<{ process: ChildProcess; endpoint: string }> {\r\n // CRITICAL: Fetch credentials FIRST, before resolving collector binary/config\r\n // This ensures credentials are in process.env even if collector download fails\r\n // The credentials will persist in process.env for the lifetime of the process.\r\n // Per Gabriel: \"these OTEL related ones you need to do it yourself at the beginning of the microservice startup\"\r\n let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;\r\n let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;\r\n let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;\r\n \r\n if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {\r\n try {\r\n logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');\r\n const credentials = await fetchClickHouseCredentials(env);\r\n clickhouseEndpoint = credentials.endpoint;\r\n clickhouseUsername = credentials.username;\r\n clickhousePassword = credentials.password;\r\n \r\n // CRITICAL: Set them in process.env IMMEDIATELY after fetching (matching C# CollectorManager.AddAuthEnvironmentVars)\r\n // In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process\r\n // This ensures they're available to both the Node.js process and inherited by the collector child process\r\n addAuthEnvironmentVars(clickhouseEndpoint, clickhouseUsername, clickhousePassword);\r\n \r\n // Verify they were set (defensive check - should never fail in Node.js)\r\n const verifyEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;\r\n const verifyUsername = process.env.BEAM_CLICKHOUSE_USERNAME;\r\n const verifyPassword = process.env.BEAM_CLICKHOUSE_PASSWORD;\r\n \r\n if (!verifyEndpoint || !verifyUsername || !verifyPassword) {\r\n logger.error(`[Collector] CRITICAL: Credentials were set but are missing from process.env! This should never happen.`);\r\n throw new Error('Failed to persist ClickHouse credentials in process.env');\r\n }\r\n \r\n logger.info('[Collector] ClickHouse credentials fetched from API and verified in process.env');\r\n } catch (error) {\r\n const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;\r\n logger.error(errorMsg);\r\n throw new Error(errorMsg);\r\n }\r\n }\r\n \r\n // Validate required environment variables (matching C# behavior)\r\n // These must be set before starting the collector, otherwise it will fail\r\n if (!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) {\r\n const errorMsg = `[Collector] Required ClickHouse credentials are missing. Set BEAM_CLICKHOUSE_ENDPOINT, BEAM_CLICKHOUSE_USERNAME, and BEAM_CLICKHOUSE_PASSWORD, or ensure the API endpoint is accessible.`;\r\n logger.error(errorMsg);\r\n throw new Error(errorMsg);\r\n }\r\n \r\n // Now resolve collector binary and config (after credentials are fetched and set)\r\n logger.info('[Collector] Resolving collector binary and config...');\r\n const collectorInfo = await resolveCollector(true, logger);\r\n \r\n if (!collectorInfo.binaryPath) {\r\n logger.error('[Collector] Binary not found and download failed');\r\n throw new Error('Collector binary not found and download failed');\r\n }\r\n \r\n if (!collectorInfo.configPath) {\r\n logger.error('[Collector] Config not found and download failed');\r\n throw new Error('Collector config not found and download failed');\r\n }\r\n \r\n logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);\r\n logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);\r\n \r\n // Set default collector configuration environment variables in process.env\r\n // This ensures they're available to both the Node.js process and the collector child process\r\n // Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()\r\n addCollectorConfigurationToEnvironment();\r\n \r\n // Determine OTLP endpoint\r\n // Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())\r\n let localEndpoint = otlpEndpoint;\r\n if (!localEndpoint) {\r\n // For now, use default OTLP HTTP port\r\n // In production, this would be discovered or set by Beamable\r\n localEndpoint = '0.0.0.0:4318';\r\n }\r\n localEndpoint = localEndpoint.replace(/^http:\\/\\//, '').replace(/^https:\\/\\//, '');\r\n \r\n // Set environment variables for collector\r\n // Note: BEAM_CLICKHOUSE_ENDPOINT is for collector → ClickHouse communication\r\n // This is different from the OTLP endpoint (microservice → collector)\r\n // The collector process will inherit all environment variables from process.env automatically\r\n // We only need to set the OTLP endpoint and discovery port explicitly here\r\n const collectorEnv: NodeJS.ProcessEnv = {\r\n ...process.env, // Inherit all process.env (including credentials and config we just set)\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),\r\n };\r\n \r\n // Start collector process\r\n const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {\r\n env: collectorEnv,\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n detached: false,\r\n });\r\n \r\n // Store globally to prevent GC and track status\r\n globalCollectorProcess = collectorProcess;\r\n globalCollectorStartError = null;\r\n globalCollectorExitCode = null;\r\n globalCollectorStderr = [];\r\n \r\n collectorProcess.stdout?.on('data', (data) => {\r\n logger.debug(`[Collector] ${data.toString().trim()}`);\r\n });\r\n \r\n collectorProcess.stderr?.on('data', (data) => {\r\n const errorLine = data.toString().trim();\r\n globalCollectorStderr.push(errorLine);\r\n // Keep only last 50 lines to prevent memory issues\r\n if (globalCollectorStderr.length > 50) {\r\n globalCollectorStderr.shift();\r\n }\r\n logger.debug(`[Collector ERR] ${errorLine}`);\r\n });\r\n \r\n collectorProcess.on('error', (err) => {\r\n globalCollectorStartError = err.message;\r\n logger.error(`[Collector] Failed to start: ${err.message}`);\r\n });\r\n \r\n collectorProcess.on('exit', (code) => {\r\n globalCollectorExitCode = code;\r\n globalCollectorProcess = null; // Clear reference when process exits\r\n logger.warn(`[Collector] Process exited with code ${code}`);\r\n });\r\n \r\n logger.info(`[Collector] Started with PID ${collectorProcess.pid}, endpoint: ${localEndpoint}`);\r\n \r\n return {\r\n process: collectorProcess,\r\n endpoint: `http://${localEndpoint}`,\r\n };\r\n}\r\n\r\n/**\r\n * Gets detailed collector status including process info and errors\r\n */\r\nexport function getCollectorProcessStatus(): {\r\n hasProcess: boolean;\r\n pid: number | null;\r\n exitCode: number | null;\r\n startError: string | null;\r\n stderr: string[];\r\n} {\r\n return {\r\n hasProcess: globalCollectorProcess !== null,\r\n pid: globalCollectorProcess?.pid ?? null,\r\n exitCode: globalCollectorExitCode,\r\n startError: globalCollectorStartError,\r\n stderr: [...globalCollectorStderr], // Return a copy\r\n };\r\n}\r\n\r\n/**\r\n * Discovers or starts the collector and returns the OTLP endpoint\r\n */\r\nexport async function discoverOrStartCollector(\r\n logger: Logger,\r\n standardOtelEnabled: boolean,\r\n env?: EnvironmentConfig\r\n): Promise<string | null> {\r\n if (!standardOtelEnabled) {\r\n return null;\r\n }\r\n \r\n // First, check if collector is already running\r\n const status = await isCollectorRunning();\r\n if (status.isRunning && status.isReady && status.otlpEndpoint) {\r\n logger.info(`[Collector] Found running collector at ${status.otlpEndpoint}`);\r\n return `http://${status.otlpEndpoint}`;\r\n }\r\n \r\n // Collector not running - start it\r\n try {\r\n logger.info('[Collector] Starting OpenTelemetry collector...');\r\n const { endpoint } = await startCollector(logger, undefined, env);\r\n \r\n // Wait a bit for collector to start and become ready\r\n // Try to discover it\r\n for (let i = 0; i < DISCOVERY_ATTEMPTS; i++) {\r\n await new Promise(resolve => setTimeout(resolve, DISCOVERY_DELAY));\r\n const newStatus = await isCollectorRunning();\r\n if (newStatus.isRunning && newStatus.isReady) {\r\n logger.info(`[Collector] Collector is ready at ${newStatus.otlpEndpoint || endpoint}`);\r\n return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;\r\n }\r\n }\r\n \r\n // Return the endpoint we started with, even if discovery didn't find it yet\r\n logger.warn('[Collector] Collector started but not yet ready, using configured endpoint');\r\n return endpoint;\r\n } catch (err) {\r\n logger.error(`[Collector] Failed to start collector: ${err instanceof Error ? err.message : String(err)}`);\r\n return null;\r\n }\r\n}\r\n\r\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fetchClickHouseCredentials = exports.getClickHouseCredentialsStatus = exports.isCollectorRunning = exports.FederatedInventory = exports.getFederationComponents = exports.FederationRegistry = exports.FederationComponent = exports.listRegisteredStorageObjects = exports.getStorageMetadata = exports.StorageObject = exports.StorageService = exports.createInventoryService = exports.InventoryUpdateBuilder = exports.runMicroservice = exports.MicroserviceRuntime = exports.BeamableRuntimeError = exports.loadEnvironmentConfig = exports.BEAMABLE_SERVICES_TOKEN = exports.REQUEST_CONTEXT_TOKEN = exports.ENVIRONMENT_CONFIG_TOKEN = exports.LOGGER_TOKEN = exports.ServiceLifetime = exports.DependencyBuilder = exports.BeamableServiceManager = exports.InitializeServices = exports.ConfigureServices = exports.SwaggerTags = exports.SwaggerCategory = exports.AdminCallable = exports.ServerCallable = exports.ClientCallable = exports.Callable = exports.Microservice = void 0;
|
|
3
|
+
exports.getCollectorProcessStatus = exports.addAuthEnvironmentVars = exports.fetchClickHouseCredentials = exports.getClickHouseCredentialsStatus = exports.isCollectorRunning = exports.FederatedInventory = exports.getFederationComponents = exports.FederationRegistry = exports.FederationComponent = exports.listRegisteredStorageObjects = exports.getStorageMetadata = exports.StorageObject = exports.StorageService = exports.createInventoryService = exports.InventoryUpdateBuilder = exports.runMicroservice = exports.MicroserviceRuntime = exports.BeamableRuntimeError = exports.loadEnvironmentConfig = exports.BEAMABLE_SERVICES_TOKEN = exports.REQUEST_CONTEXT_TOKEN = exports.ENVIRONMENT_CONFIG_TOKEN = exports.LOGGER_TOKEN = exports.ServiceLifetime = exports.DependencyBuilder = exports.BeamableServiceManager = exports.InitializeServices = exports.ConfigureServices = exports.SwaggerTags = exports.SwaggerCategory = exports.AdminCallable = exports.ServerCallable = exports.ClientCallable = exports.Callable = exports.Microservice = void 0;
|
|
4
4
|
var decorators_js_1 = require("./decorators.js");
|
|
5
5
|
Object.defineProperty(exports, "Microservice", { enumerable: true, get: function () { return decorators_js_1.Microservice; } });
|
|
6
6
|
Object.defineProperty(exports, "Callable", { enumerable: true, get: function () { return decorators_js_1.Callable; } });
|
|
@@ -44,3 +44,5 @@ var collector_manager_js_1 = require("./collector-manager.js");
|
|
|
44
44
|
Object.defineProperty(exports, "isCollectorRunning", { enumerable: true, get: function () { return collector_manager_js_1.isCollectorRunning; } });
|
|
45
45
|
Object.defineProperty(exports, "getClickHouseCredentialsStatus", { enumerable: true, get: function () { return collector_manager_js_1.getClickHouseCredentialsStatus; } });
|
|
46
46
|
Object.defineProperty(exports, "fetchClickHouseCredentials", { enumerable: true, get: function () { return collector_manager_js_1.fetchClickHouseCredentials; } });
|
|
47
|
+
Object.defineProperty(exports, "addAuthEnvironmentVars", { enumerable: true, get: function () { return collector_manager_js_1.addAuthEnvironmentVars; } });
|
|
48
|
+
Object.defineProperty(exports, "getCollectorProcessStatus", { enumerable: true, get: function () { return collector_manager_js_1.getCollectorProcessStatus; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export { MicroserviceRuntime, runMicroservice } from './runtime.js';
|
|
|
10
10
|
export { InventoryUpdateBuilder, createInventoryService, type InventoryUpdatePayload, type InventoryBuilderOptions, } from './inventory.js';
|
|
11
11
|
export { StorageService, StorageObject, getStorageMetadata, listRegisteredStorageObjects } from './storage.js';
|
|
12
12
|
export { FederationComponent, FederationRegistry, getFederationComponents, FederatedInventory, type FederationIdentity, type FederatedAuthenticationRequest, type FederatedAuthenticationResponse, type FederatedInventoryProxyState, type FederatedInventoryStateRequest, type FederatedInventoryTransactionRequest, type FederatedItemCreateRequest, type FederatedItemDeleteRequest, type FederatedItemUpdateRequest, type FederatedItemProperty, type FederatedItemProxy, } from './federation.js';
|
|
13
|
-
export { isCollectorRunning, getClickHouseCredentialsStatus, fetchClickHouseCredentials, type CollectorStatus, } from './collector-manager.js';
|
|
13
|
+
export { isCollectorRunning, getClickHouseCredentialsStatus, fetchClickHouseCredentials, addAuthEnvironmentVars, getCollectorProcessStatus, type CollectorStatus, } from './collector-manager.js';
|
|
14
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACvF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,oCAAoC,EACzC,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,0BAA0B,EAC1B,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACvF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,oCAAoC,EACzC,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,5 +7,5 @@ export { MicroserviceRuntime, runMicroservice } from './runtime.js';
|
|
|
7
7
|
export { InventoryUpdateBuilder, createInventoryService, } from './inventory.js';
|
|
8
8
|
export { StorageService, StorageObject, getStorageMetadata, listRegisteredStorageObjects } from './storage.js';
|
|
9
9
|
export { FederationComponent, FederationRegistry, getFederationComponents, FederatedInventory, } from './federation.js';
|
|
10
|
-
export { isCollectorRunning, getClickHouseCredentialsStatus, fetchClickHouseCredentials, } from './collector-manager.js';
|
|
10
|
+
export { isCollectorRunning, getClickHouseCredentialsStatus, fetchClickHouseCredentials, addAuthEnvironmentVars, getCollectorProcessStatus, } from './collector-manager.js';
|
|
11
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GAGvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GAYnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACd,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,YAAY,EACZ,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GAGvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GAYnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,GAE1B,MAAM,wBAAwB,CAAC","sourcesContent":["export {\r\n Microservice,\r\n Callable,\r\n ClientCallable,\r\n ServerCallable,\r\n AdminCallable,\r\n SwaggerCategory,\r\n SwaggerTags,\r\n ConfigureServices,\r\n InitializeServices,\r\n} from './decorators.js';\r\nexport type { MicroserviceOptions, CallableOptions, ServiceAccess } from './types.js';\r\nexport type { EnvironmentConfig, RequestContext, ServiceDefinition } from './types.js';\r\nexport type { BeamableMicroserviceServices, BoundBeamApi } from './services.js';\r\nexport { BeamableServiceManager } from './services.js';\r\nexport {\r\n DependencyBuilder,\r\n type DependencyScope,\r\n ServiceLifetime,\r\n LOGGER_TOKEN,\r\n ENVIRONMENT_CONFIG_TOKEN,\r\n REQUEST_CONTEXT_TOKEN,\r\n BEAMABLE_SERVICES_TOKEN,\r\n} from './dependency.js';\r\nexport { loadEnvironmentConfig } from './env.js';\r\nexport { BeamableRuntimeError } from './errors.js';\r\nexport { MicroserviceRuntime, runMicroservice } from './runtime.js';\r\nexport {\r\n InventoryUpdateBuilder,\r\n createInventoryService,\r\n type InventoryUpdatePayload,\r\n type InventoryBuilderOptions,\r\n} from './inventory.js';\r\nexport { StorageService, StorageObject, getStorageMetadata, listRegisteredStorageObjects } from './storage.js';\r\nexport {\r\n FederationComponent,\r\n FederationRegistry,\r\n getFederationComponents,\r\n FederatedInventory,\r\n type FederationIdentity,\r\n type FederatedAuthenticationRequest,\r\n type FederatedAuthenticationResponse,\r\n type FederatedInventoryProxyState,\r\n type FederatedInventoryStateRequest,\r\n type FederatedInventoryTransactionRequest,\r\n type FederatedItemCreateRequest,\r\n type FederatedItemDeleteRequest,\r\n type FederatedItemUpdateRequest,\r\n type FederatedItemProperty,\r\n type FederatedItemProxy,\r\n} from './federation.js';\r\nexport {\r\n isCollectorRunning,\r\n getClickHouseCredentialsStatus,\r\n fetchClickHouseCredentials,\r\n addAuthEnvironmentVars,\r\n getCollectorProcessStatus,\r\n type CollectorStatus,\r\n} from './collector-manager.js';\r\n"]}
|
package/package.json
CHANGED
package/src/collector-manager.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface CollectorStatus {
|
|
|
29
29
|
const COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)
|
|
30
30
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
31
31
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
32
|
+
|
|
33
|
+
// Global storage for collector process and errors (to prevent GC and track status)
|
|
34
|
+
let globalCollectorProcess: ChildProcess | null = null;
|
|
35
|
+
let globalCollectorStartError: string | null = null;
|
|
36
|
+
let globalCollectorExitCode: number | null = null;
|
|
37
|
+
let globalCollectorStderr: string[] = [];
|
|
32
38
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
33
39
|
const DISCOVERY_ATTEMPTS = 10;
|
|
34
40
|
|
|
@@ -350,15 +356,17 @@ function addCollectorConfigurationToEnvironment(): void {
|
|
|
350
356
|
/**
|
|
351
357
|
* Sets ClickHouse credentials in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
352
358
|
*/
|
|
353
|
-
function addAuthEnvironmentVars(endpoint: string, username: string, password: string): void {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
export function addAuthEnvironmentVars(endpoint: string, username: string, password: string): void {
|
|
360
|
+
// Always set the values when explicitly provided (matching C# behavior)
|
|
361
|
+
// This ensures credentials fetched from API override any existing (potentially empty) values
|
|
362
|
+
// In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process
|
|
363
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = endpoint;
|
|
364
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = username;
|
|
365
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = password;
|
|
366
|
+
|
|
367
|
+
// Verify they were actually set (for debugging)
|
|
368
|
+
if (!process.env.BEAM_CLICKHOUSE_ENDPOINT || !process.env.BEAM_CLICKHOUSE_USERNAME || !process.env.BEAM_CLICKHOUSE_PASSWORD) {
|
|
369
|
+
throw new Error(`Failed to set ClickHouse credentials in process.env - this should never happen in Node.js`);
|
|
362
370
|
}
|
|
363
371
|
}
|
|
364
372
|
|
|
@@ -370,28 +378,9 @@ export async function startCollector(
|
|
|
370
378
|
otlpEndpoint?: string,
|
|
371
379
|
env?: EnvironmentConfig
|
|
372
380
|
): Promise<{ process: ChildProcess; endpoint: string }> {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (!collectorInfo.binaryPath) {
|
|
377
|
-
logger.error('[Collector] Binary not found and download failed');
|
|
378
|
-
throw new Error('Collector binary not found and download failed');
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (!collectorInfo.configPath) {
|
|
382
|
-
logger.error('[Collector] Config not found and download failed');
|
|
383
|
-
throw new Error('Collector config not found and download failed');
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
387
|
-
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
388
|
-
|
|
389
|
-
// Set default collector configuration environment variables in process.env
|
|
390
|
-
// This ensures they're available to both the Node.js process and the collector child process
|
|
391
|
-
// Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()
|
|
392
|
-
addCollectorConfigurationToEnvironment();
|
|
393
|
-
|
|
394
|
-
// Fetch ClickHouse credentials from Beamable API if not in environment variables
|
|
381
|
+
// CRITICAL: Fetch credentials FIRST, before resolving collector binary/config
|
|
382
|
+
// This ensures credentials are in process.env even if collector download fails
|
|
383
|
+
// The credentials will persist in process.env for the lifetime of the process.
|
|
395
384
|
// Per Gabriel: "these OTEL related ones you need to do it yourself at the beginning of the microservice startup"
|
|
396
385
|
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
397
386
|
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
@@ -399,14 +388,28 @@ export async function startCollector(
|
|
|
399
388
|
|
|
400
389
|
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
401
390
|
try {
|
|
391
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
402
392
|
const credentials = await fetchClickHouseCredentials(env);
|
|
403
393
|
clickhouseEndpoint = credentials.endpoint;
|
|
404
394
|
clickhouseUsername = credentials.username;
|
|
405
395
|
clickhousePassword = credentials.password;
|
|
406
396
|
|
|
407
|
-
// Set them in process.env (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
397
|
+
// CRITICAL: Set them in process.env IMMEDIATELY after fetching (matching C# CollectorManager.AddAuthEnvironmentVars)
|
|
398
|
+
// In Node.js, process.env is a writable object - setting properties here persists for the lifetime of the process
|
|
408
399
|
// This ensures they're available to both the Node.js process and inherited by the collector child process
|
|
409
400
|
addAuthEnvironmentVars(clickhouseEndpoint, clickhouseUsername, clickhousePassword);
|
|
401
|
+
|
|
402
|
+
// Verify they were set (defensive check - should never fail in Node.js)
|
|
403
|
+
const verifyEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
404
|
+
const verifyUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
405
|
+
const verifyPassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
406
|
+
|
|
407
|
+
if (!verifyEndpoint || !verifyUsername || !verifyPassword) {
|
|
408
|
+
logger.error(`[Collector] CRITICAL: Credentials were set but are missing from process.env! This should never happen.`);
|
|
409
|
+
throw new Error('Failed to persist ClickHouse credentials in process.env');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
logger.info('[Collector] ClickHouse credentials fetched from API and verified in process.env');
|
|
410
413
|
} catch (error) {
|
|
411
414
|
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
412
415
|
logger.error(errorMsg);
|
|
@@ -422,6 +425,28 @@ export async function startCollector(
|
|
|
422
425
|
throw new Error(errorMsg);
|
|
423
426
|
}
|
|
424
427
|
|
|
428
|
+
// Now resolve collector binary and config (after credentials are fetched and set)
|
|
429
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
430
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
431
|
+
|
|
432
|
+
if (!collectorInfo.binaryPath) {
|
|
433
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
434
|
+
throw new Error('Collector binary not found and download failed');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!collectorInfo.configPath) {
|
|
438
|
+
logger.error('[Collector] Config not found and download failed');
|
|
439
|
+
throw new Error('Collector config not found and download failed');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
443
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
444
|
+
|
|
445
|
+
// Set default collector configuration environment variables in process.env
|
|
446
|
+
// This ensures they're available to both the Node.js process and the collector child process
|
|
447
|
+
// Matches C# CollectorManager.AddCollectorConfigurationToEnvironment()
|
|
448
|
+
addCollectorConfigurationToEnvironment();
|
|
449
|
+
|
|
425
450
|
// Determine OTLP endpoint
|
|
426
451
|
// Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())
|
|
427
452
|
let localEndpoint = otlpEndpoint;
|
|
@@ -450,19 +475,34 @@ export async function startCollector(
|
|
|
450
475
|
detached: false,
|
|
451
476
|
});
|
|
452
477
|
|
|
478
|
+
// Store globally to prevent GC and track status
|
|
479
|
+
globalCollectorProcess = collectorProcess;
|
|
480
|
+
globalCollectorStartError = null;
|
|
481
|
+
globalCollectorExitCode = null;
|
|
482
|
+
globalCollectorStderr = [];
|
|
483
|
+
|
|
453
484
|
collectorProcess.stdout?.on('data', (data) => {
|
|
454
485
|
logger.debug(`[Collector] ${data.toString().trim()}`);
|
|
455
486
|
});
|
|
456
487
|
|
|
457
488
|
collectorProcess.stderr?.on('data', (data) => {
|
|
458
|
-
|
|
489
|
+
const errorLine = data.toString().trim();
|
|
490
|
+
globalCollectorStderr.push(errorLine);
|
|
491
|
+
// Keep only last 50 lines to prevent memory issues
|
|
492
|
+
if (globalCollectorStderr.length > 50) {
|
|
493
|
+
globalCollectorStderr.shift();
|
|
494
|
+
}
|
|
495
|
+
logger.debug(`[Collector ERR] ${errorLine}`);
|
|
459
496
|
});
|
|
460
497
|
|
|
461
498
|
collectorProcess.on('error', (err) => {
|
|
499
|
+
globalCollectorStartError = err.message;
|
|
462
500
|
logger.error(`[Collector] Failed to start: ${err.message}`);
|
|
463
501
|
});
|
|
464
502
|
|
|
465
503
|
collectorProcess.on('exit', (code) => {
|
|
504
|
+
globalCollectorExitCode = code;
|
|
505
|
+
globalCollectorProcess = null; // Clear reference when process exits
|
|
466
506
|
logger.warn(`[Collector] Process exited with code ${code}`);
|
|
467
507
|
});
|
|
468
508
|
|
|
@@ -474,6 +514,25 @@ export async function startCollector(
|
|
|
474
514
|
};
|
|
475
515
|
}
|
|
476
516
|
|
|
517
|
+
/**
|
|
518
|
+
* Gets detailed collector status including process info and errors
|
|
519
|
+
*/
|
|
520
|
+
export function getCollectorProcessStatus(): {
|
|
521
|
+
hasProcess: boolean;
|
|
522
|
+
pid: number | null;
|
|
523
|
+
exitCode: number | null;
|
|
524
|
+
startError: string | null;
|
|
525
|
+
stderr: string[];
|
|
526
|
+
} {
|
|
527
|
+
return {
|
|
528
|
+
hasProcess: globalCollectorProcess !== null,
|
|
529
|
+
pid: globalCollectorProcess?.pid ?? null,
|
|
530
|
+
exitCode: globalCollectorExitCode,
|
|
531
|
+
startError: globalCollectorStartError,
|
|
532
|
+
stderr: [...globalCollectorStderr], // Return a copy
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
477
536
|
/**
|
|
478
537
|
* Discovers or starts the collector and returns the OTLP endpoint
|
|
479
538
|
*/
|