@omen.foundation/node-microservice-runtime 0.1.26 → 0.1.27
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 +78 -18
- package/dist/collector-manager.d.ts +27 -3
- package/dist/collector-manager.d.ts.map +1 -1
- package/dist/collector-manager.js +89 -18
- package/dist/collector-manager.js.map +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.cjs +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/collector-manager.ts +125 -21
- package/src/index.ts +5 -0
- package/src/logger.ts +1 -1
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fetchClickHouseCredentials = fetchClickHouseCredentials;
|
|
7
|
+
exports.getClickHouseCredentialsStatus = getClickHouseCredentialsStatus;
|
|
6
8
|
exports.isCollectorRunning = isCollectorRunning;
|
|
7
9
|
exports.startCollector = startCollector;
|
|
8
10
|
exports.discoverOrStartCollector = discoverOrStartCollector;
|
|
@@ -12,11 +14,39 @@ const path_1 = require("path");
|
|
|
12
14
|
const promises_1 = require("stream/promises");
|
|
13
15
|
const zlib_1 = require("zlib");
|
|
14
16
|
const dgram_1 = __importDefault(require("dgram"));
|
|
17
|
+
const urls_js_1 = require("./utils/urls.js");
|
|
15
18
|
const COLLECTOR_VERSION = '1.0.1';
|
|
16
19
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
17
20
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
18
21
|
const DISCOVERY_DELAY = 100;
|
|
19
22
|
const DISCOVERY_ATTEMPTS = 10;
|
|
23
|
+
async function fetchClickHouseCredentials(env, logger) {
|
|
24
|
+
const apiUrl = (0, urls_js_1.hostToHttpUrl)(env.host);
|
|
25
|
+
const configUrl = new URL('/api/beamo/otel/auth/writer/config', apiUrl).toString();
|
|
26
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
27
|
+
const headers = {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
Accept: 'application/json',
|
|
30
|
+
'beam-scope': `${env.cid}.${env.pid}`,
|
|
31
|
+
};
|
|
32
|
+
const response = await fetch(configUrl, {
|
|
33
|
+
method: 'GET',
|
|
34
|
+
headers,
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
38
|
+
throw new Error(`Failed to fetch ClickHouse credentials: ${response.status} ${response.statusText} - ${errorText}`);
|
|
39
|
+
}
|
|
40
|
+
const credentials = await response.json();
|
|
41
|
+
if (!credentials.endpoint || !credentials.username || !credentials.password) {
|
|
42
|
+
throw new Error('Invalid ClickHouse credentials response: missing required fields');
|
|
43
|
+
}
|
|
44
|
+
logger.info('[Collector] Successfully fetched ClickHouse credentials');
|
|
45
|
+
if (credentials.expiresAt) {
|
|
46
|
+
logger.debug(`[Collector] Credentials expire at: ${credentials.expiresAt}`);
|
|
47
|
+
}
|
|
48
|
+
return credentials;
|
|
49
|
+
}
|
|
20
50
|
function getCollectorStoragePath() {
|
|
21
51
|
const tempDir = process.env.TMPDIR || process.env.TMP || '/tmp';
|
|
22
52
|
return (0, path_1.join)(tempDir, 'beam', 'collectors', COLLECTOR_VERSION);
|
|
@@ -124,6 +154,25 @@ function discoverCollectorViaUDP(timeoutMs = 5000) {
|
|
|
124
154
|
});
|
|
125
155
|
});
|
|
126
156
|
}
|
|
157
|
+
function getClickHouseCredentialsStatus() {
|
|
158
|
+
const hasEndpoint = !!process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
159
|
+
const hasUsername = !!process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
160
|
+
const hasPassword = !!process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
161
|
+
if (hasEndpoint && hasUsername && hasPassword) {
|
|
162
|
+
return {
|
|
163
|
+
hasEndpoint: true,
|
|
164
|
+
hasUsername: true,
|
|
165
|
+
hasPassword: true,
|
|
166
|
+
source: 'environment',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
hasEndpoint,
|
|
171
|
+
hasUsername,
|
|
172
|
+
hasPassword,
|
|
173
|
+
source: 'missing',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
127
176
|
async function isCollectorRunning() {
|
|
128
177
|
try {
|
|
129
178
|
const discovered = await discoverCollectorViaUDP(2000);
|
|
@@ -145,7 +194,7 @@ async function isCollectorRunning() {
|
|
|
145
194
|
pid: 0,
|
|
146
195
|
};
|
|
147
196
|
}
|
|
148
|
-
async function startCollector(logger, otlpEndpoint) {
|
|
197
|
+
async function startCollector(logger, otlpEndpoint, env) {
|
|
149
198
|
var _a, _b;
|
|
150
199
|
logger.info('[Collector] Resolving collector binary and config...');
|
|
151
200
|
const collectorInfo = await resolveCollector(true, logger);
|
|
@@ -159,16 +208,27 @@ async function startCollector(logger, otlpEndpoint) {
|
|
|
159
208
|
}
|
|
160
209
|
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
161
210
|
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
211
|
+
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
212
|
+
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
213
|
+
let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
214
|
+
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
215
|
+
try {
|
|
216
|
+
const credentials = await fetchClickHouseCredentials(env, logger);
|
|
217
|
+
clickhouseEndpoint = credentials.endpoint;
|
|
218
|
+
clickhouseUsername = credentials.username;
|
|
219
|
+
clickhousePassword = credentials.password;
|
|
220
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = clickhouseEndpoint;
|
|
221
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = clickhouseUsername;
|
|
222
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = clickhousePassword;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
226
|
+
logger.error(errorMsg);
|
|
227
|
+
throw new Error(errorMsg);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) {
|
|
231
|
+
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.`;
|
|
172
232
|
logger.error(errorMsg);
|
|
173
233
|
throw new Error(errorMsg);
|
|
174
234
|
}
|
|
@@ -177,12 +237,12 @@ async function startCollector(logger, otlpEndpoint) {
|
|
|
177
237
|
localEndpoint = '0.0.0.0:4318';
|
|
178
238
|
}
|
|
179
239
|
localEndpoint = localEndpoint.replace(/^http:\/\//, '').replace(/^https:\/\//, '');
|
|
180
|
-
const
|
|
240
|
+
const collectorEnv = {
|
|
181
241
|
...process.env,
|
|
182
242
|
BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,
|
|
183
|
-
BEAM_CLICKHOUSE_ENDPOINT:
|
|
184
|
-
BEAM_CLICKHOUSE_USERNAME:
|
|
185
|
-
BEAM_CLICKHOUSE_PASSWORD:
|
|
243
|
+
BEAM_CLICKHOUSE_ENDPOINT: clickhouseEndpoint,
|
|
244
|
+
BEAM_CLICKHOUSE_USERNAME: clickhouseUsername,
|
|
245
|
+
BEAM_CLICKHOUSE_PASSWORD: clickhousePassword,
|
|
186
246
|
BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),
|
|
187
247
|
BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: process.env.BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT || '5s',
|
|
188
248
|
BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: process.env.BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE || '5000',
|
|
@@ -194,7 +254,7 @@ async function startCollector(logger, otlpEndpoint) {
|
|
|
194
254
|
BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME: process.env.BEAM_CLICKHOUSE_EXPORTER_RETRY_MAX_ELAPSED_TIME || '300s',
|
|
195
255
|
};
|
|
196
256
|
const collectorProcess = (0, child_process_1.spawn)(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {
|
|
197
|
-
env,
|
|
257
|
+
env: collectorEnv,
|
|
198
258
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
199
259
|
detached: false,
|
|
200
260
|
});
|
|
@@ -216,7 +276,7 @@ async function startCollector(logger, otlpEndpoint) {
|
|
|
216
276
|
endpoint: `http://${localEndpoint}`,
|
|
217
277
|
};
|
|
218
278
|
}
|
|
219
|
-
async function discoverOrStartCollector(logger, standardOtelEnabled) {
|
|
279
|
+
async function discoverOrStartCollector(logger, standardOtelEnabled, env) {
|
|
220
280
|
if (!standardOtelEnabled) {
|
|
221
281
|
return null;
|
|
222
282
|
}
|
|
@@ -227,7 +287,7 @@ async function discoverOrStartCollector(logger, standardOtelEnabled) {
|
|
|
227
287
|
}
|
|
228
288
|
try {
|
|
229
289
|
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
230
|
-
const { endpoint } = await startCollector(logger);
|
|
290
|
+
const { endpoint } = await startCollector(logger, undefined, env);
|
|
231
291
|
for (let i = 0; i < DISCOVERY_ATTEMPTS; i++) {
|
|
232
292
|
await new Promise(resolve => setTimeout(resolve, DISCOVERY_DELAY));
|
|
233
293
|
const newStatus = await isCollectorRunning();
|
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
import { ChildProcess } from 'child_process';
|
|
2
2
|
import type { Logger } from 'pino';
|
|
3
|
-
|
|
3
|
+
import type { EnvironmentConfig } from './types.js';
|
|
4
|
+
export interface CollectorStatus {
|
|
4
5
|
isRunning: boolean;
|
|
5
6
|
isReady: boolean;
|
|
6
7
|
pid: number;
|
|
7
8
|
otlpEndpoint?: string;
|
|
8
9
|
version?: string;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* ClickHouse credentials response from Beamable API
|
|
13
|
+
*/
|
|
14
|
+
interface ClickHouseCredentials {
|
|
15
|
+
endpoint: string;
|
|
16
|
+
expiresAt?: string;
|
|
17
|
+
password: string;
|
|
18
|
+
username: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fetches ClickHouse credentials from Beamable API
|
|
22
|
+
* GET /api/beamo/otel/auth/writer/config
|
|
23
|
+
*/
|
|
24
|
+
export declare function fetchClickHouseCredentials(env: EnvironmentConfig, logger: Logger): Promise<ClickHouseCredentials>;
|
|
25
|
+
/**
|
|
26
|
+
* Gets the current status of ClickHouse credentials (from env or API)
|
|
27
|
+
*/
|
|
28
|
+
export declare function getClickHouseCredentialsStatus(): {
|
|
29
|
+
hasEndpoint: boolean;
|
|
30
|
+
hasUsername: boolean;
|
|
31
|
+
hasPassword: boolean;
|
|
32
|
+
source: 'environment' | 'api' | 'missing';
|
|
33
|
+
};
|
|
10
34
|
/**
|
|
11
35
|
* Checks if collector is already running via UDP discovery
|
|
12
36
|
*/
|
|
@@ -14,13 +38,13 @@ export declare function isCollectorRunning(): Promise<CollectorStatus>;
|
|
|
14
38
|
/**
|
|
15
39
|
* Starts the OpenTelemetry collector process
|
|
16
40
|
*/
|
|
17
|
-
export declare function startCollector(logger: Logger, otlpEndpoint?: string): Promise<{
|
|
41
|
+
export declare function startCollector(logger: Logger, otlpEndpoint?: string, env?: EnvironmentConfig): Promise<{
|
|
18
42
|
process: ChildProcess;
|
|
19
43
|
endpoint: string;
|
|
20
44
|
}>;
|
|
21
45
|
/**
|
|
22
46
|
* Discovers or starts the collector and returns the OTLP endpoint
|
|
23
47
|
*/
|
|
24
|
-
export declare function discoverOrStartCollector(logger: Logger, standardOtelEnabled: boolean): Promise<string | null>;
|
|
48
|
+
export declare function discoverOrStartCollector(logger: Logger, standardOtelEnabled: boolean, env?: EnvironmentConfig): Promise<string | null>;
|
|
25
49
|
export {};
|
|
26
50
|
//# sourceMappingURL=collector-manager.d.ts.map
|
|
@@ -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;
|
|
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;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;AAQD;;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;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,qBAAqB,CAAC,CAqChC;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;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,CA4GtD;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"}
|
|
@@ -4,11 +4,46 @@ 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
|
+
import { hostToHttpUrl } from './utils/urls.js';
|
|
7
8
|
const COLLECTOR_VERSION = '1.0.1'; // Match C# collector version (from collector-version.json)
|
|
8
9
|
const COLLECTOR_DOWNLOAD_BASE = `https://collectors.beamable.com/version/${COLLECTOR_VERSION}`;
|
|
9
10
|
const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '8688', 10);
|
|
10
11
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
11
12
|
const DISCOVERY_ATTEMPTS = 10;
|
|
13
|
+
/**
|
|
14
|
+
* Fetches ClickHouse credentials from Beamable API
|
|
15
|
+
* GET /api/beamo/otel/auth/writer/config
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchClickHouseCredentials(env, logger) {
|
|
18
|
+
const apiUrl = hostToHttpUrl(env.host);
|
|
19
|
+
const configUrl = new URL('/api/beamo/otel/auth/writer/config', apiUrl).toString();
|
|
20
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
21
|
+
// Build headers with authentication
|
|
22
|
+
const headers = {
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
Accept: 'application/json',
|
|
25
|
+
'beam-scope': `${env.cid}.${env.pid}`,
|
|
26
|
+
};
|
|
27
|
+
// If we have a secret, we might need to sign the request
|
|
28
|
+
// For now, try without signature (the API might use the beam-scope header for auth)
|
|
29
|
+
const response = await fetch(configUrl, {
|
|
30
|
+
method: 'GET',
|
|
31
|
+
headers,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
35
|
+
throw new Error(`Failed to fetch ClickHouse credentials: ${response.status} ${response.statusText} - ${errorText}`);
|
|
36
|
+
}
|
|
37
|
+
const credentials = await response.json();
|
|
38
|
+
if (!credentials.endpoint || !credentials.username || !credentials.password) {
|
|
39
|
+
throw new Error('Invalid ClickHouse credentials response: missing required fields');
|
|
40
|
+
}
|
|
41
|
+
logger.info('[Collector] Successfully fetched ClickHouse credentials');
|
|
42
|
+
if (credentials.expiresAt) {
|
|
43
|
+
logger.debug(`[Collector] Credentials expire at: ${credentials.expiresAt}`);
|
|
44
|
+
}
|
|
45
|
+
return credentials;
|
|
46
|
+
}
|
|
12
47
|
/**
|
|
13
48
|
* Gets the collector storage directory (similar to C# LocalApplicationData/beam/collectors/version)
|
|
14
49
|
*/
|
|
@@ -137,6 +172,28 @@ function discoverCollectorViaUDP(timeoutMs = 5000) {
|
|
|
137
172
|
});
|
|
138
173
|
});
|
|
139
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Gets the current status of ClickHouse credentials (from env or API)
|
|
177
|
+
*/
|
|
178
|
+
export function getClickHouseCredentialsStatus() {
|
|
179
|
+
const hasEndpoint = !!process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
180
|
+
const hasUsername = !!process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
181
|
+
const hasPassword = !!process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
182
|
+
if (hasEndpoint && hasUsername && hasPassword) {
|
|
183
|
+
return {
|
|
184
|
+
hasEndpoint: true,
|
|
185
|
+
hasUsername: true,
|
|
186
|
+
hasPassword: true,
|
|
187
|
+
source: 'environment',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
hasEndpoint,
|
|
192
|
+
hasUsername,
|
|
193
|
+
hasPassword,
|
|
194
|
+
source: 'missing',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
140
197
|
/**
|
|
141
198
|
* Checks if collector is already running via UDP discovery
|
|
142
199
|
*/
|
|
@@ -165,7 +222,7 @@ export async function isCollectorRunning() {
|
|
|
165
222
|
/**
|
|
166
223
|
* Starts the OpenTelemetry collector process
|
|
167
224
|
*/
|
|
168
|
-
export async function startCollector(logger, otlpEndpoint) {
|
|
225
|
+
export async function startCollector(logger, otlpEndpoint, env) {
|
|
169
226
|
logger.info('[Collector] Resolving collector binary and config...');
|
|
170
227
|
const collectorInfo = await resolveCollector(true, logger);
|
|
171
228
|
if (!collectorInfo.binaryPath) {
|
|
@@ -178,18 +235,32 @@ export async function startCollector(logger, otlpEndpoint) {
|
|
|
178
235
|
}
|
|
179
236
|
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
180
237
|
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
238
|
+
// Fetch ClickHouse credentials from Beamable API if not in environment variables
|
|
239
|
+
// Per Gabriel: "these OTEL related ones you need to do it yourself at the beginning of the microservice startup"
|
|
240
|
+
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
241
|
+
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
242
|
+
let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
243
|
+
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
244
|
+
try {
|
|
245
|
+
const credentials = await fetchClickHouseCredentials(env, logger);
|
|
246
|
+
clickhouseEndpoint = credentials.endpoint;
|
|
247
|
+
clickhouseUsername = credentials.username;
|
|
248
|
+
clickhousePassword = credentials.password;
|
|
249
|
+
// Set them in process.env so the collector can access them
|
|
250
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = clickhouseEndpoint;
|
|
251
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = clickhouseUsername;
|
|
252
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = clickhousePassword;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
256
|
+
logger.error(errorMsg);
|
|
257
|
+
throw new Error(errorMsg);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
181
260
|
// Validate required environment variables (matching C# behavior)
|
|
182
261
|
// These must be set before starting the collector, otherwise it will fail
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
BEAM_CLICKHOUSE_USERNAME: process.env.BEAM_CLICKHOUSE_USERNAME,
|
|
186
|
-
BEAM_CLICKHOUSE_PASSWORD: process.env.BEAM_CLICKHOUSE_PASSWORD,
|
|
187
|
-
};
|
|
188
|
-
const missingVars = Object.entries(requiredEnvVars)
|
|
189
|
-
.filter(([_, value]) => !value || value.trim() === '')
|
|
190
|
-
.map(([key]) => key);
|
|
191
|
-
if (missingVars.length > 0) {
|
|
192
|
-
const errorMsg = `[Collector] Required environment variables are missing or empty: ${missingVars.join(', ')}. Collector will fail to start without these.`;
|
|
262
|
+
if (!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) {
|
|
263
|
+
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.`;
|
|
193
264
|
logger.error(errorMsg);
|
|
194
265
|
throw new Error(errorMsg);
|
|
195
266
|
}
|
|
@@ -205,12 +276,12 @@ export async function startCollector(logger, otlpEndpoint) {
|
|
|
205
276
|
// Set environment variables for collector
|
|
206
277
|
// Note: BEAM_CLICKHOUSE_ENDPOINT is for collector → ClickHouse communication
|
|
207
278
|
// This is different from the OTLP endpoint (microservice → collector)
|
|
208
|
-
const
|
|
279
|
+
const collectorEnv = {
|
|
209
280
|
...process.env,
|
|
210
281
|
BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,
|
|
211
|
-
BEAM_CLICKHOUSE_ENDPOINT:
|
|
212
|
-
BEAM_CLICKHOUSE_USERNAME:
|
|
213
|
-
BEAM_CLICKHOUSE_PASSWORD:
|
|
282
|
+
BEAM_CLICKHOUSE_ENDPOINT: clickhouseEndpoint,
|
|
283
|
+
BEAM_CLICKHOUSE_USERNAME: clickhouseUsername,
|
|
284
|
+
BEAM_CLICKHOUSE_PASSWORD: clickhousePassword,
|
|
214
285
|
BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),
|
|
215
286
|
BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: process.env.BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT || '5s',
|
|
216
287
|
BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: process.env.BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE || '5000',
|
|
@@ -223,7 +294,7 @@ export async function startCollector(logger, otlpEndpoint) {
|
|
|
223
294
|
};
|
|
224
295
|
// Start collector process
|
|
225
296
|
const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {
|
|
226
|
-
env,
|
|
297
|
+
env: collectorEnv,
|
|
227
298
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
228
299
|
detached: false,
|
|
229
300
|
});
|
|
@@ -248,7 +319,7 @@ export async function startCollector(logger, otlpEndpoint) {
|
|
|
248
319
|
/**
|
|
249
320
|
* Discovers or starts the collector and returns the OTLP endpoint
|
|
250
321
|
*/
|
|
251
|
-
export async function discoverOrStartCollector(logger, standardOtelEnabled) {
|
|
322
|
+
export async function discoverOrStartCollector(logger, standardOtelEnabled, env) {
|
|
252
323
|
if (!standardOtelEnabled) {
|
|
253
324
|
return null;
|
|
254
325
|
}
|
|
@@ -261,7 +332,7 @@ export async function discoverOrStartCollector(logger, standardOtelEnabled) {
|
|
|
261
332
|
// Collector not running - start it
|
|
262
333
|
try {
|
|
263
334
|
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
264
|
-
const { endpoint } = await startCollector(logger);
|
|
335
|
+
const { endpoint } = await startCollector(logger, undefined, env);
|
|
265
336
|
// Wait a bit for collector to start and become ready
|
|
266
337
|
// Try to discover it
|
|
267
338
|
for (let i = 0; i < DISCOVERY_ATTEMPTS; i++) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-manager.js","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgB,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAmB1B,MAAM,iBAAiB,GAAG,OAAO,CAAC,CAAC,2DAA2D;AAC9F,MAAM,uBAAuB,GAAG,2CAA2C,iBAAiB,EAAE,CAAC;AAC/F,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;AACzF,MAAM,eAAe,GAAG,GAAG,CAAC,CAAC,eAAe;AAC5C,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B;;GAEG;AACH,SAAS,uBAAuB;IAC9B,qEAAqE;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,MAAM,CAAC;IAChE,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1B,4CAA4C;IAC5C,2EAA2E;IAC3E,IAAI,QAAQ,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QAC3C,OAAO,uBAAuB,CAAC;IACjC,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACpD,OAAO,uBAAuB,CAAC;IACjC,CAAC;SAAM,IAAI,QAAQ,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnD,OAAO,wBAAwB,CAAC;IAClC,CAAC;SAAM,IAAI,QAAQ,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrD,OAAO,wBAAwB,CAAC;IAClC,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QAClD,OAAO,6BAA6B,CAAC;IACvC,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,yBAAyB,CAAC,GAAW,EAAE,UAAkB,EAAE,iBAA0B,KAAK;IACvG,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAElD,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAE1D,IAAI,cAAc,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,UAAU,cAAc,EAAE,KAAK,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAAC,gBAAyB,IAAI,EAAE,MAAe;IAC5E,MAAM,QAAQ,GAAG,uBAAuB,EAAE,CAAC;IAC3C,MAAM,UAAU,GAAG,sBAAsB,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,wBAAwB,CAAC;IAE5C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE9C,MAAM,eAAe,GAA8D,EAAE,CAAC;IAEtF,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,aAAa,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,GAAG,uBAAuB,IAAI,UAAU,KAAK,CAAC;QAChE,eAAe,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,MAAM,EAAE,IAAI,CAAC,0CAA0C,SAAS,EAAE,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,aAAa,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,GAAG,uBAAuB,IAAI,UAAU,KAAK,CAAC;QAChE,eAAe,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9E,MAAM,EAAE,IAAI,CAAC,0CAA0C,SAAS,EAAE,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,EAAE,IAAI,CAAC,2BAA2B,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QACvD,MAAM,yBAAyB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,MAAM,EAAE,IAAI,CAAC,6BAA6B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,OAAO;QACL,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;QACtD,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;KACvD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,YAAoB,IAAI;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,UAAU,GAA8B,EAAE,CAAC;QACjD,IAAI,OAAuB,CAAC;QAE5B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAA4B,CAAC;gBACtE,2BAA2B;gBAC3B,IAAI,OAAO,CAAC,OAAO,KAAK,iBAAiB,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;oBACxE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,sBAAsB;YACxB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAE1B,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBACxB,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,wCAAwC;gBACxC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACxD,CAAC,EAAE,SAAS,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,UAAU,EAAE,CAAC;YACf,OAAO;gBACL,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,UAAU,CAAC,MAAM,KAAK,OAAO;gBACtC,GAAG,EAAE,UAAU,CAAC,GAAG;gBACnB,YAAY,EAAE,UAAU,CAAC,YAAY;gBACrC,OAAO,EAAE,UAAU,CAAC,OAAO;aAC5B,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mDAAmD;IACrD,CAAC;IAED,OAAO;QACL,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,CAAC;KACP,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAc,EACd,YAAqB;IAErB,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE3D,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,6BAA6B,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC;IACrE,MAAM,CAAC,IAAI,CAAC,6BAA6B,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC;IAErE,iEAAiE;IACjE,0EAA0E;IAC1E,MAAM,eAAe,GAAG;QACtB,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QAC9D,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QAC9D,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;KAC/D,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;SAChD,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;SACrD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAEvB,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,oEAAoE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,+CAA+C,CAAC;QAC3J,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,0BAA0B;IAC1B,+EAA+E;IAC/E,IAAI,aAAa,GAAG,YAAY,CAAC;IACjC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,sCAAsC;QACtC,6DAA6D;QAC7D,aAAa,GAAG,cAAc,CAAC;IACjC,CAAC;IACD,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAEnF,0CAA0C;IAC1C,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,GAAG,GAAG;QACV,GAAG,OAAO,CAAC,GAAG;QACd,uBAAuB,EAAE,aAAa;QACtC,wBAAwB,EAAE,eAAe,CAAC,wBAAyB;QACnE,wBAAwB,EAAE,eAAe,CAAC,wBAAyB;QACnE,wBAAwB,EAAE,eAAe,CAAC,wBAAyB;QACnE,6BAA6B,EAAE,MAAM,CAAC,cAAc,CAAC;QACrD,iCAAiC,EAAE,OAAO,CAAC,GAAG,CAAC,iCAAiC,IAAI,IAAI;QACxF,oCAAoC,EAAE,OAAO,CAAC,GAAG,CAAC,oCAAoC,IAAI,MAAM;QAChG,gCAAgC,EAAE,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,IAAI;QACtF,mCAAmC,EAAE,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,MAAM;QAC9F,sCAAsC,EAAE,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,MAAM;QACpG,+CAA+C,EAAE,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,IAAI;QACpH,2CAA2C,EAAE,OAAO,CAAC,GAAG,CAAC,2CAA2C,IAAI,KAAK;QAC7G,+CAA+C,EAAE,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,MAAM;KACvH,CAAC;IAEF,0BAA0B;IAC1B,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE;QAC/F,GAAG;QACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC3C,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC3C,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACnC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACnC,MAAM,CAAC,IAAI,CAAC,wCAAwC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,gCAAgC,gBAAgB,CAAC,GAAG,eAAe,aAAa,EAAE,CAAC,CAAC;IAEhG,OAAO;QACL,OAAO,EAAE,gBAAgB;QACzB,QAAQ,EAAE,UAAU,aAAa,EAAE;KACpC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAc,EACd,mBAA4B;IAE5B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+CAA+C;IAC/C,MAAM,MAAM,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC1C,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC,0CAA0C,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAC7E,OAAO,UAAU,MAAM,CAAC,YAAY,EAAE,CAAC;IACzC,CAAC;IAED,mCAAmC;IACnC,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QAC/D,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;QAElD,qDAAqD;QACrD,qBAAqB;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC;YACnE,MAAM,SAAS,GAAG,MAAM,kBAAkB,EAAE,CAAC;YAC7C,IAAI,SAAS,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBAC7C,MAAM,CAAC,IAAI,CAAC,qCAAqC,SAAS,CAAC,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;gBACvF,OAAO,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;YAChF,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,MAAM,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QAC1F,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3G,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","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\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\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 // Validate required environment variables (matching C# behavior)\r\n // These must be set before starting the collector, otherwise it will fail\r\n const requiredEnvVars = {\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 };\r\n \r\n const missingVars = Object.entries(requiredEnvVars)\r\n .filter(([_, value]) => !value || value.trim() === '')\r\n .map(([key]) => key);\r\n \r\n if (missingVars.length > 0) {\r\n const errorMsg = `[Collector] Required environment variables are missing or empty: ${missingVars.join(', ')}. Collector will fail to start without these.`;\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 const env = {\r\n ...process.env,\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_CLICKHOUSE_ENDPOINT: requiredEnvVars.BEAM_CLICKHOUSE_ENDPOINT!,\r\n BEAM_CLICKHOUSE_USERNAME: requiredEnvVars.BEAM_CLICKHOUSE_USERNAME!,\r\n BEAM_CLICKHOUSE_PASSWORD: requiredEnvVars.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\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 * Fetches ClickHouse credentials from Beamable API\r\n * GET /api/beamo/otel/auth/writer/config\r\n */\r\nexport async function fetchClickHouseCredentials(\r\n env: EnvironmentConfig,\r\n logger: Logger\r\n): Promise<ClickHouseCredentials> {\r\n const apiUrl = hostToHttpUrl(env.host);\r\n const configUrl = new URL('/api/beamo/otel/auth/writer/config', apiUrl).toString();\r\n \r\n logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');\r\n \r\n // Build headers with authentication\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'beam-scope': `${env.cid}.${env.pid}`,\r\n };\r\n \r\n // If we have a secret, we might need to sign the request\r\n // For now, try without signature (the API might use the beam-scope header for auth)\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: ${response.status} ${response.statusText} - ${errorText}`);\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 logger.info('[Collector] Successfully fetched ClickHouse credentials');\r\n if (credentials.expiresAt) {\r\n logger.debug(`[Collector] Credentials expire at: ${credentials.expiresAt}`);\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 * 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 // 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, logger);\r\n clickhouseEndpoint = credentials.endpoint;\r\n clickhouseUsername = credentials.username;\r\n clickhousePassword = credentials.password;\r\n \r\n // Set them in process.env so the collector can access them\r\n process.env.BEAM_CLICKHOUSE_ENDPOINT = clickhouseEndpoint;\r\n process.env.BEAM_CLICKHOUSE_USERNAME = clickhouseUsername;\r\n process.env.BEAM_CLICKHOUSE_PASSWORD = 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 const collectorEnv = {\r\n ...process.env,\r\n BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,\r\n BEAM_CLICKHOUSE_ENDPOINT: clickhouseEndpoint!,\r\n BEAM_CLICKHOUSE_USERNAME: clickhouseUsername!,\r\n BEAM_CLICKHOUSE_PASSWORD: clickhousePassword!,\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: 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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
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.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; } });
|
|
@@ -40,3 +40,6 @@ Object.defineProperty(exports, "FederationComponent", { enumerable: true, get: f
|
|
|
40
40
|
Object.defineProperty(exports, "FederationRegistry", { enumerable: true, get: function () { return federation_js_1.FederationRegistry; } });
|
|
41
41
|
Object.defineProperty(exports, "getFederationComponents", { enumerable: true, get: function () { return federation_js_1.getFederationComponents; } });
|
|
42
42
|
Object.defineProperty(exports, "FederatedInventory", { enumerable: true, get: function () { return federation_js_1.FederatedInventory; } });
|
|
43
|
+
var collector_manager_js_1 = require("./collector-manager.js");
|
|
44
|
+
Object.defineProperty(exports, "isCollectorRunning", { enumerable: true, get: function () { return collector_manager_js_1.isCollectorRunning; } });
|
|
45
|
+
Object.defineProperty(exports, "getClickHouseCredentialsStatus", { enumerable: true, get: function () { return collector_manager_js_1.getClickHouseCredentialsStatus; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +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, type CollectorStatus, } from './collector-manager.js';
|
|
13
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"}
|
|
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,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,4 +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, } from './collector-manager.js';
|
|
10
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","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\n"]}
|
|
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,GAE/B,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 type CollectorStatus,\r\n} from './collector-manager.js';\r\n"]}
|
package/dist/logger.cjs
CHANGED
|
@@ -108,7 +108,7 @@ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, log
|
|
|
108
108
|
let endpointUrl = otlpEndpoint;
|
|
109
109
|
if (!endpointUrl && standardOtelEnabled && logger) {
|
|
110
110
|
try {
|
|
111
|
-
const discoveredEndpoint = await (0, collector_manager_js_1.discoverOrStartCollector)(logger, standardOtelEnabled);
|
|
111
|
+
const discoveredEndpoint = await (0, collector_manager_js_1.discoverOrStartCollector)(logger, standardOtelEnabled, env);
|
|
112
112
|
if (discoveredEndpoint) {
|
|
113
113
|
endpointUrl = discoveredEndpoint;
|
|
114
114
|
}
|
package/dist/logger.js
CHANGED
|
@@ -109,7 +109,7 @@ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, log
|
|
|
109
109
|
if (!endpointUrl && standardOtelEnabled && logger) {
|
|
110
110
|
// Try to discover existing collector or start a new one (like C# does)
|
|
111
111
|
try {
|
|
112
|
-
const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled);
|
|
112
|
+
const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled, env);
|
|
113
113
|
if (discoveredEndpoint) {
|
|
114
114
|
endpointUrl = discoveredEndpoint;
|
|
115
115
|
}
|
package/dist/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { createRequire } from 'node:module';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { logs } from '@opentelemetry/api-logs';\r\nimport { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';\r\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\r\nimport { resourceFromAttributes, defaultResource } from '@opentelemetry/resources';\r\nimport { discoverOrStartCollector } from './collector-manager.js';\r\nimport type { Logger as PinoLogger } from 'pino';\r\nimport deasync from 'deasync';\r\n\r\n// Helper to get require function that works in both CJS and ESM\r\ndeclare const require: any;\r\nfunction getRequire(): any {\r\n // Check if we're in CJS context (require.main exists)\r\n if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {\r\n // CJS context - use require directly\r\n return require;\r\n }\r\n // ESM context - use createRequire with import.meta.url\r\n // TypeScript will complain in CJS builds, but this code only runs in ESM\r\n // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected\r\n return createRequire(import.meta.url);\r\n}\r\n\r\n/**\r\n * Determines if we should use pretty logs (local dev) or raw JSON logs (deployed).\r\n * \r\n * Simple check: If IS_LOCAL=1 is set in environment, use pretty logs.\r\n * Otherwise, use raw Beamable JSON format for log collection.\r\n */\r\nfunction shouldUsePrettyLogs(): boolean {\r\n // Check for explicit IS_LOCAL flag\r\n return process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true';\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n serviceName?: string; // Service name for log filtering (e.g., \"ExampleNodeService\")\r\n qualifiedServiceName?: string; // Full qualified service name (e.g., \"micro_ExampleNodeService\")\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Initializes OpenTelemetry OTLP log exporter if configured.\r\n * Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.\r\n * \r\n * @param serviceName - Service name for resource attributes\r\n * @param qualifiedServiceName - Qualified service name for resource attributes\r\n * @param env - Environment configuration\r\n * @returns OTLP logger provider if configured, null otherwise\r\n */\r\nasync function initializeOtlpLogging(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig,\r\n logger?: PinoLogger\r\n): Promise<LoggerProvider | null> {\r\n // Check for explicit OTLP endpoint (same as C#: BEAM_OTEL_EXPORTER_OTLP_ENDPOINT)\r\n // Also check standard OTEL environment variables\r\n const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT \r\n || process.env.OTEL_EXPORTER_OTLP_ENDPOINT\r\n || (process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS : null);\r\n \r\n // Check for explicit OTLP protocol (same as C#: OtelExporterOtlpProtocol)\r\n // C# uses OtlpExportProtocol enum: HttpProtobuf or HttpJson\r\n const otlpProtocol = process.env.BEAM_OTEL_EXPORTER_OTLP_PROTOCOL\r\n || process.env.OTEL_EXPORTER_OTLP_PROTOCOL\r\n || process.env.OTEL_EXPORTER_OTLP_PROTOCOL_LOGS;\r\n \r\n // Check if standard OTLP is enabled (matching C# OtelExporterStandardEnabled)\r\n // C#: (this.InDocker() || UseLocalOtel) && string.IsNullOrEmpty(Environment.GetEnvironmentVariable(\"BEAM_DISABLE_STANDARD_OTEL\"))\r\n // UseLocalOtel = BEAM_LOCAL_OTEL is set\r\n // InDocker = IS_LOCAL is not set (for Node.js, we check IS_LOCAL !== '1' && !== 'true')\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const useLocalOtel = !!process.env.BEAM_LOCAL_OTEL;\r\n const standardOtelEnabled = (isInDocker || useLocalOtel) && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n \r\n // If no explicit endpoint and standard OTLP not enabled, skip OTLP\r\n if (!otlpEndpoint && !standardOtelEnabled) {\r\n return Promise.resolve(null);\r\n }\r\n \r\n try {\r\n // Build resource attributes (similar to C# resourceProvider)\r\n const resourceAttributes: Record<string, string> = {};\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.instance.id'] = qualifiedServiceName;\r\n }\r\n if (env?.cid) {\r\n resourceAttributes['beam.cid'] = String(env.cid);\r\n }\r\n if (env?.pid) {\r\n resourceAttributes['beam.pid'] = String(env.pid);\r\n }\r\n if (env?.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(env.routingKey);\r\n }\r\n \r\n // Determine endpoint URL\r\n // If explicit endpoint provided, use it (append /v1/logs if needed for HTTP)\r\n // If standard OTLP enabled but no explicit endpoint, try to discover or start collector\r\n let endpointUrl = otlpEndpoint;\r\n \r\n if (!endpointUrl && standardOtelEnabled && logger) {\r\n // Try to discover existing collector or start a new one (like C# does)\r\n try {\r\n const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled);\r\n if (discoveredEndpoint) {\r\n endpointUrl = discoveredEndpoint;\r\n } else {\r\n console.error('[OTLP] Standard OTLP is enabled but could not discover or start collector.');\r\n return Promise.resolve(null);\r\n }\r\n } catch (error) {\r\n console.error('[OTLP] Failed to discover/start collector:', error instanceof Error ? error.message : String(error));\r\n return Promise.resolve(null);\r\n }\r\n }\r\n \r\n // If still no endpoint, skip OTLP\r\n if (!endpointUrl) {\r\n if (standardOtelEnabled) {\r\n console.error('[OTLP] Standard OTLP is enabled but no endpoint available. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');\r\n }\r\n return Promise.resolve(null);\r\n }\r\n \r\n // Ensure endpoint format is correct based on protocol\r\n // C# behavior:\r\n // - Http protocol: endpoint should be like \"http://127.0.0.1:4348\" (with /v1/logs appended)\r\n // - Grpc protocol: endpoint should be like \"127.0.0.1:4348\" (no http:// prefix, no /v1/logs)\r\n // For now, we only support HTTP exporter, so we append /v1/logs\r\n // If Grpc is specified, we'd need a different exporter package\r\n let finalEndpointUrl = endpointUrl;\r\n if (otlpProtocol && otlpProtocol.toLowerCase() === 'grpc') {\r\n // Grpc protocol - don't modify endpoint (no http://, no /v1/logs)\r\n // Note: Grpc exporter not yet implemented, this is for future support\r\n console.warn('[OTLP] Grpc protocol specified but not yet supported, using HTTP endpoint format');\r\n finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n } else {\r\n // HTTP protocol - ensure /v1/logs suffix\r\n finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n }\r\n \r\n // Create OTLP HTTP exporter\r\n // Beamable uses HttpProtobuf as the standard protocol\r\n // C# behavior:\r\n // 1. If OtelExporterOtlpEndpoint and OtelExporterOtlpProtocol are provided, use them\r\n // 2. Otherwise, always use HttpProtobuf protocol (Beamable's standard)\r\n // Node.js: @opentelemetry/exporter-logs-otlp-http uses HTTP/JSON by default\r\n // We configure contentType to 'application/x-protobuf' to match Beamable's standard\r\n const exporterOptions: {\r\n url: string;\r\n headers?: Record<string, string>;\r\n contentType?: string;\r\n } = {\r\n url: finalEndpointUrl,\r\n };\r\n \r\n // Configure protocol/contentType (matching C# OtlpExportProtocol)\r\n // Beamable uses HttpProtobuf as the default protocol\r\n // C# supports: HttpProtobuf, HttpJson, Grpc\r\n // If explicit protocol provided, use it; otherwise always default to HttpProtobuf\r\n if (otlpProtocol) {\r\n // Parse protocol string (HttpProtobuf, HttpJson, or Grpc)\r\n const protocol = otlpProtocol.toLowerCase();\r\n if (protocol === 'httpprotobuf' || protocol === 'protobuf') {\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n } else if (protocol === 'httpjson' || protocol === 'json') {\r\n exporterOptions.contentType = 'application/json';\r\n } else if (protocol === 'grpc') {\r\n // Note: Grpc protocol would require a different exporter package\r\n // For now, we'll log a warning and fall back to HttpProtobuf\r\n console.warn('[OTLP] Grpc protocol is not yet supported in Node.js runtime, falling back to HttpProtobuf');\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n } else {\r\n // Invalid protocol - default to HttpProtobuf (Beamable's standard)\r\n console.warn(`[OTLP] Unknown protocol \"${otlpProtocol}\", defaulting to HttpProtobuf`);\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n }\r\n } else {\r\n // No protocol specified - always default to HttpProtobuf (Beamable's standard)\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n }\r\n \r\n // Headers if provided (matching C# OtelExporterOtlpHeaders)\r\n if (process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS) {\r\n exporterOptions.headers = JSON.parse(process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS);\r\n }\r\n \r\n const exporter = new OTLPLogExporter(exporterOptions);\r\n \r\n // Create resource with attributes (merge with default resource)\r\n // Note: BEAM_ALLOW_STARTUP_WITHOUT_ATTRIBUTES_RESOURCE is not applicable here\r\n // as we always create a resource with attributes\r\n const baseResource = defaultResource();\r\n const customResource = resourceFromAttributes(resourceAttributes);\r\n const resource = baseResource.merge(customResource);\r\n \r\n // Create log record processor\r\n // Note: C# supports retry via BEAM_DISABLE_RETRY_OTEL and BEAM_OTEL_RETRY_MAX_SIZE\r\n // SimpleLogRecordProcessor doesn't support retry - would need BatchLogRecordProcessor\r\n // For now, we use SimpleLogRecordProcessor (no retry)\r\n // TODO: Consider implementing retry logic if BEAM_DISABLE_RETRY_OTEL is not set\r\n const processor = new SimpleLogRecordProcessor(exporter);\r\n \r\n // Create logger provider with resource and processor\r\n const loggerProvider = new LoggerProvider({\r\n resource: resource,\r\n processors: [processor],\r\n });\r\n \r\n // Set as global logger provider\r\n logs.setGlobalLoggerProvider(loggerProvider);\r\n \r\n // Log successful initialization (to stdout for debugging)\r\n // This helps diagnose if OTLP is working in deployed environments\r\n console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);\r\n \r\n // CRITICAL: Verify collector is actually ready before returning\r\n // If we started a collector, wait for it to be ready (up to 5 seconds)\r\n if (standardOtelEnabled && logger) {\r\n try {\r\n const { isCollectorRunning } = await import('./collector-manager.js');\r\n // Wait up to 5 seconds for collector to be ready\r\n for (let i = 0; i < 50; i++) {\r\n const status = await isCollectorRunning();\r\n if (status.isRunning && status.isReady) {\r\n if (logger) {\r\n logger.info(`[OTLP] Collector confirmed ready at ${status.otlpEndpoint || finalEndpointUrl}`);\r\n }\r\n break;\r\n }\r\n // Wait 100ms between checks\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n } catch (error) {\r\n // If we can't verify, log but continue\r\n if (logger) {\r\n logger.warn(`[OTLP] Could not verify collector readiness: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n }\r\n \r\n return Promise.resolve(loggerProvider);\r\n } catch (error) {\r\n // If OTLP initialization fails, log error but continue without OTLP\r\n // Don't throw - we still want stdout logging to work\r\n // Log to stderr so it's visible in container logs\r\n console.error('[OTLP] Failed to initialize OTLP logging:', error instanceof Error ? error.message : String(error));\r\n if (error instanceof Error && error.stack) {\r\n console.error('[OTLP] Stack trace:', error.stack);\r\n }\r\n return Promise.resolve(null);\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Also includes OpenTelemetry-compatible fields for ClickHouse compatibility.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n * \r\n * Also sends logs via OTLP if OTLP logger provider is configured.\r\n */\r\nfunction createBeamableLogFormatter(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n otlpProviderRef?: { provider: LoggerProvider | null }\r\n): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings/Buffers, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n // Ensure we have a Buffer - Pino may write strings or Buffers\r\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\r\n try {\r\n const line = buffer.toString('utf8');\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format (for CloudWatch Logs Insights)\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include service name in context for CloudWatch filtering\r\n if (serviceName) {\r\n contextFields.serviceName = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n contextFields.qualifiedServiceName = qualifiedServiceName;\r\n }\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Add OpenTelemetry-compatible fields for ClickHouse compatibility\r\n // These fields allow an OpenTelemetry collector to parse and forward logs to ClickHouse\r\n // The Portal's realm-level logs page queries ClickHouse's otel_logs table\r\n const otelFields: Record<string, unknown> = {};\r\n \r\n // ResourceAttributes - service identification (for ClickHouse filtering)\r\n const resourceAttributes: Record<string, unknown> = {};\r\n // IMPORTANT: The Portal searches for service:ExampleNodeService (just the service name)\r\n // So we need to set service.namespace to the service name, NOT the qualified name\r\n // The qualified name (micro_ExampleNodeService) is used for CloudWatch log stream filtering\r\n // But ClickHouse uses just the service name for filtering\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n // Also include qualified name for reference\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.qualifiedName'] = qualifiedServiceName;\r\n }\r\n if (pinoLog.cid) {\r\n resourceAttributes['beam.cid'] = String(pinoLog.cid);\r\n }\r\n if (pinoLog.pid) {\r\n resourceAttributes['beam.pid'] = String(pinoLog.pid);\r\n }\r\n if (pinoLog.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(pinoLog.routingKey);\r\n }\r\n \r\n // LogAttributes - log-specific attributes\r\n const logAttributes: Record<string, unknown> = {};\r\n if (pinoLog.component) {\r\n logAttributes['component'] = String(pinoLog.component);\r\n }\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n if (err.message) logAttributes['exception.message'] = String(err.message);\r\n if (err.stack) logAttributes['exception.stacktrace'] = String(err.stack);\r\n if (err.type) logAttributes['exception.type'] = String(err.type);\r\n }\r\n \r\n // Map Beamable log level to OpenTelemetry SeverityText\r\n // Beamable: Debug, Info, Warning, Error, Fatal\r\n // OpenTelemetry: Trace, Debug, Info, Warn, Error, Fatal, Unspecified\r\n const severityTextMap: Record<string, string> = {\r\n 'Debug': 'Debug',\r\n 'Info': 'Information',\r\n 'Warning': 'Warning',\r\n 'Error': 'Error',\r\n 'Fatal': 'Critical',\r\n };\r\n const severityText = severityTextMap[level] || 'Information';\r\n \r\n // CRITICAL: ResourceAttributes and LogAttributes MUST always be present (even if empty)\r\n // ClickHouse schema expects these fields to exist for proper querying\r\n // The Portal queries ResourceAttributes['service.namespace'] - if ResourceAttributes is missing, queries fail\r\n \r\n // Ensure ResourceAttributes always has service.namespace when serviceName is provided\r\n // This is the primary field used by Portal for service filtering\r\n if (serviceName && !resourceAttributes['service.namespace']) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n \r\n // Add OpenTelemetry fields to the log\r\n // These are in addition to the Beamable format, so both systems can parse the logs\r\n // CRITICAL: Always include all required OpenTelemetry fields for ClickHouse compatibility\r\n otelFields['Timestamp'] = timestamp; // OpenTelemetry timestamp format (ISO 8601)\r\n otelFields['SeverityText'] = severityText;\r\n otelFields['Body'] = messageParts.length > 0 ? messageParts.join(' ') : 'No message';\r\n // ALWAYS include ResourceAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['ResourceAttributes'] = resourceAttributes;\r\n // ALWAYS include LogAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['LogAttributes'] = logAttributes;\r\n \r\n // IMPORTANT: For CloudWatch Logs Insights, we need to ensure @message contains valid JSON\r\n // The Portal expects @message to be a JSON string that can be parsed to extract __t, __l, __m\r\n // We output the Beamable format as the primary format, and include OpenTelemetry fields\r\n // CloudWatch will store this as @message, and an OpenTelemetry collector will parse it\r\n // and forward to ClickHouse's otel_logs table\r\n \r\n // Merge OpenTelemetry fields into the log (for ClickHouse compatibility)\r\n // An OpenTelemetry collector can parse these fields and forward to ClickHouse\r\n // Note: These extra fields won't break CloudWatch - it will just store them in @message\r\n Object.assign(beamableLog, otelFields);\r\n \r\n // Send log via OTLP if configured (similar to C# MicroserviceOtelLogRecordExporter)\r\n // Check if provider is available (may be null if still initializing)\r\n if (otlpProviderRef?.provider) {\r\n try {\r\n const otlpLogger = otlpProviderRef.provider.getLogger(\r\n serviceName || 'beamable-node-runtime',\r\n undefined, // version\r\n {\r\n schemaUrl: undefined, // optional schema URL\r\n }\r\n );\r\n \r\n // Map Beamable level to OpenTelemetry SeverityNumber\r\n const severityNumberMap: Record<string, number> = {\r\n 'Debug': 5, // SEVERITY_NUMBER_DEBUG\r\n 'Info': 9, // SEVERITY_NUMBER_INFO\r\n 'Warning': 13, // SEVERITY_NUMBER_WARN\r\n 'Error': 17, // SEVERITY_NUMBER_ERROR\r\n 'Fatal': 21, // SEVERITY_NUMBER_FATAL\r\n };\r\n \r\n // Create log record for OTLP\r\n otlpLogger.emit({\r\n severityNumber: severityNumberMap[level] || 9,\r\n severityText: severityText,\r\n body: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n attributes: {\r\n ...logAttributes,\r\n // Include additional context\r\n ...(pinoLog.component ? { component: String(pinoLog.component) } : {}),\r\n },\r\n timestamp: new Date(timestamp).getTime() * 1_000_000, // nanoseconds\r\n observedTimestamp: Date.now() * 1_000_000, // nanoseconds\r\n });\r\n } catch (otlpError) {\r\n // If OTLP send fails, continue with stdout logging\r\n // Don't block the log output\r\n // Note: C# supports retry via BEAM_DISABLE_RETRY_OTEL and BEAM_OTEL_RETRY_MAX_SIZE\r\n // SimpleLogRecordProcessor doesn't support retry - would need BatchLogRecordProcessor\r\n // For now, we silently continue (retry would be handled at processor level if implemented)\r\n }\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n // CloudWatch Logs Insights will store this entire JSON string in the @message field\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Synchronously initializes OTLP logging with a timeout.\r\n * This ensures OTLP is ready before any logs are emitted.\r\n * Similar to C# microservices which configure logging early in startup.\r\n * \r\n * Uses a blocking wait mechanism to ensure initialization completes before returning.\r\n */\r\nfunction initializeOtlpSync(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig,\r\n timeoutMs: number = 60000 // 60 seconds to allow collector download, startup, and readiness check\r\n): LoggerProvider | null {\r\n // Match C# logic: (this.InDocker() || UseLocalOtel) && !BEAM_DISABLE_STANDARD_OTEL\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const useLocalOtel = !!process.env.BEAM_LOCAL_OTEL;\r\n const standardOtelEnabled = (isInDocker || useLocalOtel) && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n const hasExplicitEndpoint = !!process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT;\r\n \r\n // If OTLP is not needed, return immediately\r\n if (!standardOtelEnabled && !hasExplicitEndpoint) {\r\n return null;\r\n }\r\n \r\n // Create a minimal console logger for initialization messages\r\n const initLogger = pino({\r\n name: 'beamable-otlp-init',\r\n level: 'info',\r\n }, process.stdout);\r\n \r\n // Use deasync to wait synchronously for the async initialization\r\n // This allows the event loop to process while we wait, enabling async operations\r\n // (like collector download) to complete\r\n let provider: LoggerProvider | null = null;\r\n let completed = false;\r\n let initError: Error | null = null;\r\n \r\n // Start initialization promise (callbacks set flags for deasync.loopWhile to check)\r\n initializeOtlpLogging(\r\n serviceName,\r\n qualifiedServiceName,\r\n env,\r\n initLogger\r\n ).then((result) => {\r\n provider = result;\r\n completed = true;\r\n if (result) {\r\n initLogger.info('[OTLP] OpenTelemetry logging initialized successfully');\r\n }\r\n return result;\r\n }).catch((error) => {\r\n initError = error instanceof Error ? error : new Error(String(error));\r\n completed = true;\r\n initLogger.error(`[OTLP] Failed to initialize: ${initError.message}`);\r\n return null;\r\n });\r\n \r\n // Set timeout to prevent infinite wait\r\n const startTime = Date.now();\r\n const timeoutId = setTimeout(() => {\r\n if (!completed) {\r\n initLogger.warn(`[OTLP] Initialization timeout after ${timeoutMs}ms, continuing without OTLP`);\r\n completed = true;\r\n }\r\n }, timeoutMs);\r\n \r\n // Wait synchronously for the promise to resolve using deasync\r\n // deasync.loopWhile() allows event loop to process while we wait\r\n // This enables async operations (like collector download) to run and complete\r\n // CRITICAL: This must complete before the logger is created to capture all startup logs\r\n try {\r\n // Use deasync to wait for completion, with timeout check\r\n // loopWhile returns false to stop waiting, true to continue\r\n deasync.loopWhile(() => {\r\n // Check if we've exceeded timeout\r\n const elapsed = Date.now() - startTime;\r\n if (elapsed >= timeoutMs) {\r\n initLogger.warn(`[OTLP] Synchronous wait timeout after ${elapsed}ms`);\r\n return false; // Stop waiting\r\n }\r\n // Continue waiting if not completed\r\n // This allows the event loop to process async operations\r\n return !completed;\r\n });\r\n } catch (error) {\r\n // If deasync fails, log and continue\r\n initLogger.error(`[OTLP] Error during synchronous wait: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n \r\n clearTimeout(timeoutId);\r\n \r\n // Verify we got a provider (collector is ready)\r\n if (completed && provider) {\r\n initLogger.info('[OTLP] Initialization completed successfully, collector is ready');\r\n } else if (!completed) {\r\n initLogger.warn('[OTLP] Initialization did not complete in time, logs may not be sent via OTLP initially');\r\n initLogger.warn('[OTLP] Service will continue without OTLP telemetry - requests will still be serviced');\r\n } else {\r\n initLogger.warn('[OTLP] Initialization completed but no provider returned, OTLP telemetry disabled');\r\n }\r\n \r\n return provider;\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const usePrettyLogs = shouldUsePrettyLogs();\r\n \r\n // Initialize OTLP synchronously BEFORE creating the logger\r\n // This ensures all logs from this point forward are captured via OTLP\r\n // Similar to C# microservices which configure logging early in startup\r\n // CRITICAL: We wait for collector to be fully initialized and ready before proceeding\r\n const otlpProvider = initializeOtlpSync(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n env,\r\n 60000 // 60 second timeout to allow collector download, startup, and readiness verification\r\n );\r\n \r\n // Shared reference for OTLP logger provider\r\n const otlpProviderRef: { provider: LoggerProvider | null } = { provider: otlpProvider };\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n // Include service name in base fields for filtering\r\n serviceName: options.serviceName,\r\n qualifiedServiceName: options.qualifiedServiceName,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (!usePrettyLogs) {\r\n // Deployed/remote: Use Beamable JSON format for log collection\r\n // Include OpenTelemetry fields for ClickHouse compatibility\r\n // Also send logs via OTLP if configured\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n // Use getRequire() which handles both CJS and ESM contexts\r\n const requireFn = getRequire();\r\n const pinoPretty = requireFn('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n // This is expected if pino-pretty isn't installed, so we silently fall back\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format if not local, default Pino format if local\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (!usePrettyLogs) {\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { createRequire } from 'node:module';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { logs } from '@opentelemetry/api-logs';\r\nimport { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';\r\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\r\nimport { resourceFromAttributes, defaultResource } from '@opentelemetry/resources';\r\nimport { discoverOrStartCollector } from './collector-manager.js';\r\nimport type { Logger as PinoLogger } from 'pino';\r\nimport deasync from 'deasync';\r\n\r\n// Helper to get require function that works in both CJS and ESM\r\ndeclare const require: any;\r\nfunction getRequire(): any {\r\n // Check if we're in CJS context (require.main exists)\r\n if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {\r\n // CJS context - use require directly\r\n return require;\r\n }\r\n // ESM context - use createRequire with import.meta.url\r\n // TypeScript will complain in CJS builds, but this code only runs in ESM\r\n // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected\r\n return createRequire(import.meta.url);\r\n}\r\n\r\n/**\r\n * Determines if we should use pretty logs (local dev) or raw JSON logs (deployed).\r\n * \r\n * Simple check: If IS_LOCAL=1 is set in environment, use pretty logs.\r\n * Otherwise, use raw Beamable JSON format for log collection.\r\n */\r\nfunction shouldUsePrettyLogs(): boolean {\r\n // Check for explicit IS_LOCAL flag\r\n return process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true';\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n serviceName?: string; // Service name for log filtering (e.g., \"ExampleNodeService\")\r\n qualifiedServiceName?: string; // Full qualified service name (e.g., \"micro_ExampleNodeService\")\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Initializes OpenTelemetry OTLP log exporter if configured.\r\n * Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.\r\n * \r\n * @param serviceName - Service name for resource attributes\r\n * @param qualifiedServiceName - Qualified service name for resource attributes\r\n * @param env - Environment configuration\r\n * @returns OTLP logger provider if configured, null otherwise\r\n */\r\nasync function initializeOtlpLogging(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig,\r\n logger?: PinoLogger\r\n): Promise<LoggerProvider | null> {\r\n // Check for explicit OTLP endpoint (same as C#: BEAM_OTEL_EXPORTER_OTLP_ENDPOINT)\r\n // Also check standard OTEL environment variables\r\n const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT \r\n || process.env.OTEL_EXPORTER_OTLP_ENDPOINT\r\n || (process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS : null);\r\n \r\n // Check for explicit OTLP protocol (same as C#: OtelExporterOtlpProtocol)\r\n // C# uses OtlpExportProtocol enum: HttpProtobuf or HttpJson\r\n const otlpProtocol = process.env.BEAM_OTEL_EXPORTER_OTLP_PROTOCOL\r\n || process.env.OTEL_EXPORTER_OTLP_PROTOCOL\r\n || process.env.OTEL_EXPORTER_OTLP_PROTOCOL_LOGS;\r\n \r\n // Check if standard OTLP is enabled (matching C# OtelExporterStandardEnabled)\r\n // C#: (this.InDocker() || UseLocalOtel) && string.IsNullOrEmpty(Environment.GetEnvironmentVariable(\"BEAM_DISABLE_STANDARD_OTEL\"))\r\n // UseLocalOtel = BEAM_LOCAL_OTEL is set\r\n // InDocker = IS_LOCAL is not set (for Node.js, we check IS_LOCAL !== '1' && !== 'true')\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const useLocalOtel = !!process.env.BEAM_LOCAL_OTEL;\r\n const standardOtelEnabled = (isInDocker || useLocalOtel) && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n \r\n // If no explicit endpoint and standard OTLP not enabled, skip OTLP\r\n if (!otlpEndpoint && !standardOtelEnabled) {\r\n return Promise.resolve(null);\r\n }\r\n \r\n try {\r\n // Build resource attributes (similar to C# resourceProvider)\r\n const resourceAttributes: Record<string, string> = {};\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.instance.id'] = qualifiedServiceName;\r\n }\r\n if (env?.cid) {\r\n resourceAttributes['beam.cid'] = String(env.cid);\r\n }\r\n if (env?.pid) {\r\n resourceAttributes['beam.pid'] = String(env.pid);\r\n }\r\n if (env?.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(env.routingKey);\r\n }\r\n \r\n // Determine endpoint URL\r\n // If explicit endpoint provided, use it (append /v1/logs if needed for HTTP)\r\n // If standard OTLP enabled but no explicit endpoint, try to discover or start collector\r\n let endpointUrl = otlpEndpoint;\r\n \r\n if (!endpointUrl && standardOtelEnabled && logger) {\r\n // Try to discover existing collector or start a new one (like C# does)\r\n try {\r\n const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled, env);\r\n if (discoveredEndpoint) {\r\n endpointUrl = discoveredEndpoint;\r\n } else {\r\n console.error('[OTLP] Standard OTLP is enabled but could not discover or start collector.');\r\n return Promise.resolve(null);\r\n }\r\n } catch (error) {\r\n console.error('[OTLP] Failed to discover/start collector:', error instanceof Error ? error.message : String(error));\r\n return Promise.resolve(null);\r\n }\r\n }\r\n \r\n // If still no endpoint, skip OTLP\r\n if (!endpointUrl) {\r\n if (standardOtelEnabled) {\r\n console.error('[OTLP] Standard OTLP is enabled but no endpoint available. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');\r\n }\r\n return Promise.resolve(null);\r\n }\r\n \r\n // Ensure endpoint format is correct based on protocol\r\n // C# behavior:\r\n // - Http protocol: endpoint should be like \"http://127.0.0.1:4348\" (with /v1/logs appended)\r\n // - Grpc protocol: endpoint should be like \"127.0.0.1:4348\" (no http:// prefix, no /v1/logs)\r\n // For now, we only support HTTP exporter, so we append /v1/logs\r\n // If Grpc is specified, we'd need a different exporter package\r\n let finalEndpointUrl = endpointUrl;\r\n if (otlpProtocol && otlpProtocol.toLowerCase() === 'grpc') {\r\n // Grpc protocol - don't modify endpoint (no http://, no /v1/logs)\r\n // Note: Grpc exporter not yet implemented, this is for future support\r\n console.warn('[OTLP] Grpc protocol specified but not yet supported, using HTTP endpoint format');\r\n finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n } else {\r\n // HTTP protocol - ensure /v1/logs suffix\r\n finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n }\r\n \r\n // Create OTLP HTTP exporter\r\n // Beamable uses HttpProtobuf as the standard protocol\r\n // C# behavior:\r\n // 1. If OtelExporterOtlpEndpoint and OtelExporterOtlpProtocol are provided, use them\r\n // 2. Otherwise, always use HttpProtobuf protocol (Beamable's standard)\r\n // Node.js: @opentelemetry/exporter-logs-otlp-http uses HTTP/JSON by default\r\n // We configure contentType to 'application/x-protobuf' to match Beamable's standard\r\n const exporterOptions: {\r\n url: string;\r\n headers?: Record<string, string>;\r\n contentType?: string;\r\n } = {\r\n url: finalEndpointUrl,\r\n };\r\n \r\n // Configure protocol/contentType (matching C# OtlpExportProtocol)\r\n // Beamable uses HttpProtobuf as the default protocol\r\n // C# supports: HttpProtobuf, HttpJson, Grpc\r\n // If explicit protocol provided, use it; otherwise always default to HttpProtobuf\r\n if (otlpProtocol) {\r\n // Parse protocol string (HttpProtobuf, HttpJson, or Grpc)\r\n const protocol = otlpProtocol.toLowerCase();\r\n if (protocol === 'httpprotobuf' || protocol === 'protobuf') {\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n } else if (protocol === 'httpjson' || protocol === 'json') {\r\n exporterOptions.contentType = 'application/json';\r\n } else if (protocol === 'grpc') {\r\n // Note: Grpc protocol would require a different exporter package\r\n // For now, we'll log a warning and fall back to HttpProtobuf\r\n console.warn('[OTLP] Grpc protocol is not yet supported in Node.js runtime, falling back to HttpProtobuf');\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n } else {\r\n // Invalid protocol - default to HttpProtobuf (Beamable's standard)\r\n console.warn(`[OTLP] Unknown protocol \"${otlpProtocol}\", defaulting to HttpProtobuf`);\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n }\r\n } else {\r\n // No protocol specified - always default to HttpProtobuf (Beamable's standard)\r\n exporterOptions.contentType = 'application/x-protobuf';\r\n }\r\n \r\n // Headers if provided (matching C# OtelExporterOtlpHeaders)\r\n if (process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS) {\r\n exporterOptions.headers = JSON.parse(process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS);\r\n }\r\n \r\n const exporter = new OTLPLogExporter(exporterOptions);\r\n \r\n // Create resource with attributes (merge with default resource)\r\n // Note: BEAM_ALLOW_STARTUP_WITHOUT_ATTRIBUTES_RESOURCE is not applicable here\r\n // as we always create a resource with attributes\r\n const baseResource = defaultResource();\r\n const customResource = resourceFromAttributes(resourceAttributes);\r\n const resource = baseResource.merge(customResource);\r\n \r\n // Create log record processor\r\n // Note: C# supports retry via BEAM_DISABLE_RETRY_OTEL and BEAM_OTEL_RETRY_MAX_SIZE\r\n // SimpleLogRecordProcessor doesn't support retry - would need BatchLogRecordProcessor\r\n // For now, we use SimpleLogRecordProcessor (no retry)\r\n // TODO: Consider implementing retry logic if BEAM_DISABLE_RETRY_OTEL is not set\r\n const processor = new SimpleLogRecordProcessor(exporter);\r\n \r\n // Create logger provider with resource and processor\r\n const loggerProvider = new LoggerProvider({\r\n resource: resource,\r\n processors: [processor],\r\n });\r\n \r\n // Set as global logger provider\r\n logs.setGlobalLoggerProvider(loggerProvider);\r\n \r\n // Log successful initialization (to stdout for debugging)\r\n // This helps diagnose if OTLP is working in deployed environments\r\n console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);\r\n \r\n // CRITICAL: Verify collector is actually ready before returning\r\n // If we started a collector, wait for it to be ready (up to 5 seconds)\r\n if (standardOtelEnabled && logger) {\r\n try {\r\n const { isCollectorRunning } = await import('./collector-manager.js');\r\n // Wait up to 5 seconds for collector to be ready\r\n for (let i = 0; i < 50; i++) {\r\n const status = await isCollectorRunning();\r\n if (status.isRunning && status.isReady) {\r\n if (logger) {\r\n logger.info(`[OTLP] Collector confirmed ready at ${status.otlpEndpoint || finalEndpointUrl}`);\r\n }\r\n break;\r\n }\r\n // Wait 100ms between checks\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n } catch (error) {\r\n // If we can't verify, log but continue\r\n if (logger) {\r\n logger.warn(`[OTLP] Could not verify collector readiness: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n }\r\n }\r\n \r\n return Promise.resolve(loggerProvider);\r\n } catch (error) {\r\n // If OTLP initialization fails, log error but continue without OTLP\r\n // Don't throw - we still want stdout logging to work\r\n // Log to stderr so it's visible in container logs\r\n console.error('[OTLP] Failed to initialize OTLP logging:', error instanceof Error ? error.message : String(error));\r\n if (error instanceof Error && error.stack) {\r\n console.error('[OTLP] Stack trace:', error.stack);\r\n }\r\n return Promise.resolve(null);\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Also includes OpenTelemetry-compatible fields for ClickHouse compatibility.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n * \r\n * Also sends logs via OTLP if OTLP logger provider is configured.\r\n */\r\nfunction createBeamableLogFormatter(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n otlpProviderRef?: { provider: LoggerProvider | null }\r\n): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings/Buffers, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n // Ensure we have a Buffer - Pino may write strings or Buffers\r\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\r\n try {\r\n const line = buffer.toString('utf8');\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format (for CloudWatch Logs Insights)\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include service name in context for CloudWatch filtering\r\n if (serviceName) {\r\n contextFields.serviceName = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n contextFields.qualifiedServiceName = qualifiedServiceName;\r\n }\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Add OpenTelemetry-compatible fields for ClickHouse compatibility\r\n // These fields allow an OpenTelemetry collector to parse and forward logs to ClickHouse\r\n // The Portal's realm-level logs page queries ClickHouse's otel_logs table\r\n const otelFields: Record<string, unknown> = {};\r\n \r\n // ResourceAttributes - service identification (for ClickHouse filtering)\r\n const resourceAttributes: Record<string, unknown> = {};\r\n // IMPORTANT: The Portal searches for service:ExampleNodeService (just the service name)\r\n // So we need to set service.namespace to the service name, NOT the qualified name\r\n // The qualified name (micro_ExampleNodeService) is used for CloudWatch log stream filtering\r\n // But ClickHouse uses just the service name for filtering\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n // Also include qualified name for reference\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.qualifiedName'] = qualifiedServiceName;\r\n }\r\n if (pinoLog.cid) {\r\n resourceAttributes['beam.cid'] = String(pinoLog.cid);\r\n }\r\n if (pinoLog.pid) {\r\n resourceAttributes['beam.pid'] = String(pinoLog.pid);\r\n }\r\n if (pinoLog.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(pinoLog.routingKey);\r\n }\r\n \r\n // LogAttributes - log-specific attributes\r\n const logAttributes: Record<string, unknown> = {};\r\n if (pinoLog.component) {\r\n logAttributes['component'] = String(pinoLog.component);\r\n }\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n if (err.message) logAttributes['exception.message'] = String(err.message);\r\n if (err.stack) logAttributes['exception.stacktrace'] = String(err.stack);\r\n if (err.type) logAttributes['exception.type'] = String(err.type);\r\n }\r\n \r\n // Map Beamable log level to OpenTelemetry SeverityText\r\n // Beamable: Debug, Info, Warning, Error, Fatal\r\n // OpenTelemetry: Trace, Debug, Info, Warn, Error, Fatal, Unspecified\r\n const severityTextMap: Record<string, string> = {\r\n 'Debug': 'Debug',\r\n 'Info': 'Information',\r\n 'Warning': 'Warning',\r\n 'Error': 'Error',\r\n 'Fatal': 'Critical',\r\n };\r\n const severityText = severityTextMap[level] || 'Information';\r\n \r\n // CRITICAL: ResourceAttributes and LogAttributes MUST always be present (even if empty)\r\n // ClickHouse schema expects these fields to exist for proper querying\r\n // The Portal queries ResourceAttributes['service.namespace'] - if ResourceAttributes is missing, queries fail\r\n \r\n // Ensure ResourceAttributes always has service.namespace when serviceName is provided\r\n // This is the primary field used by Portal for service filtering\r\n if (serviceName && !resourceAttributes['service.namespace']) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n \r\n // Add OpenTelemetry fields to the log\r\n // These are in addition to the Beamable format, so both systems can parse the logs\r\n // CRITICAL: Always include all required OpenTelemetry fields for ClickHouse compatibility\r\n otelFields['Timestamp'] = timestamp; // OpenTelemetry timestamp format (ISO 8601)\r\n otelFields['SeverityText'] = severityText;\r\n otelFields['Body'] = messageParts.length > 0 ? messageParts.join(' ') : 'No message';\r\n // ALWAYS include ResourceAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['ResourceAttributes'] = resourceAttributes;\r\n // ALWAYS include LogAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['LogAttributes'] = logAttributes;\r\n \r\n // IMPORTANT: For CloudWatch Logs Insights, we need to ensure @message contains valid JSON\r\n // The Portal expects @message to be a JSON string that can be parsed to extract __t, __l, __m\r\n // We output the Beamable format as the primary format, and include OpenTelemetry fields\r\n // CloudWatch will store this as @message, and an OpenTelemetry collector will parse it\r\n // and forward to ClickHouse's otel_logs table\r\n \r\n // Merge OpenTelemetry fields into the log (for ClickHouse compatibility)\r\n // An OpenTelemetry collector can parse these fields and forward to ClickHouse\r\n // Note: These extra fields won't break CloudWatch - it will just store them in @message\r\n Object.assign(beamableLog, otelFields);\r\n \r\n // Send log via OTLP if configured (similar to C# MicroserviceOtelLogRecordExporter)\r\n // Check if provider is available (may be null if still initializing)\r\n if (otlpProviderRef?.provider) {\r\n try {\r\n const otlpLogger = otlpProviderRef.provider.getLogger(\r\n serviceName || 'beamable-node-runtime',\r\n undefined, // version\r\n {\r\n schemaUrl: undefined, // optional schema URL\r\n }\r\n );\r\n \r\n // Map Beamable level to OpenTelemetry SeverityNumber\r\n const severityNumberMap: Record<string, number> = {\r\n 'Debug': 5, // SEVERITY_NUMBER_DEBUG\r\n 'Info': 9, // SEVERITY_NUMBER_INFO\r\n 'Warning': 13, // SEVERITY_NUMBER_WARN\r\n 'Error': 17, // SEVERITY_NUMBER_ERROR\r\n 'Fatal': 21, // SEVERITY_NUMBER_FATAL\r\n };\r\n \r\n // Create log record for OTLP\r\n otlpLogger.emit({\r\n severityNumber: severityNumberMap[level] || 9,\r\n severityText: severityText,\r\n body: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n attributes: {\r\n ...logAttributes,\r\n // Include additional context\r\n ...(pinoLog.component ? { component: String(pinoLog.component) } : {}),\r\n },\r\n timestamp: new Date(timestamp).getTime() * 1_000_000, // nanoseconds\r\n observedTimestamp: Date.now() * 1_000_000, // nanoseconds\r\n });\r\n } catch (otlpError) {\r\n // If OTLP send fails, continue with stdout logging\r\n // Don't block the log output\r\n // Note: C# supports retry via BEAM_DISABLE_RETRY_OTEL and BEAM_OTEL_RETRY_MAX_SIZE\r\n // SimpleLogRecordProcessor doesn't support retry - would need BatchLogRecordProcessor\r\n // For now, we silently continue (retry would be handled at processor level if implemented)\r\n }\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n // CloudWatch Logs Insights will store this entire JSON string in the @message field\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Synchronously initializes OTLP logging with a timeout.\r\n * This ensures OTLP is ready before any logs are emitted.\r\n * Similar to C# microservices which configure logging early in startup.\r\n * \r\n * Uses a blocking wait mechanism to ensure initialization completes before returning.\r\n */\r\nfunction initializeOtlpSync(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig,\r\n timeoutMs: number = 60000 // 60 seconds to allow collector download, startup, and readiness check\r\n): LoggerProvider | null {\r\n // Match C# logic: (this.InDocker() || UseLocalOtel) && !BEAM_DISABLE_STANDARD_OTEL\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const useLocalOtel = !!process.env.BEAM_LOCAL_OTEL;\r\n const standardOtelEnabled = (isInDocker || useLocalOtel) && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n const hasExplicitEndpoint = !!process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT;\r\n \r\n // If OTLP is not needed, return immediately\r\n if (!standardOtelEnabled && !hasExplicitEndpoint) {\r\n return null;\r\n }\r\n \r\n // Create a minimal console logger for initialization messages\r\n const initLogger = pino({\r\n name: 'beamable-otlp-init',\r\n level: 'info',\r\n }, process.stdout);\r\n \r\n // Use deasync to wait synchronously for the async initialization\r\n // This allows the event loop to process while we wait, enabling async operations\r\n // (like collector download) to complete\r\n let provider: LoggerProvider | null = null;\r\n let completed = false;\r\n let initError: Error | null = null;\r\n \r\n // Start initialization promise (callbacks set flags for deasync.loopWhile to check)\r\n initializeOtlpLogging(\r\n serviceName,\r\n qualifiedServiceName,\r\n env,\r\n initLogger\r\n ).then((result) => {\r\n provider = result;\r\n completed = true;\r\n if (result) {\r\n initLogger.info('[OTLP] OpenTelemetry logging initialized successfully');\r\n }\r\n return result;\r\n }).catch((error) => {\r\n initError = error instanceof Error ? error : new Error(String(error));\r\n completed = true;\r\n initLogger.error(`[OTLP] Failed to initialize: ${initError.message}`);\r\n return null;\r\n });\r\n \r\n // Set timeout to prevent infinite wait\r\n const startTime = Date.now();\r\n const timeoutId = setTimeout(() => {\r\n if (!completed) {\r\n initLogger.warn(`[OTLP] Initialization timeout after ${timeoutMs}ms, continuing without OTLP`);\r\n completed = true;\r\n }\r\n }, timeoutMs);\r\n \r\n // Wait synchronously for the promise to resolve using deasync\r\n // deasync.loopWhile() allows event loop to process while we wait\r\n // This enables async operations (like collector download) to run and complete\r\n // CRITICAL: This must complete before the logger is created to capture all startup logs\r\n try {\r\n // Use deasync to wait for completion, with timeout check\r\n // loopWhile returns false to stop waiting, true to continue\r\n deasync.loopWhile(() => {\r\n // Check if we've exceeded timeout\r\n const elapsed = Date.now() - startTime;\r\n if (elapsed >= timeoutMs) {\r\n initLogger.warn(`[OTLP] Synchronous wait timeout after ${elapsed}ms`);\r\n return false; // Stop waiting\r\n }\r\n // Continue waiting if not completed\r\n // This allows the event loop to process async operations\r\n return !completed;\r\n });\r\n } catch (error) {\r\n // If deasync fails, log and continue\r\n initLogger.error(`[OTLP] Error during synchronous wait: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n \r\n clearTimeout(timeoutId);\r\n \r\n // Verify we got a provider (collector is ready)\r\n if (completed && provider) {\r\n initLogger.info('[OTLP] Initialization completed successfully, collector is ready');\r\n } else if (!completed) {\r\n initLogger.warn('[OTLP] Initialization did not complete in time, logs may not be sent via OTLP initially');\r\n initLogger.warn('[OTLP] Service will continue without OTLP telemetry - requests will still be serviced');\r\n } else {\r\n initLogger.warn('[OTLP] Initialization completed but no provider returned, OTLP telemetry disabled');\r\n }\r\n \r\n return provider;\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const usePrettyLogs = shouldUsePrettyLogs();\r\n \r\n // Initialize OTLP synchronously BEFORE creating the logger\r\n // This ensures all logs from this point forward are captured via OTLP\r\n // Similar to C# microservices which configure logging early in startup\r\n // CRITICAL: We wait for collector to be fully initialized and ready before proceeding\r\n const otlpProvider = initializeOtlpSync(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n env,\r\n 60000 // 60 second timeout to allow collector download, startup, and readiness verification\r\n );\r\n \r\n // Shared reference for OTLP logger provider\r\n const otlpProviderRef: { provider: LoggerProvider | null } = { provider: otlpProvider };\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n // Include service name in base fields for filtering\r\n serviceName: options.serviceName,\r\n qualifiedServiceName: options.qualifiedServiceName,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (!usePrettyLogs) {\r\n // Deployed/remote: Use Beamable JSON format for log collection\r\n // Include OpenTelemetry fields for ClickHouse compatibility\r\n // Also send logs via OTLP if configured\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n // Use getRequire() which handles both CJS and ESM contexts\r\n const requireFn = getRequire();\r\n const pinoPretty = requireFn('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n // This is expected if pino-pretty isn't installed, so we silently fall back\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format if not local, default Pino format if local\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (!usePrettyLogs) {\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
package/src/collector-manager.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { pipeline } from 'stream/promises';
|
|
|
5
5
|
import { createGunzip } from 'zlib';
|
|
6
6
|
import type { Logger } from 'pino';
|
|
7
7
|
import dgram from 'dgram';
|
|
8
|
+
import type { EnvironmentConfig } from './types.js';
|
|
9
|
+
import { hostToHttpUrl } from './utils/urls.js';
|
|
8
10
|
|
|
9
11
|
// Protocol is httpprotobuf
|
|
10
12
|
|
|
@@ -15,7 +17,7 @@ interface CollectorDiscoveryEntry {
|
|
|
15
17
|
otlpEndpoint: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
interface CollectorStatus {
|
|
20
|
+
export interface CollectorStatus {
|
|
19
21
|
isRunning: boolean;
|
|
20
22
|
isReady: boolean;
|
|
21
23
|
pid: number;
|
|
@@ -29,6 +31,62 @@ const DISCOVERY_PORT = parseInt(process.env.BEAM_COLLECTOR_DISCOVERY_PORT || '86
|
|
|
29
31
|
const DISCOVERY_DELAY = 100; // milliseconds
|
|
30
32
|
const DISCOVERY_ATTEMPTS = 10;
|
|
31
33
|
|
|
34
|
+
/**
|
|
35
|
+
* ClickHouse credentials response from Beamable API
|
|
36
|
+
*/
|
|
37
|
+
interface ClickHouseCredentials {
|
|
38
|
+
endpoint: string;
|
|
39
|
+
expiresAt?: string;
|
|
40
|
+
password: string;
|
|
41
|
+
username: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Fetches ClickHouse credentials from Beamable API
|
|
46
|
+
* GET /api/beamo/otel/auth/writer/config
|
|
47
|
+
*/
|
|
48
|
+
export async function fetchClickHouseCredentials(
|
|
49
|
+
env: EnvironmentConfig,
|
|
50
|
+
logger: Logger
|
|
51
|
+
): Promise<ClickHouseCredentials> {
|
|
52
|
+
const apiUrl = hostToHttpUrl(env.host);
|
|
53
|
+
const configUrl = new URL('/api/beamo/otel/auth/writer/config', apiUrl).toString();
|
|
54
|
+
|
|
55
|
+
logger.info('[Collector] Fetching ClickHouse credentials from Beamable API...');
|
|
56
|
+
|
|
57
|
+
// Build headers with authentication
|
|
58
|
+
const headers: Record<string, string> = {
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
Accept: 'application/json',
|
|
61
|
+
'beam-scope': `${env.cid}.${env.pid}`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// If we have a secret, we might need to sign the request
|
|
65
|
+
// For now, try without signature (the API might use the beam-scope header for auth)
|
|
66
|
+
const response = await fetch(configUrl, {
|
|
67
|
+
method: 'GET',
|
|
68
|
+
headers,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
73
|
+
throw new Error(`Failed to fetch ClickHouse credentials: ${response.status} ${response.statusText} - ${errorText}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const credentials = await response.json() as ClickHouseCredentials;
|
|
77
|
+
|
|
78
|
+
if (!credentials.endpoint || !credentials.username || !credentials.password) {
|
|
79
|
+
throw new Error('Invalid ClickHouse credentials response: missing required fields');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
logger.info('[Collector] Successfully fetched ClickHouse credentials');
|
|
83
|
+
if (credentials.expiresAt) {
|
|
84
|
+
logger.debug(`[Collector] Credentials expire at: ${credentials.expiresAt}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return credentials;
|
|
88
|
+
}
|
|
89
|
+
|
|
32
90
|
/**
|
|
33
91
|
* Gets the collector storage directory (similar to C# LocalApplicationData/beam/collectors/version)
|
|
34
92
|
*/
|
|
@@ -170,6 +228,36 @@ function discoverCollectorViaUDP(timeoutMs: number = 5000): Promise<CollectorDis
|
|
|
170
228
|
});
|
|
171
229
|
}
|
|
172
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Gets the current status of ClickHouse credentials (from env or API)
|
|
233
|
+
*/
|
|
234
|
+
export function getClickHouseCredentialsStatus(): {
|
|
235
|
+
hasEndpoint: boolean;
|
|
236
|
+
hasUsername: boolean;
|
|
237
|
+
hasPassword: boolean;
|
|
238
|
+
source: 'environment' | 'api' | 'missing';
|
|
239
|
+
} {
|
|
240
|
+
const hasEndpoint = !!process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
241
|
+
const hasUsername = !!process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
242
|
+
const hasPassword = !!process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
243
|
+
|
|
244
|
+
if (hasEndpoint && hasUsername && hasPassword) {
|
|
245
|
+
return {
|
|
246
|
+
hasEndpoint: true,
|
|
247
|
+
hasUsername: true,
|
|
248
|
+
hasPassword: true,
|
|
249
|
+
source: 'environment',
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
hasEndpoint,
|
|
255
|
+
hasUsername,
|
|
256
|
+
hasPassword,
|
|
257
|
+
source: 'missing',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
173
261
|
/**
|
|
174
262
|
* Checks if collector is already running via UDP discovery
|
|
175
263
|
*/
|
|
@@ -201,7 +289,8 @@ export async function isCollectorRunning(): Promise<CollectorStatus> {
|
|
|
201
289
|
*/
|
|
202
290
|
export async function startCollector(
|
|
203
291
|
logger: Logger,
|
|
204
|
-
otlpEndpoint?: string
|
|
292
|
+
otlpEndpoint?: string,
|
|
293
|
+
env?: EnvironmentConfig
|
|
205
294
|
): Promise<{ process: ChildProcess; endpoint: string }> {
|
|
206
295
|
logger.info('[Collector] Resolving collector binary and config...');
|
|
207
296
|
const collectorInfo = await resolveCollector(true, logger);
|
|
@@ -219,20 +308,34 @@ export async function startCollector(
|
|
|
219
308
|
logger.info(`[Collector] Using binary: ${collectorInfo.binaryPath}`);
|
|
220
309
|
logger.info(`[Collector] Using config: ${collectorInfo.configPath}`);
|
|
221
310
|
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
BEAM_CLICKHOUSE_PASSWORD: process.env.BEAM_CLICKHOUSE_PASSWORD,
|
|
228
|
-
};
|
|
311
|
+
// Fetch ClickHouse credentials from Beamable API if not in environment variables
|
|
312
|
+
// Per Gabriel: "these OTEL related ones you need to do it yourself at the beginning of the microservice startup"
|
|
313
|
+
let clickhouseEndpoint = process.env.BEAM_CLICKHOUSE_ENDPOINT;
|
|
314
|
+
let clickhouseUsername = process.env.BEAM_CLICKHOUSE_USERNAME;
|
|
315
|
+
let clickhousePassword = process.env.BEAM_CLICKHOUSE_PASSWORD;
|
|
229
316
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
317
|
+
if ((!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) && env) {
|
|
318
|
+
try {
|
|
319
|
+
const credentials = await fetchClickHouseCredentials(env, logger);
|
|
320
|
+
clickhouseEndpoint = credentials.endpoint;
|
|
321
|
+
clickhouseUsername = credentials.username;
|
|
322
|
+
clickhousePassword = credentials.password;
|
|
323
|
+
|
|
324
|
+
// Set them in process.env so the collector can access them
|
|
325
|
+
process.env.BEAM_CLICKHOUSE_ENDPOINT = clickhouseEndpoint;
|
|
326
|
+
process.env.BEAM_CLICKHOUSE_USERNAME = clickhouseUsername;
|
|
327
|
+
process.env.BEAM_CLICKHOUSE_PASSWORD = clickhousePassword;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
const errorMsg = `[Collector] Failed to fetch ClickHouse credentials from API: ${error instanceof Error ? error.message : String(error)}`;
|
|
330
|
+
logger.error(errorMsg);
|
|
331
|
+
throw new Error(errorMsg);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
233
334
|
|
|
234
|
-
|
|
235
|
-
|
|
335
|
+
// Validate required environment variables (matching C# behavior)
|
|
336
|
+
// These must be set before starting the collector, otherwise it will fail
|
|
337
|
+
if (!clickhouseEndpoint || !clickhouseUsername || !clickhousePassword) {
|
|
338
|
+
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.`;
|
|
236
339
|
logger.error(errorMsg);
|
|
237
340
|
throw new Error(errorMsg);
|
|
238
341
|
}
|
|
@@ -250,12 +353,12 @@ export async function startCollector(
|
|
|
250
353
|
// Set environment variables for collector
|
|
251
354
|
// Note: BEAM_CLICKHOUSE_ENDPOINT is for collector → ClickHouse communication
|
|
252
355
|
// This is different from the OTLP endpoint (microservice → collector)
|
|
253
|
-
const
|
|
356
|
+
const collectorEnv = {
|
|
254
357
|
...process.env,
|
|
255
358
|
BEAM_OTLP_HTTP_ENDPOINT: localEndpoint,
|
|
256
|
-
BEAM_CLICKHOUSE_ENDPOINT:
|
|
257
|
-
BEAM_CLICKHOUSE_USERNAME:
|
|
258
|
-
BEAM_CLICKHOUSE_PASSWORD:
|
|
359
|
+
BEAM_CLICKHOUSE_ENDPOINT: clickhouseEndpoint!,
|
|
360
|
+
BEAM_CLICKHOUSE_USERNAME: clickhouseUsername!,
|
|
361
|
+
BEAM_CLICKHOUSE_PASSWORD: clickhousePassword!,
|
|
259
362
|
BEAM_COLLECTOR_DISCOVERY_PORT: String(DISCOVERY_PORT),
|
|
260
363
|
BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT: process.env.BEAM_CLICKHOUSE_PROCESSOR_TIMEOUT || '5s',
|
|
261
364
|
BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE: process.env.BEAM_CLICKHOUSE_PROCESSOR_BATCH_SIZE || '5000',
|
|
@@ -269,7 +372,7 @@ export async function startCollector(
|
|
|
269
372
|
|
|
270
373
|
// Start collector process
|
|
271
374
|
const collectorProcess = spawn(collectorInfo.binaryPath, ['--config', collectorInfo.configPath], {
|
|
272
|
-
env,
|
|
375
|
+
env: collectorEnv,
|
|
273
376
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
274
377
|
detached: false,
|
|
275
378
|
});
|
|
@@ -303,7 +406,8 @@ export async function startCollector(
|
|
|
303
406
|
*/
|
|
304
407
|
export async function discoverOrStartCollector(
|
|
305
408
|
logger: Logger,
|
|
306
|
-
standardOtelEnabled: boolean
|
|
409
|
+
standardOtelEnabled: boolean,
|
|
410
|
+
env?: EnvironmentConfig
|
|
307
411
|
): Promise<string | null> {
|
|
308
412
|
if (!standardOtelEnabled) {
|
|
309
413
|
return null;
|
|
@@ -319,7 +423,7 @@ export async function discoverOrStartCollector(
|
|
|
319
423
|
// Collector not running - start it
|
|
320
424
|
try {
|
|
321
425
|
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
322
|
-
const { endpoint } = await startCollector(logger);
|
|
426
|
+
const { endpoint } = await startCollector(logger, undefined, env);
|
|
323
427
|
|
|
324
428
|
// Wait a bit for collector to start and become ready
|
|
325
429
|
// Try to discover it
|
package/src/index.ts
CHANGED
package/src/logger.ts
CHANGED
|
@@ -135,7 +135,7 @@ async function initializeOtlpLogging(
|
|
|
135
135
|
if (!endpointUrl && standardOtelEnabled && logger) {
|
|
136
136
|
// Try to discover existing collector or start a new one (like C# does)
|
|
137
137
|
try {
|
|
138
|
-
const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled);
|
|
138
|
+
const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled, env);
|
|
139
139
|
if (discoveredEndpoint) {
|
|
140
140
|
endpointUrl = discoveredEndpoint;
|
|
141
141
|
} else {
|