@omen.foundation/node-microservice-runtime 0.1.19 → 0.1.21
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.
|
@@ -12,7 +12,7 @@ const path_1 = require("path");
|
|
|
12
12
|
const promises_1 = require("stream/promises");
|
|
13
13
|
const zlib_1 = require("zlib");
|
|
14
14
|
const dgram_1 = __importDefault(require("dgram"));
|
|
15
|
-
const COLLECTOR_VERSION = '
|
|
15
|
+
const COLLECTOR_VERSION = '1.0.1';
|
|
16
16
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
17
17
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
18
18
|
const DISCOVERY_DELAY = 100;
|
|
@@ -25,19 +25,19 @@ function getCollectorBinaryName() {
|
|
|
25
25
|
const platform = process.platform;
|
|
26
26
|
const arch = process.arch;
|
|
27
27
|
if (platform === 'linux' && arch === 'x64') {
|
|
28
|
-
return '
|
|
28
|
+
return 'collector-linux-amd64';
|
|
29
29
|
}
|
|
30
30
|
else if (platform === 'linux' && arch === 'arm64') {
|
|
31
|
-
return '
|
|
31
|
+
return 'collector-linux-arm64';
|
|
32
32
|
}
|
|
33
33
|
else if (platform === 'darwin' && arch === 'x64') {
|
|
34
|
-
return '
|
|
34
|
+
return 'collector-darwin-amd64';
|
|
35
35
|
}
|
|
36
36
|
else if (platform === 'darwin' && arch === 'arm64') {
|
|
37
|
-
return '
|
|
37
|
+
return 'collector-darwin-arm64';
|
|
38
38
|
}
|
|
39
39
|
else if (platform === 'win32' && arch === 'x64') {
|
|
40
|
-
return '
|
|
40
|
+
return 'collector-windows-amd64.exe';
|
|
41
41
|
}
|
|
42
42
|
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
43
43
|
}
|
|
@@ -46,7 +46,7 @@ async function downloadAndDecompressGzip(url, outputPath, makeExecutable = false
|
|
|
46
46
|
if (!response.ok) {
|
|
47
47
|
throw new Error(`Failed to download ${url}: ${response.statusText}`);
|
|
48
48
|
}
|
|
49
|
-
const dir =
|
|
49
|
+
const dir = (0, path_1.dirname)(outputPath);
|
|
50
50
|
if (!(0, fs_1.existsSync)(dir)) {
|
|
51
51
|
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
52
52
|
}
|
|
@@ -62,7 +62,7 @@ async function downloadAndDecompressGzip(url, outputPath, makeExecutable = false
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
async function resolveCollector(allowDownload = true) {
|
|
65
|
+
async function resolveCollector(allowDownload = true, logger) {
|
|
66
66
|
const basePath = getCollectorStoragePath();
|
|
67
67
|
const binaryName = getCollectorBinaryName();
|
|
68
68
|
const configName = 'clickhouse-config.yaml';
|
|
@@ -72,13 +72,23 @@ async function resolveCollector(allowDownload = true) {
|
|
|
72
72
|
if (!(0, fs_1.existsSync)(binaryPath) && allowDownload) {
|
|
73
73
|
const binaryUrl = `${COLLECTOR_DOWNLOAD_BASE}/${binaryName}.gz`;
|
|
74
74
|
itemsToDownload.push({ url: binaryUrl, path: binaryPath, executable: true });
|
|
75
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Will download binary from: ${binaryUrl}`);
|
|
76
|
+
}
|
|
77
|
+
else if ((0, fs_1.existsSync)(binaryPath)) {
|
|
78
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Binary found at: ${binaryPath}`);
|
|
75
79
|
}
|
|
76
80
|
if (!(0, fs_1.existsSync)(configPath) && allowDownload) {
|
|
77
81
|
const configUrl = `${COLLECTOR_DOWNLOAD_BASE}/${configName}.gz`;
|
|
78
82
|
itemsToDownload.push({ url: configUrl, path: configPath, executable: false });
|
|
83
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Will download config from: ${configUrl}`);
|
|
84
|
+
}
|
|
85
|
+
else if ((0, fs_1.existsSync)(configPath)) {
|
|
86
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Config found at: ${configPath}`);
|
|
79
87
|
}
|
|
80
88
|
for (const item of itemsToDownload) {
|
|
89
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Downloading ${item.url}...`);
|
|
81
90
|
await downloadAndDecompressGzip(item.url, item.path, item.executable);
|
|
91
|
+
logger === null || logger === void 0 ? void 0 : logger.info(`[Collector] Downloaded to ${item.path}`);
|
|
82
92
|
}
|
|
83
93
|
return {
|
|
84
94
|
binaryPath: (0, fs_1.existsSync)(binaryPath) ? binaryPath : null,
|
|
@@ -137,14 +147,22 @@ async function isCollectorRunning() {
|
|
|
137
147
|
}
|
|
138
148
|
async function startCollector(logger, otlpEndpoint) {
|
|
139
149
|
var _a, _b;
|
|
140
|
-
|
|
150
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
151
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
141
152
|
if (!collectorInfo.binaryPath) {
|
|
153
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
142
154
|
throw new Error('Collector binary not found and download failed');
|
|
143
155
|
}
|
|
144
156
|
if (!collectorInfo.configPath) {
|
|
157
|
+
logger.error('[Collector] Config not found and download failed');
|
|
145
158
|
throw new Error('Collector config not found and download failed');
|
|
146
159
|
}
|
|
147
|
-
|
|
160
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
161
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
162
|
+
let localEndpoint = otlpEndpoint;
|
|
163
|
+
if (!localEndpoint) {
|
|
164
|
+
localEndpoint = '0.0.0.0:4318';
|
|
165
|
+
}
|
|
148
166
|
localEndpoint = localEndpoint.replace(/^http:\/\//, '').replace(/^https:\/\//, '');
|
|
149
167
|
const env = {
|
|
150
168
|
...process.env,
|
|
@@ -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;AAKpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAUnC,UAAU,eAAe;IACvB,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;AAKpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAUnC,UAAU,eAAe;IACvB,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;AAqJD;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAqBnE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0EtD;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,OAAO,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmCxB"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import { createWriteStream, existsSync, chmodSync, mkdirSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
4
|
import { pipeline } from 'stream/promises';
|
|
5
5
|
import { createGunzip } from 'zlib';
|
|
6
6
|
import dgram from 'dgram';
|
|
7
|
-
const COLLECTOR_VERSION = '
|
|
7
|
+
const COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)
|
|
8
8
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
9
9
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
10
10
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
@@ -23,20 +23,22 @@ function getCollectorStoragePath() {
|
|
|
23
23
|
function getCollectorBinaryName() {
|
|
24
24
|
const platform = process.platform;
|
|
25
25
|
const arch = process.arch;
|
|
26
|
+
// Match C# naming: collector-{osArchSuffix}
|
|
27
|
+
// C# returns "collector-linux-amd64", not "beamable-collector-linux-amd64"
|
|
26
28
|
if (platform === 'linux' && arch === 'x64') {
|
|
27
|
-
return '
|
|
29
|
+
return 'collector-linux-amd64';
|
|
28
30
|
}
|
|
29
31
|
else if (platform === 'linux' && arch === 'arm64') {
|
|
30
|
-
return '
|
|
32
|
+
return 'collector-linux-arm64';
|
|
31
33
|
}
|
|
32
34
|
else if (platform === 'darwin' && arch === 'x64') {
|
|
33
|
-
return '
|
|
35
|
+
return 'collector-darwin-amd64';
|
|
34
36
|
}
|
|
35
37
|
else if (platform === 'darwin' && arch === 'arm64') {
|
|
36
|
-
return '
|
|
38
|
+
return 'collector-darwin-arm64';
|
|
37
39
|
}
|
|
38
40
|
else if (platform === 'win32' && arch === 'x64') {
|
|
39
|
-
return '
|
|
41
|
+
return 'collector-windows-amd64.exe';
|
|
40
42
|
}
|
|
41
43
|
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
42
44
|
}
|
|
@@ -48,7 +50,7 @@ async function downloadAndDecompressGzip(url, outputPath, makeExecutable = false
|
|
|
48
50
|
if (!response.ok) {
|
|
49
51
|
throw new Error(`Failed to download ${url}: ${response.statusText}`);
|
|
50
52
|
}
|
|
51
|
-
const dir =
|
|
53
|
+
const dir = dirname(outputPath);
|
|
52
54
|
if (!existsSync(dir)) {
|
|
53
55
|
mkdirSync(dir, { recursive: true });
|
|
54
56
|
}
|
|
@@ -67,7 +69,7 @@ async function downloadAndDecompressGzip(url, outputPath, makeExecutable = false
|
|
|
67
69
|
/**
|
|
68
70
|
* Resolves the collector binary and config, downloading if needed
|
|
69
71
|
*/
|
|
70
|
-
async function resolveCollector(allowDownload = true) {
|
|
72
|
+
async function resolveCollector(allowDownload = true, logger) {
|
|
71
73
|
const basePath = getCollectorStoragePath();
|
|
72
74
|
const binaryName = getCollectorBinaryName();
|
|
73
75
|
const configName = 'clickhouse-config.yaml';
|
|
@@ -77,13 +79,23 @@ async function resolveCollector(allowDownload = true) {
|
|
|
77
79
|
if (!existsSync(binaryPath) && allowDownload) {
|
|
78
80
|
const binaryUrl = `${COLLECTOR_DOWNLOAD_BASE}/${binaryName}.gz`;
|
|
79
81
|
itemsToDownload.push({ url: binaryUrl, path: binaryPath, executable: true });
|
|
82
|
+
logger?.info(`[Collector] Will download binary from: ${binaryUrl}`);
|
|
83
|
+
}
|
|
84
|
+
else if (existsSync(binaryPath)) {
|
|
85
|
+
logger?.info(`[Collector] Binary found at: ${binaryPath}`);
|
|
80
86
|
}
|
|
81
87
|
if (!existsSync(configPath) && allowDownload) {
|
|
82
88
|
const configUrl = `${COLLECTOR_DOWNLOAD_BASE}/${configName}.gz`;
|
|
83
89
|
itemsToDownload.push({ url: configUrl, path: configPath, executable: false });
|
|
90
|
+
logger?.info(`[Collector] Will download config from: ${configUrl}`);
|
|
91
|
+
}
|
|
92
|
+
else if (existsSync(configPath)) {
|
|
93
|
+
logger?.info(`[Collector] Config found at: ${configPath}`);
|
|
84
94
|
}
|
|
85
95
|
for (const item of itemsToDownload) {
|
|
96
|
+
logger?.info(`[Collector] Downloading ${item.url}...`);
|
|
86
97
|
await downloadAndDecompressGzip(item.url, item.path, item.executable);
|
|
98
|
+
logger?.info(`[Collector] Downloaded to ${item.path}`);
|
|
87
99
|
}
|
|
88
100
|
return {
|
|
89
101
|
binaryPath: existsSync(binaryPath) ? binaryPath : null,
|
|
@@ -154,15 +166,26 @@ export async function isCollectorRunning() {
|
|
|
154
166
|
* Starts the OpenTelemetry collector process
|
|
155
167
|
*/
|
|
156
168
|
export async function startCollector(logger, otlpEndpoint) {
|
|
157
|
-
|
|
169
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
170
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
158
171
|
if (!collectorInfo.binaryPath) {
|
|
172
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
159
173
|
throw new Error('Collector binary not found and download failed');
|
|
160
174
|
}
|
|
161
175
|
if (!collectorInfo.configPath) {
|
|
176
|
+
logger.error('[Collector] Config not found and download failed');
|
|
162
177
|
throw new Error('Collector config not found and download failed');
|
|
163
178
|
}
|
|
179
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
180
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
164
181
|
// Determine OTLP endpoint
|
|
165
|
-
|
|
182
|
+
// Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())
|
|
183
|
+
let localEndpoint = otlpEndpoint;
|
|
184
|
+
if (!localEndpoint) {
|
|
185
|
+
// For now, use default OTLP HTTP port
|
|
186
|
+
// In production, this would be discovered or set by Beamable
|
|
187
|
+
localEndpoint = '0.0.0.0:4318';
|
|
188
|
+
}
|
|
166
189
|
localEndpoint = localEndpoint.replace(/^http:\/\//, '').replace(/^https:\/\//, '');
|
|
167
190
|
// Set environment variables for collector
|
|
168
191
|
const env = {
|
|
@@ -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 } from 'path';\r\nimport { pipeline } from 'stream/promises';\r\nimport { createGunzip } from 'zlib';\r\nimport type { Logger } from 'pino';\r\nimport dgram from 'dgram';\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\ninterface 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 = '0.0.123'; // Match C# collector version\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 * 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 if (platform === 'linux' && arch === 'x64') {\r\n return 'beamable-collector-linux-amd64';\r\n } else if (platform === 'linux' && arch === 'arm64') {\r\n return 'beamable-collector-linux-arm64';\r\n } else if (platform === 'darwin' && arch === 'x64') {\r\n return 'beamable-collector-darwin-amd64';\r\n } else if (platform === 'darwin' && arch === 'arm64') {\r\n return 'beamable-collector-darwin-arm64';\r\n } else if (platform === 'win32' && arch === 'x64') {\r\n return 'beamable-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 = require('path').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): 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 }\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 }\r\n \r\n for (const item of itemsToDownload) {\r\n await downloadAndDecompressGzip(item.url, item.path, item.executable);\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 * 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 * Starts the OpenTelemetry collector process\r\n */\r\nexport async function startCollector(\r\n logger: Logger,\r\n otlpEndpoint?: string\r\n): Promise<{ process: ChildProcess; endpoint: string }> {\r\n const collectorInfo = await resolveCollector(true);\r\n \r\n if (!collectorInfo.binaryPath) {\r\n throw new Error('Collector binary not found and download failed');\r\n }\r\n \r\n if (!collectorInfo.configPath) {\r\n throw new Error('Collector config not found and download failed');\r\n }\r\n \r\n // Determine OTLP endpoint\r\n let localEndpoint = otlpEndpoint || 'localhost:4318';\r\n localEndpoint = localEndpoint.replace(/^http:\\/\\//, '').replace(/^https:\\/\\//, '');\r\n \r\n // Set environment variables for collector\r\n const env = {\r\n ...process.env,\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_CLICKHOUSE_ENDPOINT: process.env.BEAM_CLICKHOUSE_ENDPOINT || '',\r\n BEAM_CLICKHOUSE_USERNAME: process.env.BEAM_CLICKHOUSE_USERNAME || '',\r\n BEAM_CLICKHOUSE_PASSWORD: process.env.BEAM_CLICKHOUSE_PASSWORD || '',\r\n BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),\r\n BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: process.env.BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT || '5s',\r\n BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: process.env.BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE || '5000',\r\n BEAM_CLICKHOUSE_EXPORTER_TIMEOUT: process.env.BEAM_CLICKHOUSE_EXPORTER_TIMEOUT || '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE: process.env.BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE || '1000',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED || 'true',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL || '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL || '30s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME || '300s',\r\n };\r\n \r\n // Start collector process\r\n const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {\r\n env,\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): 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);\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 type { Logger } from 'pino';\r\nimport dgram from 'dgram';\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\ninterface 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 * 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 * 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 * Starts the OpenTelemetry collector process\r\n */\r\nexport async function startCollector(\r\n logger: Logger,\r\n otlpEndpoint?: string\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 // 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 const env = {\r\n ...process.env,\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_CLICKHOUSE_ENDPOINT: process.env.BEAM_CLICKHOUSE_ENDPOINT || '',\r\n BEAM_CLICKHOUSE_USERNAME: process.env.BEAM_CLICKHOUSE_USERNAME || '',\r\n BEAM_CLICKHOUSE_PASSWORD: process.env.BEAM_CLICKHOUSE_PASSWORD || '',\r\n BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),\r\n BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: process.env.BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT || '5s',\r\n BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: process.env.BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE || '5000',\r\n BEAM_CLICKHOUSE_EXPORTER_TIMEOUT: process.env.BEAM_CLICKHOUSE_EXPORTER_TIMEOUT || '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE: process.env.BEAM_CLICKHOUSE_EXPORTER_QUEUE_SIZE || '1000',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_ENABLED || 'true',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_INITIAL_INTERVAL || '5s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_INTERVAL || '30s',\r\n BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME || '300s',\r\n };\r\n \r\n // Start collector process\r\n const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {\r\n env,\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): 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);\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/package.json
CHANGED
package/src/collector-manager.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn, ChildProcess } from 'child_process';
|
|
2
2
|
import { createWriteStream, existsSync, chmodSync, mkdirSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
4
|
import { pipeline } from 'stream/promises';
|
|
5
5
|
import { createGunzip } from 'zlib';
|
|
6
6
|
import type { Logger } from 'pino';
|
|
@@ -21,7 +21,7 @@ interface CollectorStatus {
|
|
|
21
21
|
version?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const COLLECTOR_VERSION = '
|
|
24
|
+
const COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)
|
|
25
25
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
26
26
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
27
27
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
@@ -43,16 +43,18 @@ function getCollectorBinaryName(): string {
|
|
|
43
43
|
const platform = process.platform;
|
|
44
44
|
const arch = process.arch;
|
|
45
45
|
|
|
46
|
+
// Match C# naming: collector-{osArchSuffix}
|
|
47
|
+
// C# returns "collector-linux-amd64", not "beamable-collector-linux-amd64"
|
|
46
48
|
if (platform === 'linux' && arch === 'x64') {
|
|
47
|
-
return '
|
|
49
|
+
return 'collector-linux-amd64';
|
|
48
50
|
} else if (platform === 'linux' && arch === 'arm64') {
|
|
49
|
-
return '
|
|
51
|
+
return 'collector-linux-arm64';
|
|
50
52
|
} else if (platform === 'darwin' && arch === 'x64') {
|
|
51
|
-
return '
|
|
53
|
+
return 'collector-darwin-amd64';
|
|
52
54
|
} else if (platform === 'darwin' && arch === 'arm64') {
|
|
53
|
-
return '
|
|
55
|
+
return 'collector-darwin-arm64';
|
|
54
56
|
} else if (platform === 'win32' && arch === 'x64') {
|
|
55
|
-
return '
|
|
57
|
+
return 'collector-windows-amd64.exe';
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
@@ -67,7 +69,7 @@ async function downloadAndDecompressGzip(url: string, outputPath: string, makeEx
|
|
|
67
69
|
throw new Error(`Failed to download ${url}: ${response.statusText}`);
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
const dir =
|
|
72
|
+
const dir = dirname(outputPath);
|
|
71
73
|
if (!existsSync(dir)) {
|
|
72
74
|
mkdirSync(dir, { recursive: true });
|
|
73
75
|
}
|
|
@@ -89,7 +91,7 @@ async function downloadAndDecompressGzip(url: string, outputPath: string, makeEx
|
|
|
89
91
|
/**
|
|
90
92
|
* Resolves the collector binary and config, downloading if needed
|
|
91
93
|
*/
|
|
92
|
-
async function resolveCollector(allowDownload: boolean = true): Promise<{ binaryPath: string | null; configPath: string | null }> {
|
|
94
|
+
async function resolveCollector(allowDownload: boolean = true, logger?: Logger): Promise<{ binaryPath: string | null; configPath: string | null }> {
|
|
93
95
|
const basePath = getCollectorStoragePath();
|
|
94
96
|
const binaryName = getCollectorBinaryName();
|
|
95
97
|
const configName = 'clickhouse-config.yaml';
|
|
@@ -102,15 +104,23 @@ async function resolveCollector(allowDownload: boolean = true): Promise<{ binary
|
|
|
102
104
|
if (!existsSync(binaryPath) && allowDownload) {
|
|
103
105
|
const binaryUrl = `${COLLECTOR_DOWNLOAD_BASE}/${binaryName}.gz`;
|
|
104
106
|
itemsToDownload.push({ url: binaryUrl, path: binaryPath, executable: true });
|
|
107
|
+
logger?.info(`[Collector] Will download binary from: ${binaryUrl}`);
|
|
108
|
+
} else if (existsSync(binaryPath)) {
|
|
109
|
+
logger?.info(`[Collector] Binary found at: ${binaryPath}`);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
if (!existsSync(configPath) && allowDownload) {
|
|
108
113
|
const configUrl = `${COLLECTOR_DOWNLOAD_BASE}/${configName}.gz`;
|
|
109
114
|
itemsToDownload.push({ url: configUrl, path: configPath, executable: false });
|
|
115
|
+
logger?.info(`[Collector] Will download config from: ${configUrl}`);
|
|
116
|
+
} else if (existsSync(configPath)) {
|
|
117
|
+
logger?.info(`[Collector] Config found at: ${configPath}`);
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
for (const item of itemsToDownload) {
|
|
121
|
+
logger?.info(`[Collector] Downloading ${item.url}...`);
|
|
113
122
|
await downloadAndDecompressGzip(item.url, item.path, item.executable);
|
|
123
|
+
logger?.info(`[Collector] Downloaded to ${item.path}`);
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
return {
|
|
@@ -191,18 +201,30 @@ export async function startCollector(
|
|
|
191
201
|
logger: Logger,
|
|
192
202
|
otlpEndpoint?: string
|
|
193
203
|
): Promise<{ process: ChildProcess; endpoint: string }> {
|
|
194
|
-
|
|
204
|
+
logger.info('[Collector] Resolving collector binary and config...');
|
|
205
|
+
const collectorInfo = await resolveCollector(true, logger);
|
|
195
206
|
|
|
196
207
|
if (!collectorInfo.binaryPath) {
|
|
208
|
+
logger.error('[Collector] Binary not found and download failed');
|
|
197
209
|
throw new Error('Collector binary not found and download failed');
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
if (!collectorInfo.configPath) {
|
|
213
|
+
logger.error('[Collector] Config not found and download failed');
|
|
201
214
|
throw new Error('Collector config not found and download failed');
|
|
202
215
|
}
|
|
203
216
|
|
|
217
|
+
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
218
|
+
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
219
|
+
|
|
204
220
|
// Determine OTLP endpoint
|
|
205
|
-
|
|
221
|
+
// Use a free port if not specified (like C# does with PortUtil.FreeEndpoint())
|
|
222
|
+
let localEndpoint = otlpEndpoint;
|
|
223
|
+
if (!localEndpoint) {
|
|
224
|
+
// For now, use default OTLP HTTP port
|
|
225
|
+
// In production, this would be discovered or set by Beamable
|
|
226
|
+
localEndpoint = '0.0.0.0:4318';
|
|
227
|
+
}
|
|
206
228
|
localEndpoint = localEndpoint.replace(/^http:\/\//, '').replace(/^https:\/\//, '');
|
|
207
229
|
|
|
208
230
|
// Set environment variables for collector
|