@zintrust/trace 1.5.2 → 1.6.1

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/src/register.ts CHANGED
@@ -199,344 +199,461 @@ const buildTraceRedactionOverrides = (input: {
199
199
  : undefined;
200
200
  };
201
201
 
202
- const core = (await importCore()) as CoreApi;
203
- const Env = core.Env;
204
- const startupOverrides = await resolveTraceStartupOverrides(core);
202
+ type TraceEnvApi = NonNullable<CoreApi['Env']>;
203
+
204
+ type TraceEnvValues = {
205
+ connectionRaw: string;
206
+ observeConnectionRaw: string;
207
+ pruneAfterHoursRaw: string;
208
+ slowQueryThresholdRaw: string;
209
+ logMinLevelRaw: string;
210
+ traceProxyRaw: string;
211
+ traceProxyUrlRaw: string;
212
+ traceProxyPathRaw: string;
213
+ traceProxyKeyIdRaw: string;
214
+ traceProxySecretRaw: string;
215
+ traceProxyTimeoutRaw: string;
216
+ traceServiceTagRaw: string;
217
+ appNameRaw: string;
218
+ appKeyRaw: string;
219
+ captureCachePayloadsRaw: string;
220
+ captureQueryBindingsRaw: string;
221
+ contentDispatchDriverRaw: string;
222
+ contentDispatchQueueRaw: string;
223
+ contentDispatchEnqueueTimeoutRaw: string;
224
+ contentDispatchWorkerEnabledRaw: string;
225
+ contentDispatchWorkerIntervalRaw: string;
226
+ contentDispatchWorkerDurationRaw: string;
227
+ contentDispatchWorkerConcurrencyRaw: string;
228
+ redactionKeys?: string[];
229
+ redactionHeaders?: string[];
230
+ redactionBody?: string[];
231
+ redactionQuery?: string[];
232
+ };
205
233
 
206
- if (!traceAlreadyInitialized && Env) {
207
- const enabled = startupOverrides?.enabled === true || Env.getBool('TRACE_ENABLED', false);
234
+ const readTraceEnvValues = (Env: TraceEnvApi): TraceEnvValues => {
235
+ return {
236
+ connectionRaw: Env.get('TRACE_DB_CONNECTION', '').trim(),
237
+ observeConnectionRaw: Env.get('TRACE_QUERY_CONNECTION', '').trim(),
238
+ pruneAfterHoursRaw: Env.get('TRACE_PRUNE_HOURS', '').trim(),
239
+ slowQueryThresholdRaw: Env.get('TRACE_SLOW_QUERY_MS', '').trim(),
240
+ logMinLevelRaw: Env.get('TRACE_LOG_LEVEL', '').trim(),
241
+ traceProxyRaw: Env.get('TRACE_PROXY', '').trim(),
242
+ traceProxyUrlRaw: Env.get('TRACE_PROXY_URL', '').trim(),
243
+ traceProxyPathRaw: Env.get('TRACE_PROXY_PATH', '').trim(),
244
+ traceProxyKeyIdRaw: Env.get('TRACE_PROXY_KEY_ID', '').trim(),
245
+ traceProxySecretRaw: Env.get('TRACE_PROXY_SECRET', '').trim(),
246
+ traceProxyTimeoutRaw: Env.get('TRACE_PROXY_TIMEOUT_MS', '').trim(),
247
+ traceServiceTagRaw: Env.get('TRACE_SERVICE_TAG', '').trim(),
248
+ appNameRaw: Env.get('APP_NAME', '').trim(),
249
+ appKeyRaw: Env.get('APP_KEY', '').trim(),
250
+ captureCachePayloadsRaw: Env.get('TRACE_CACHE_PAYLOADS', '').trim(),
251
+ captureQueryBindingsRaw: Env.get('TRACE_QUERY_BINDINGS', '').trim(),
252
+ contentDispatchDriverRaw: Env.get('TRACE_CONTENT_QUEUE_DRIVER', '').trim(),
253
+ contentDispatchQueueRaw: Env.get('TRACE_CONTENT_QUEUE_NAME', '').trim(),
254
+ contentDispatchEnqueueTimeoutRaw: Env.get('TRACE_CONTENT_QUEUE_ENQUEUE_TIMEOUT_MS', '').trim(),
255
+ contentDispatchWorkerEnabledRaw: Env.get('TRACE_CONTENT_QUEUE_WORKER_ENABLED', '').trim(),
256
+ contentDispatchWorkerIntervalRaw: Env.get('TRACE_CONTENT_QUEUE_WORKER_INTERVAL_MS', '').trim(),
257
+ contentDispatchWorkerDurationRaw: Env.get('TRACE_CONTENT_QUEUE_WORKER_MAX_DURATION_MS', '').trim(),
258
+ contentDispatchWorkerConcurrencyRaw: Env.get('TRACE_CONTENT_QUEUE_WORKER_CONCURRENCY', '').trim(),
259
+ redactionKeys: parseEnvList(Env.get('TRACE_REDACT_KEYS', '')),
260
+ redactionHeaders: parseEnvList(Env.get('TRACE_REDACT_HEADERS', '')),
261
+ redactionBody: parseEnvList(Env.get('TRACE_REDACT_BODY', '')),
262
+ redactionQuery: parseEnvList(Env.get('TRACE_REDACT_QUERY', '')),
263
+ };
264
+ };
208
265
 
209
- if (enabled) {
210
- const connectionRaw = Env.get('TRACE_DB_CONNECTION', '').trim();
211
- const observeConnectionRaw = Env.get('TRACE_QUERY_CONNECTION', '').trim();
212
- const pruneAfterHoursRaw = Env.get('TRACE_PRUNE_HOURS', '').trim();
213
- const slowQueryThresholdRaw = Env.get('TRACE_SLOW_QUERY_MS', '').trim();
214
- const logMinLevelRaw = Env.get('TRACE_LOG_LEVEL', '').trim();
215
- const traceProxyRaw = Env.get('TRACE_PROXY', '').trim();
216
- const traceProxyUrlRaw = Env.get('TRACE_PROXY_URL', '').trim();
217
- const traceProxyPathRaw = Env.get('TRACE_PROXY_PATH', '').trim();
218
- const traceProxyKeyIdRaw = Env.get('TRACE_PROXY_KEY_ID', '').trim();
219
- const traceProxySecretRaw = Env.get('TRACE_PROXY_SECRET', '').trim();
220
- const traceProxyTimeoutRaw = Env.get('TRACE_PROXY_TIMEOUT_MS', '').trim();
221
- const traceServiceTagRaw = Env.get('TRACE_SERVICE_TAG', '').trim();
222
- const appNameRaw = Env.get('APP_NAME', '').trim();
223
- const appKeyRaw = Env.get('APP_KEY', '').trim();
224
- const captureCachePayloadsRaw = Env.get('TRACE_CACHE_PAYLOADS', '').trim();
225
- const captureQueryBindingsRaw = Env.get('TRACE_QUERY_BINDINGS', '').trim();
226
- const contentDispatchDriverRaw = Env.get('TRACE_CONTENT_QUEUE_DRIVER', '').trim();
227
- const contentDispatchQueueRaw = Env.get('TRACE_CONTENT_QUEUE_NAME', '').trim();
228
- const contentDispatchEnqueueTimeoutRaw = Env.get(
229
- 'TRACE_CONTENT_QUEUE_ENQUEUE_TIMEOUT_MS',
230
- ''
231
- ).trim();
232
- const contentDispatchWorkerEnabledRaw = Env.get(
233
- 'TRACE_CONTENT_QUEUE_WORKER_ENABLED',
234
- ''
235
- ).trim();
236
- const contentDispatchWorkerIntervalRaw = Env.get(
237
- 'TRACE_CONTENT_QUEUE_WORKER_INTERVAL_MS',
238
- ''
239
- ).trim();
240
- const contentDispatchWorkerDurationRaw = Env.get(
241
- 'TRACE_CONTENT_QUEUE_WORKER_MAX_DURATION_MS',
242
- ''
243
- ).trim();
244
- const contentDispatchWorkerConcurrencyRaw = Env.get(
245
- 'TRACE_CONTENT_QUEUE_WORKER_CONCURRENCY',
246
- ''
247
- ).trim();
248
- const redactionKeys = parseEnvList(Env.get('TRACE_REDACT_KEYS', ''));
249
- const redactionHeaders = parseEnvList(Env.get('TRACE_REDACT_HEADERS', ''));
250
- const redactionBody = parseEnvList(Env.get('TRACE_REDACT_BODY', ''));
251
- const redactionQuery = parseEnvList(Env.get('TRACE_REDACT_QUERY', ''));
252
-
253
- const connection = connectionRaw === '' ? startupOverrides?.connection : connectionRaw;
254
- const observeConnection =
255
- observeConnectionRaw === '' ? startupOverrides?.observeConnection : observeConnectionRaw;
256
- const pruneAfterHours =
257
- pruneAfterHoursRaw === ''
258
- ? startupOverrides?.pruneAfterHours
259
- : Number.parseInt(pruneAfterHoursRaw, 10);
260
- const slowQueryThreshold =
261
- slowQueryThresholdRaw === ''
262
- ? startupOverrides?.slowQueryThreshold
263
- : Number.parseInt(slowQueryThresholdRaw, 10);
264
- const logMinLevel = (logMinLevelRaw === '' ? startupOverrides?.logMinLevel : logMinLevelRaw) as
265
- | 'debug'
266
- | 'info'
267
- | 'warn'
268
- | 'error'
269
- | 'fatal';
270
- const captureCachePayloads =
271
- parseEnvBool(captureCachePayloadsRaw) ?? startupOverrides?.captureCachePayloads;
272
- const captureQueryBindings =
273
- parseEnvBool(captureQueryBindingsRaw) ?? startupOverrides?.captureQueryBindings;
274
- const traceProxyEnabled = parseEnvBool(traceProxyRaw) ?? startupOverrides?.proxy?.enabled;
275
- const traceProxyUrl = traceProxyUrlRaw === '' ? startupOverrides?.proxy?.url : traceProxyUrlRaw;
276
- const traceProxyPath =
277
- traceProxyPathRaw === '' ? startupOverrides?.proxy?.path : traceProxyPathRaw;
278
- const traceProxyKeyId =
279
- traceProxyKeyIdRaw === ''
280
- ? (startupOverrides?.proxy?.keyId ?? appNameRaw)
281
- : traceProxyKeyIdRaw;
282
- const traceProxySecret =
283
- traceProxySecretRaw === ''
284
- ? (startupOverrides?.proxy?.secret ?? appKeyRaw)
285
- : traceProxySecretRaw;
286
- const traceProxyTimeout =
287
- traceProxyTimeoutRaw === ''
288
- ? startupOverrides?.proxy?.timeoutMs
289
- : Number.parseInt(traceProxyTimeoutRaw, 10);
290
- const traceServiceTag =
291
- traceServiceTagRaw === ''
292
- ? (startupOverrides?.serviceTag ?? appNameRaw).trim() || undefined
293
- : traceServiceTagRaw;
294
- const contentDispatchDriver =
295
- contentDispatchDriverRaw === ''
296
- ? startupOverrides?.contentDispatch?.driver
297
- : contentDispatchDriverRaw;
298
- const contentDispatchQueueName =
299
- contentDispatchQueueRaw === ''
300
- ? startupOverrides?.contentDispatch?.queueName
301
- : contentDispatchQueueRaw;
302
- const contentDispatchEnqueueTimeout =
303
- contentDispatchEnqueueTimeoutRaw === ''
304
- ? startupOverrides?.contentDispatch?.enqueueTimeoutMs
305
- : Number.parseInt(contentDispatchEnqueueTimeoutRaw, 10);
306
- const contentDispatchWorkerEnabled =
307
- parseEnvBool(contentDispatchWorkerEnabledRaw) ??
308
- startupOverrides?.contentDispatch?.worker?.enabled;
309
- const contentDispatchWorkerInterval =
310
- contentDispatchWorkerIntervalRaw === ''
311
- ? startupOverrides?.contentDispatch?.worker?.intervalMs
312
- : Number.parseInt(contentDispatchWorkerIntervalRaw, 10);
313
- const contentDispatchWorkerDuration =
314
- contentDispatchWorkerDurationRaw === ''
315
- ? startupOverrides?.contentDispatch?.worker?.maxDurationMs
316
- : Number.parseInt(contentDispatchWorkerDurationRaw, 10);
317
- const contentDispatchWorkerConcurrency =
318
- contentDispatchWorkerConcurrencyRaw === ''
319
- ? startupOverrides?.contentDispatch?.worker?.concurrency
320
- : Number.parseInt(contentDispatchWorkerConcurrencyRaw, 10);
321
- const redaction = buildTraceRedactionOverrides({
322
- startupOverrides,
323
- redactionBody,
324
- redactionHeaders,
325
- redactionKeys,
326
- redactionQuery,
327
- });
328
- const defaultContentDispatch = TraceConfig.defaults().contentDispatch;
329
- const startupContentDispatch = startupOverrides?.contentDispatch;
330
- const startupContentDispatchWorker = startupContentDispatch?.worker;
331
-
332
- const config = TraceConfig.merge({
333
- ...startupOverrides,
334
- enabled,
335
- connection,
336
- observeConnection,
337
- ...(typeof traceServiceTag === 'string' && traceServiceTag !== ''
338
- ? { serviceTag: traceServiceTag }
339
- : {}),
340
- proxy: {
341
- ...TraceConfig.defaults().proxy,
342
- ...startupOverrides?.proxy,
343
- ...(typeof traceProxyEnabled === 'boolean' ? { enabled: traceProxyEnabled } : {}),
344
- ...(typeof traceProxyUrl === 'string' && traceProxyUrl !== ''
345
- ? { url: traceProxyUrl }
346
- : {}),
347
- ...(typeof traceProxyPath === 'string' && traceProxyPath !== ''
348
- ? { path: traceProxyPath }
349
- : {}),
350
- ...(typeof traceProxyKeyId === 'string' && traceProxyKeyId !== ''
351
- ? { keyId: traceProxyKeyId }
352
- : {}),
353
- ...(typeof traceProxySecret === 'string' && traceProxySecret !== ''
354
- ? { secret: traceProxySecret }
355
- : {}),
356
- ...(typeof traceProxyTimeout === 'number' && Number.isFinite(traceProxyTimeout)
357
- ? { timeoutMs: traceProxyTimeout }
358
- : {}),
359
- },
360
- ...(typeof pruneAfterHours === 'number' && Number.isFinite(pruneAfterHours)
361
- ? { pruneAfterHours }
362
- : {}),
363
- ...(typeof slowQueryThreshold === 'number' && Number.isFinite(slowQueryThreshold)
364
- ? { slowQueryThreshold }
365
- : {}),
366
- ...(typeof captureCachePayloads === 'boolean' ? { captureCachePayloads } : {}),
367
- ...(typeof captureQueryBindings === 'boolean' ? { captureQueryBindings } : {}),
368
- contentDispatch: {
369
- ...defaultContentDispatch,
370
- ...startupContentDispatch,
371
- ...(typeof contentDispatchDriver === 'string' && contentDispatchDriver !== ''
372
- ? { driver: contentDispatchDriver }
373
- : {}),
374
- ...(typeof contentDispatchQueueName === 'string' && contentDispatchQueueName !== ''
375
- ? { queueName: contentDispatchQueueName }
376
- : {}),
377
- ...(typeof contentDispatchEnqueueTimeout === 'number' &&
378
- Number.isFinite(contentDispatchEnqueueTimeout)
379
- ? { enqueueTimeoutMs: contentDispatchEnqueueTimeout }
380
- : {}),
381
- worker: {
382
- ...defaultContentDispatch.worker,
383
- ...startupContentDispatchWorker,
384
- enabled:
385
- typeof contentDispatchWorkerEnabled === 'boolean'
386
- ? contentDispatchWorkerEnabled
387
- : (startupContentDispatchWorker?.enabled ?? defaultContentDispatch.worker.enabled),
388
- intervalMs:
389
- typeof contentDispatchWorkerInterval === 'number' &&
390
- Number.isFinite(contentDispatchWorkerInterval)
391
- ? contentDispatchWorkerInterval
392
- : (startupContentDispatchWorker?.intervalMs ??
393
- defaultContentDispatch.worker.intervalMs),
394
- maxDurationMs:
395
- typeof contentDispatchWorkerDuration === 'number' &&
396
- Number.isFinite(contentDispatchWorkerDuration)
397
- ? contentDispatchWorkerDuration
398
- : (startupContentDispatchWorker?.maxDurationMs ??
399
- defaultContentDispatch.worker.maxDurationMs),
400
- concurrency:
401
- typeof contentDispatchWorkerConcurrency === 'number' &&
402
- Number.isFinite(contentDispatchWorkerConcurrency)
403
- ? contentDispatchWorkerConcurrency
404
- : (startupContentDispatchWorker?.concurrency ??
405
- defaultContentDispatch.worker.concurrency),
406
- },
407
- },
408
- logMinLevel,
409
- ...(redaction === undefined ? {} : { redaction }),
410
- });
411
-
412
- const resolvedConnectionName = resolveTraceConnectionName(Env, config.connection);
413
- const resolvedObservedConnectionName = resolveObservedConnectionName(
414
- Env,
415
- config.observeConnection,
416
- resolvedConnectionName
417
- );
418
- globalTraceRegisterState.__zintrust_system_trace_connection_name__ = resolvedConnectionName;
419
- globalTraceRegisterState.__zintrust_system_trace_observe_connection_name__ =
420
- resolvedObservedConnectionName;
421
- const storageDb = core.useDatabase?.(undefined, resolvedConnectionName);
422
- const observedDb = core.useDatabase?.(undefined, resolvedObservedConnectionName);
266
+ const resolveStringOverride = (rawValue: string, fallback: string | undefined): string | undefined => {
267
+ return rawValue === '' ? fallback : rawValue;
268
+ };
423
269
 
424
- assertTraceConnectionResolved(core, storageDb, {
425
- connectionName: resolvedConnectionName,
426
- envKey: 'TRACE_DB_CONNECTION',
427
- });
428
- assertTraceConnectionResolved(core, observedDb, {
429
- connectionName: resolvedObservedConnectionName,
430
- envKey: 'TRACE_QUERY_CONNECTION',
431
- });
432
- await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
433
-
434
- const resolvedStorage = config.proxy.enabled
435
- ? ProxyTraceStorage.create({
436
- baseUrl: config.proxy.url ?? '',
437
- path: config.proxy.path,
438
- keyId: config.proxy.keyId ?? '',
439
- secret: config.proxy.secret ?? '',
440
- timeoutMs: config.proxy.timeoutMs,
441
- })
442
- : TraceStorage.resolveStorage(storageDb);
443
-
444
- const storage = TraceWriteDiagnostics.wrapStorage(
445
- TraceContentBudget.wrapStorage(
446
- TraceContentRedaction.wrapStorage(
447
- TraceEntryFiltering.wrapStorage(
448
- TraceServiceTag.wrapStorage(resolvedStorage, config),
449
- config
450
- ),
451
- config.redaction
452
- ),
453
- config
270
+ const resolveNumberOverride = (rawValue: string, fallback: number | undefined): number | undefined => {
271
+ return rawValue === '' ? fallback : Number.parseInt(rawValue, 10);
272
+ };
273
+
274
+ const resolveBooleanOverride = (
275
+ rawValue: string,
276
+ fallback: boolean | undefined
277
+ ): boolean | undefined => {
278
+ return parseEnvBool(rawValue) ?? fallback;
279
+ };
280
+
281
+ const resolveTraceProxyKeyId = (
282
+ startupOverrides: TraceConfigOverrides | undefined,
283
+ values: TraceEnvValues
284
+ ): string | undefined => {
285
+ return resolveStringOverride(values.traceProxyKeyIdRaw, startupOverrides?.proxy?.keyId ?? values.appNameRaw);
286
+ };
287
+
288
+ const resolveTraceProxySecret = (
289
+ startupOverrides: TraceConfigOverrides | undefined,
290
+ values: TraceEnvValues
291
+ ): string | undefined => {
292
+ return resolveStringOverride(values.traceProxySecretRaw, startupOverrides?.proxy?.secret ?? values.appKeyRaw);
293
+ };
294
+
295
+ const withStringProperty = (key: string, value: string | undefined): Record<string, string> => {
296
+ return typeof value === 'string' && value !== '' ? { [key]: value } : {};
297
+ };
298
+
299
+ const withNumberProperty = (key: string, value: number | undefined): Record<string, number> => {
300
+ return typeof value === 'number' && Number.isFinite(value) ? { [key]: value } : {};
301
+ };
302
+
303
+ const withBooleanProperty = (
304
+ key: string,
305
+ value: boolean | undefined
306
+ ): Record<string, boolean> => {
307
+ return typeof value === 'boolean' ? { [key]: value } : {};
308
+ };
309
+
310
+ const resolveTraceServiceTag = (
311
+ startupOverrides: TraceConfigOverrides | undefined,
312
+ values: TraceEnvValues
313
+ ): string | undefined => {
314
+ const fallback = (startupOverrides?.serviceTag ?? values.appNameRaw).trim() || undefined;
315
+ return resolveStringOverride(values.traceServiceTagRaw, fallback);
316
+ };
317
+
318
+ const resolveContentDispatchWorkerEnabled = (
319
+ startupOverrides: TraceConfigOverrides | undefined,
320
+ values: TraceEnvValues
321
+ ): boolean | undefined => {
322
+ return resolveBooleanOverride(
323
+ values.contentDispatchWorkerEnabledRaw,
324
+ startupOverrides?.contentDispatch?.worker?.enabled
325
+ );
326
+ };
327
+
328
+ const resolveContentDispatchWorkerInterval = (
329
+ startupOverrides: TraceConfigOverrides | undefined,
330
+ values: TraceEnvValues
331
+ ): number | undefined => {
332
+ return resolveNumberOverride(
333
+ values.contentDispatchWorkerIntervalRaw,
334
+ startupOverrides?.contentDispatch?.worker?.intervalMs
335
+ );
336
+ };
337
+
338
+ const resolveContentDispatchWorkerDuration = (
339
+ startupOverrides: TraceConfigOverrides | undefined,
340
+ values: TraceEnvValues
341
+ ): number | undefined => {
342
+ return resolveNumberOverride(
343
+ values.contentDispatchWorkerDurationRaw,
344
+ startupOverrides?.contentDispatch?.worker?.maxDurationMs
345
+ );
346
+ };
347
+
348
+ const resolveContentDispatchWorkerConcurrency = (
349
+ startupOverrides: TraceConfigOverrides | undefined,
350
+ values: TraceEnvValues
351
+ ): number | undefined => {
352
+ return resolveNumberOverride(
353
+ values.contentDispatchWorkerConcurrencyRaw,
354
+ startupOverrides?.contentDispatch?.worker?.concurrency
355
+ );
356
+ };
357
+
358
+ const buildTraceContentDispatchWorkerConfig = (
359
+ startupOverrides: TraceConfigOverrides | undefined,
360
+ values: TraceEnvValues
361
+ ): NonNullable<NonNullable<TraceConfigOverrides['contentDispatch']>['worker']> => {
362
+ const defaultWorker = TraceConfig.defaults().contentDispatch.worker;
363
+ const startupWorker = startupOverrides?.contentDispatch?.worker;
364
+ const contentDispatchWorkerEnabled = resolveContentDispatchWorkerEnabled(startupOverrides, values);
365
+ const contentDispatchWorkerInterval = resolveContentDispatchWorkerInterval(startupOverrides, values);
366
+ const contentDispatchWorkerDuration = resolveContentDispatchWorkerDuration(startupOverrides, values);
367
+ const contentDispatchWorkerConcurrency = resolveContentDispatchWorkerConcurrency(
368
+ startupOverrides,
369
+ values
370
+ );
371
+
372
+ return {
373
+ ...defaultWorker,
374
+ ...startupWorker,
375
+ enabled: contentDispatchWorkerEnabled ?? startupWorker?.enabled ?? defaultWorker.enabled,
376
+ intervalMs: contentDispatchWorkerInterval ?? startupWorker?.intervalMs ?? defaultWorker.intervalMs,
377
+ maxDurationMs:
378
+ contentDispatchWorkerDuration ?? startupWorker?.maxDurationMs ?? defaultWorker.maxDurationMs,
379
+ concurrency:
380
+ contentDispatchWorkerConcurrency ?? startupWorker?.concurrency ?? defaultWorker.concurrency,
381
+ };
382
+ };
383
+
384
+ const buildTraceProxyConfig = (
385
+ startupOverrides: TraceConfigOverrides | undefined,
386
+ values: TraceEnvValues
387
+ ): TraceConfigOverrides['proxy'] => {
388
+ const traceProxyEnabled = resolveBooleanOverride(values.traceProxyRaw, startupOverrides?.proxy?.enabled);
389
+ const traceProxyUrl = resolveStringOverride(values.traceProxyUrlRaw, startupOverrides?.proxy?.url);
390
+ const traceProxyPath = resolveStringOverride(values.traceProxyPathRaw, startupOverrides?.proxy?.path);
391
+ const traceProxyKeyId = resolveTraceProxyKeyId(startupOverrides, values);
392
+ const traceProxySecret = resolveTraceProxySecret(startupOverrides, values);
393
+ const traceProxyTimeout = resolveNumberOverride(
394
+ values.traceProxyTimeoutRaw,
395
+ startupOverrides?.proxy?.timeoutMs
396
+ );
397
+
398
+ return {
399
+ ...TraceConfig.defaults().proxy,
400
+ ...startupOverrides?.proxy,
401
+ ...withBooleanProperty('enabled', traceProxyEnabled),
402
+ ...withStringProperty('url', traceProxyUrl),
403
+ ...withStringProperty('path', traceProxyPath),
404
+ ...withStringProperty('keyId', traceProxyKeyId),
405
+ ...withStringProperty('secret', traceProxySecret),
406
+ ...withNumberProperty('timeoutMs', traceProxyTimeout),
407
+ };
408
+ };
409
+
410
+ const buildTraceContentDispatchConfig = (
411
+ startupOverrides: TraceConfigOverrides | undefined,
412
+ values: TraceEnvValues
413
+ ): NonNullable<TraceConfigOverrides['contentDispatch']> => {
414
+ const defaultContentDispatch = TraceConfig.defaults().contentDispatch;
415
+ const startupContentDispatch = startupOverrides?.contentDispatch;
416
+ const contentDispatchDriver = resolveStringOverride(
417
+ values.contentDispatchDriverRaw,
418
+ startupContentDispatch?.driver
419
+ );
420
+ const contentDispatchQueueName = resolveStringOverride(
421
+ values.contentDispatchQueueRaw,
422
+ startupContentDispatch?.queueName
423
+ );
424
+ const contentDispatchEnqueueTimeout = resolveNumberOverride(
425
+ values.contentDispatchEnqueueTimeoutRaw,
426
+ startupContentDispatch?.enqueueTimeoutMs
427
+ );
428
+
429
+ return {
430
+ ...defaultContentDispatch,
431
+ ...startupContentDispatch,
432
+ ...withStringProperty('driver', contentDispatchDriver),
433
+ ...withStringProperty('queueName', contentDispatchQueueName),
434
+ ...withNumberProperty('enqueueTimeoutMs', contentDispatchEnqueueTimeout),
435
+ worker: buildTraceContentDispatchWorkerConfig(startupOverrides, values),
436
+ };
437
+ };
438
+
439
+ const buildTraceRuntimeConfig = (
440
+ Env: TraceEnvApi,
441
+ startupOverrides: TraceConfigOverrides | undefined
442
+ ): ReturnType<typeof TraceConfig.merge> => {
443
+ const values = readTraceEnvValues(Env);
444
+ const enabled = startupOverrides?.enabled === true || Env.getBool('TRACE_ENABLED', false);
445
+ const connection = resolveStringOverride(values.connectionRaw, startupOverrides?.connection);
446
+ const observeConnection = resolveStringOverride(
447
+ values.observeConnectionRaw,
448
+ startupOverrides?.observeConnection
449
+ );
450
+ const pruneAfterHours = resolveNumberOverride(
451
+ values.pruneAfterHoursRaw,
452
+ startupOverrides?.pruneAfterHours
453
+ );
454
+ const slowQueryThreshold = resolveNumberOverride(
455
+ values.slowQueryThresholdRaw,
456
+ startupOverrides?.slowQueryThreshold
457
+ );
458
+ const logMinLevel = (
459
+ values.logMinLevelRaw === '' ? startupOverrides?.logMinLevel : values.logMinLevelRaw
460
+ ) as 'debug' | 'info' | 'warn' | 'error' | 'fatal';
461
+ const captureCachePayloads = resolveBooleanOverride(
462
+ values.captureCachePayloadsRaw,
463
+ startupOverrides?.captureCachePayloads
464
+ );
465
+ const captureQueryBindings = resolveBooleanOverride(
466
+ values.captureQueryBindingsRaw,
467
+ startupOverrides?.captureQueryBindings
468
+ );
469
+ const traceServiceTag = resolveTraceServiceTag(startupOverrides, values);
470
+ const redaction = buildTraceRedactionOverrides({
471
+ startupOverrides,
472
+ redactionBody: values.redactionBody,
473
+ redactionHeaders: values.redactionHeaders,
474
+ redactionKeys: values.redactionKeys,
475
+ redactionQuery: values.redactionQuery,
476
+ });
477
+
478
+ return TraceConfig.merge({
479
+ ...startupOverrides,
480
+ enabled,
481
+ connection,
482
+ observeConnection,
483
+ ...withStringProperty('serviceTag', traceServiceTag),
484
+ proxy: buildTraceProxyConfig(startupOverrides, values),
485
+ ...withNumberProperty('pruneAfterHours', pruneAfterHours),
486
+ ...withNumberProperty('slowQueryThreshold', slowQueryThreshold),
487
+ ...withBooleanProperty('captureCachePayloads', captureCachePayloads),
488
+ ...withBooleanProperty('captureQueryBindings', captureQueryBindings),
489
+ contentDispatch: buildTraceContentDispatchConfig(startupOverrides, values),
490
+ logMinLevel,
491
+ ...(redaction === undefined ? {} : { redaction }),
492
+ });
493
+ };
494
+
495
+ const createTraceWatcherArgs = async (
496
+ core: CoreApi,
497
+ Env: TraceEnvApi,
498
+ config: ReturnType<typeof TraceConfig.merge>
499
+ ): Promise<Pick<ITraceWatcherConfig, 'storage' | 'config' | 'db'>> => {
500
+ const resolvedConnectionName = resolveTraceConnectionName(Env, config.connection);
501
+ const resolvedObservedConnectionName = resolveObservedConnectionName(
502
+ Env,
503
+ config.observeConnection,
504
+ resolvedConnectionName
505
+ );
506
+ globalTraceRegisterState.__zintrust_system_trace_connection_name__ = resolvedConnectionName;
507
+ globalTraceRegisterState.__zintrust_system_trace_observe_connection_name__ =
508
+ resolvedObservedConnectionName;
509
+
510
+ const storageDb = core.useDatabase?.(undefined, resolvedConnectionName);
511
+ const observedDb = core.useDatabase?.(undefined, resolvedObservedConnectionName);
512
+
513
+ assertTraceConnectionResolved(core, storageDb, {
514
+ connectionName: resolvedConnectionName,
515
+ envKey: 'TRACE_DB_CONNECTION',
516
+ });
517
+ assertTraceConnectionResolved(core, observedDb, {
518
+ connectionName: resolvedObservedConnectionName,
519
+ envKey: 'TRACE_QUERY_CONNECTION',
520
+ });
521
+ await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
522
+
523
+ const resolvedStorage = config.proxy.enabled
524
+ ? ProxyTraceStorage.create({
525
+ baseUrl: config.proxy.url ?? '',
526
+ path: config.proxy.path,
527
+ keyId: config.proxy.keyId ?? '',
528
+ secret: config.proxy.secret ?? '',
529
+ timeoutMs: config.proxy.timeoutMs,
530
+ })
531
+ : TraceStorage.resolveStorage(storageDb);
532
+
533
+ const storage = TraceWriteDiagnostics.wrapStorage(
534
+ TraceContentBudget.wrapStorage(
535
+ TraceContentRedaction.wrapStorage(
536
+ TraceEntryFiltering.wrapStorage(TraceServiceTag.wrapStorage(resolvedStorage, config), config),
537
+ config.redaction
454
538
  ),
455
- {
456
- connectionName: resolvedConnectionName,
457
- logger: core.Logger,
539
+ config
540
+ ),
541
+ {
542
+ connectionName: resolvedConnectionName,
543
+ logger: core.Logger,
544
+ }
545
+ );
546
+
547
+ return { storage, config, db: observedDb };
548
+ };
549
+
550
+ const registerTraceWatchers = async (
551
+ watcherArgs: Pick<ITraceWatcherConfig, 'storage' | 'config' | 'db'>
552
+ ): Promise<void> => {
553
+ const [
554
+ { HttpWatcher },
555
+ { QueryWatcher },
556
+ { LogWatcher },
557
+ { ExceptionWatcher },
558
+ { JobWatcher },
559
+ { CacheWatcher },
560
+ { ScheduleWatcher },
561
+ { MailWatcher },
562
+ { AuthWatcher },
563
+ { EventWatcher },
564
+ { ModelWatcher },
565
+ { NotificationWatcher },
566
+ { RedisWatcher },
567
+ { GateWatcher },
568
+ { MiddlewareWatcher },
569
+ { CommandWatcher },
570
+ { BatchWatcher },
571
+ { DumpWatcher },
572
+ { ViewWatcher },
573
+ { HttpClientWatcher },
574
+ ] = await Promise.all([
575
+ import('./watchers/HttpWatcher'),
576
+ import('./watchers/QueryWatcher'),
577
+ import('./watchers/LogWatcher'),
578
+ import('./watchers/ExceptionWatcher'),
579
+ import('./watchers/JobWatcher'),
580
+ import('./watchers/CacheWatcher'),
581
+ import('./watchers/ScheduleWatcher'),
582
+ import('./watchers/MailWatcher'),
583
+ import('./watchers/AuthWatcher'),
584
+ import('./watchers/EventWatcher'),
585
+ import('./watchers/ModelWatcher'),
586
+ import('./watchers/NotificationWatcher'),
587
+ import('./watchers/RedisWatcher'),
588
+ import('./watchers/GateWatcher'),
589
+ import('./watchers/MiddlewareWatcher'),
590
+ import('./watchers/CommandWatcher'),
591
+ import('./watchers/BatchWatcher'),
592
+ import('./watchers/DumpWatcher'),
593
+ import('./watchers/ViewWatcher'),
594
+ import('./watchers/HttpClientWatcher'),
595
+ ]);
596
+
597
+ HttpWatcher.register({ ...watcherArgs, registerMiddleware: resolveRegisterMiddleware() });
598
+ QueryWatcher.register(watcherArgs);
599
+ LogWatcher.register(watcherArgs);
600
+ ExceptionWatcher.register(watcherArgs);
601
+ JobWatcher.register(watcherArgs);
602
+ CacheWatcher.register(watcherArgs);
603
+ ScheduleWatcher.register(watcherArgs);
604
+ MailWatcher.register(watcherArgs);
605
+ AuthWatcher.register(watcherArgs);
606
+ EventWatcher.register(watcherArgs);
607
+ ModelWatcher.register(watcherArgs);
608
+ NotificationWatcher.register(watcherArgs);
609
+ RedisWatcher.register(watcherArgs);
610
+ GateWatcher.register(watcherArgs);
611
+ MiddlewareWatcher.register(watcherArgs);
612
+ CommandWatcher.register(watcherArgs);
613
+ BatchWatcher.register(watcherArgs);
614
+ DumpWatcher.register(watcherArgs);
615
+ ViewWatcher.register(watcherArgs);
616
+ HttpClientWatcher.register(watcherArgs);
617
+ };
618
+
619
+ const activateTrace = async (
620
+ core: CoreApi,
621
+ Env: TraceEnvApi,
622
+ startupOverrides: TraceConfigOverrides | undefined
623
+ ): Promise<void> => {
624
+ const config = buildTraceRuntimeConfig(Env, startupOverrides);
625
+ if (!config.enabled) return;
626
+
627
+ const watcherArgs = await createTraceWatcherArgs(core, Env, config);
628
+
629
+ if (core.RequestContext) {
630
+ TraceContext.setRequestContextImpl(
631
+ core.RequestContext as {
632
+ current?: () => unknown;
633
+ peek?: () => unknown;
458
634
  }
459
635
  );
636
+ }
460
637
 
461
- if (core.RequestContext) {
462
- TraceContext.setRequestContextImpl(
463
- core.RequestContext as {
464
- current?: () => unknown;
465
- peek?: () => unknown;
466
- }
467
- );
468
- }
638
+ await registerTraceWatchers(watcherArgs);
639
+ };
640
+
641
+ const initializeTraceRegister = async (): Promise<void> => {
642
+ const core = (await importCore()) as CoreApi;
643
+ const Env = core.Env;
644
+ const startupOverrides = await resolveTraceStartupOverrides(core);
469
645
 
470
- const [
471
- { HttpWatcher },
472
- { QueryWatcher },
473
- { LogWatcher },
474
- { ExceptionWatcher },
475
- { JobWatcher },
476
- { CacheWatcher },
477
- { ScheduleWatcher },
478
- { MailWatcher },
479
- { AuthWatcher },
480
- { EventWatcher },
481
- { ModelWatcher },
482
- { NotificationWatcher },
483
- { RedisWatcher },
484
- { GateWatcher },
485
- { MiddlewareWatcher },
486
- { CommandWatcher },
487
- { BatchWatcher },
488
- { DumpWatcher },
489
- { ViewWatcher },
490
- { HttpClientWatcher },
491
- ] = await Promise.all([
492
- import('./watchers/HttpWatcher'),
493
- import('./watchers/QueryWatcher'),
494
- import('./watchers/LogWatcher'),
495
- import('./watchers/ExceptionWatcher'),
496
- import('./watchers/JobWatcher'),
497
- import('./watchers/CacheWatcher'),
498
- import('./watchers/ScheduleWatcher'),
499
- import('./watchers/MailWatcher'),
500
- import('./watchers/AuthWatcher'),
501
- import('./watchers/EventWatcher'),
502
- import('./watchers/ModelWatcher'),
503
- import('./watchers/NotificationWatcher'),
504
- import('./watchers/RedisWatcher'),
505
- import('./watchers/GateWatcher'),
506
- import('./watchers/MiddlewareWatcher'),
507
- import('./watchers/CommandWatcher'),
508
- import('./watchers/BatchWatcher'),
509
- import('./watchers/DumpWatcher'),
510
- import('./watchers/ViewWatcher'),
511
- import('./watchers/HttpClientWatcher'),
512
- ]);
513
-
514
- const watcherArgs = { storage, config, db: observedDb };
515
-
516
- HttpWatcher.register({ ...watcherArgs, registerMiddleware: resolveRegisterMiddleware() });
517
-
518
- QueryWatcher.register(watcherArgs);
519
- LogWatcher.register(watcherArgs);
520
- ExceptionWatcher.register(watcherArgs);
521
- JobWatcher.register(watcherArgs);
522
- CacheWatcher.register(watcherArgs);
523
- ScheduleWatcher.register(watcherArgs);
524
- MailWatcher.register(watcherArgs);
525
- AuthWatcher.register(watcherArgs);
526
- EventWatcher.register(watcherArgs);
527
- ModelWatcher.register(watcherArgs);
528
- NotificationWatcher.register(watcherArgs);
529
- RedisWatcher.register(watcherArgs);
530
- GateWatcher.register(watcherArgs);
531
- MiddlewareWatcher.register(watcherArgs);
532
- CommandWatcher.register(watcherArgs);
533
- BatchWatcher.register(watcherArgs);
534
- DumpWatcher.register(watcherArgs);
535
- ViewWatcher.register(watcherArgs);
536
- HttpClientWatcher.register(watcherArgs);
646
+ if (!traceAlreadyInitialized && Env) {
647
+ await activateTrace(core, Env, startupOverrides);
648
+
649
+ return;
537
650
  }
538
- } else if (!traceAlreadyInitialized) {
539
- // Running outside a ZinTrust project - skip init silently.
540
- // eslint-disable-next-line no-console
541
- console.warn('[trace] @zintrust/core not found - trace will not be activated.');
542
- }
651
+
652
+ if (!traceAlreadyInitialized) {
653
+ // Running outside a ZinTrust project - skip init silently.
654
+ // eslint-disable-next-line no-console
655
+ console.warn('[trace] @zintrust/core not found - trace will not be activated.');
656
+ }
657
+ };
658
+
659
+ export const registerTraceReady = initializeTraceRegister();