@reproapp/node-sdk 0.0.46 → 0.0.48
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/index.d.ts +16 -3
- package/dist/index.js +327 -159
- package/dist/ingest/client.js +11 -0
- package/dist/privacy.d.ts +1 -0
- package/dist/privacy.js +306 -7
- package/dist/semantic-serialization.d.ts +9 -0
- package/dist/semantic-serialization.js +171 -0
- package/package.json +3 -3
- package/src/index.ts +513 -144
- package/src/ingest/client.ts +13 -0
- package/src/privacy.ts +409 -7
- package/src/semantic-serialization.ts +182 -0
- package/test/content-addressed-mongodb.testcontainers.test.js +348 -0
- package/test/content-addressed-trace-values.test.js +264 -0
- package/test/fixtures/semantic-serialization-trace-controller.js +85 -0
- package/test/fixtures/semantic-serialization-trace-server.js +19 -0
- package/test/ingest-high-priority-fairness.test.js +76 -0
- package/test/policy-capture-attestation.test.js +234 -0
- package/test/policy-session-completion.testcontainers.test.js +271 -0
- package/test/privacy-policy-index.test.js +194 -0
- package/test/sdk-background-timeout-isolation.test.js +39 -0
- package/test/semantic-serialization-trace.test.js +152 -0
- package/test/semantic-serialization.test.js +229 -0
- package/test/session-storage-budget.test.js +298 -0
- package/test/trace-batch-size.test.js +37 -0
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Request, Response, NextFunction } from 'express';
|
|
|
2
2
|
import type { Schema, Model, Query } from 'mongoose';
|
|
3
3
|
import * as mongoose from 'mongoose';
|
|
4
4
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
5
|
-
import { randomUUID } from 'crypto';
|
|
5
|
+
import { createHmac, randomUUID } from 'crypto';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { pathToFileURL } from 'url';
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type RuntimePrivacySurface,
|
|
29
29
|
type RuntimePrivacyTraceContext,
|
|
30
30
|
} from './privacy';
|
|
31
|
+
import { projectSemanticInfrastructureValue } from './semantic-serialization';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* ============================= IMPORTANT =============================
|
|
@@ -379,7 +380,11 @@ type PendingTraceEventRecord = TraceEventRecord & {
|
|
|
379
380
|
};
|
|
380
381
|
|
|
381
382
|
type TraceValueCaptureRef = {
|
|
382
|
-
status:
|
|
383
|
+
status:
|
|
384
|
+
| 'stored'
|
|
385
|
+
| 'truncated-for-storage'
|
|
386
|
+
| 'skipped-non-meaningful'
|
|
387
|
+
| 'failed-policy';
|
|
383
388
|
valueId?: string;
|
|
384
389
|
projectedKind?: string;
|
|
385
390
|
};
|
|
@@ -414,6 +419,7 @@ type TraceValueBatchEntry = {
|
|
|
414
419
|
};
|
|
415
420
|
|
|
416
421
|
type TraceMaterializationConfig = {
|
|
422
|
+
tenantId: string;
|
|
417
423
|
appId: string;
|
|
418
424
|
appSecret?: string;
|
|
419
425
|
appName?: string;
|
|
@@ -427,6 +433,7 @@ type TraceMaterializationWorkerPayload = {
|
|
|
427
433
|
masking: NormalizedMaskingConfig | null;
|
|
428
434
|
privacy: NormalizedRuntimePrivacyPolicy | null;
|
|
429
435
|
fullValueCaptureEnabled: boolean;
|
|
436
|
+
valueNamespace?: string;
|
|
430
437
|
};
|
|
431
438
|
|
|
432
439
|
type TraceMaterializationWorkerRequest = {
|
|
@@ -1212,20 +1219,70 @@ type RequestCaptureScopeState = {
|
|
|
1212
1219
|
traceBatchCount: number;
|
|
1213
1220
|
dbChangeCount: number;
|
|
1214
1221
|
emailEventCount: number;
|
|
1222
|
+
captureFailures: Array<{
|
|
1223
|
+
stage: string;
|
|
1224
|
+
target: string;
|
|
1225
|
+
reason: string;
|
|
1226
|
+
}>;
|
|
1227
|
+
privacyMode: 'policy' | 'fallback' | 'raw';
|
|
1228
|
+
privacyPolicyFingerprint?: string;
|
|
1229
|
+
privacyStatementCount: number;
|
|
1230
|
+
emittedValueDefinitionIds: Set<string>;
|
|
1215
1231
|
};
|
|
1216
1232
|
|
|
1217
1233
|
function createRequestCaptureScope(
|
|
1218
1234
|
requestRid: string,
|
|
1219
1235
|
transport: RequestCaptureScopeState['transport'],
|
|
1236
|
+
cfg?: ReproMiddlewareConfig,
|
|
1220
1237
|
): RequestCaptureScopeState {
|
|
1238
|
+
const privacy = cfg ? getRuntimePrivacyState(cfg).policy ?? null : null;
|
|
1239
|
+
const privacyMode =
|
|
1240
|
+
cfg && shouldCapturePrivacyRaw(cfg)
|
|
1241
|
+
? 'raw'
|
|
1242
|
+
: privacy
|
|
1243
|
+
? 'policy'
|
|
1244
|
+
: 'fallback';
|
|
1221
1245
|
return {
|
|
1222
1246
|
requestRid,
|
|
1223
1247
|
transport,
|
|
1224
|
-
|
|
1248
|
+
// Every scope represents one captured transport request. This is an
|
|
1249
|
+
// observed expectation, not a count of successful enqueue operations.
|
|
1250
|
+
backendRequestCount: 1,
|
|
1225
1251
|
traceBatchCount: 0,
|
|
1226
1252
|
dbChangeCount: 0,
|
|
1227
1253
|
emailEventCount: 0,
|
|
1254
|
+
captureFailures: [],
|
|
1255
|
+
privacyMode,
|
|
1256
|
+
privacyPolicyFingerprint: privacy?.fingerprint,
|
|
1257
|
+
privacyStatementCount: privacy?.statements.length ?? 0,
|
|
1258
|
+
emittedValueDefinitionIds: new Set(),
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function recordRequestCaptureFailure(
|
|
1263
|
+
scope: RequestCaptureScopeState | null | undefined,
|
|
1264
|
+
stage: string,
|
|
1265
|
+
target: string,
|
|
1266
|
+
reason: string,
|
|
1267
|
+
): void {
|
|
1268
|
+
if (!scope) return;
|
|
1269
|
+
const normalized = {
|
|
1270
|
+
stage: String(stage || 'capture').slice(0, 80),
|
|
1271
|
+
target: String(target || 'unknown').slice(0, 120),
|
|
1272
|
+
reason: String(reason || 'capture_failed').slice(0, 120),
|
|
1228
1273
|
};
|
|
1274
|
+
if (
|
|
1275
|
+
scope.captureFailures.some((failure) =>
|
|
1276
|
+
failure.stage === normalized.stage &&
|
|
1277
|
+
failure.target === normalized.target &&
|
|
1278
|
+
failure.reason === normalized.reason,
|
|
1279
|
+
)
|
|
1280
|
+
) {
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
if (scope.captureFailures.length < 25) {
|
|
1284
|
+
scope.captureFailures.push(normalized);
|
|
1285
|
+
}
|
|
1229
1286
|
}
|
|
1230
1287
|
|
|
1231
1288
|
function buildRequestCaptureManifestPayload(
|
|
@@ -1255,6 +1312,20 @@ function buildRequestCaptureManifestPayload(
|
|
|
1255
1312
|
to: expectedTraceBatches - 1,
|
|
1256
1313
|
}
|
|
1257
1314
|
: undefined,
|
|
1315
|
+
privacy: {
|
|
1316
|
+
mode: scope.privacyMode,
|
|
1317
|
+
status:
|
|
1318
|
+
scope.captureFailures.length > 0
|
|
1319
|
+
? 'failed'
|
|
1320
|
+
: scope.privacyMode === 'raw'
|
|
1321
|
+
? 'bypassed'
|
|
1322
|
+
: 'applied',
|
|
1323
|
+
policyFingerprint: scope.privacyPolicyFingerprint,
|
|
1324
|
+
statementCount: scope.privacyStatementCount,
|
|
1325
|
+
},
|
|
1326
|
+
captureFailures: scope.captureFailures.length
|
|
1327
|
+
? scope.captureFailures.map((failure) => ({ ...failure }))
|
|
1328
|
+
: undefined,
|
|
1258
1329
|
closedAt,
|
|
1259
1330
|
};
|
|
1260
1331
|
}
|
|
@@ -1439,12 +1510,6 @@ const SDK_BACKGROUND_MAX_DEFER_MS = (() => {
|
|
|
1439
1510
|
return Number.POSITIVE_INFINITY;
|
|
1440
1511
|
})();
|
|
1441
1512
|
|
|
1442
|
-
const SDK_BACKGROUND_WORK_TIMEOUT_MS = (() => {
|
|
1443
|
-
const env = Number(process.env.REPRO_SDK_BACKGROUND_WORK_TIMEOUT_MS);
|
|
1444
|
-
if (Number.isFinite(env) && env >= 0) return Math.trunc(env);
|
|
1445
|
-
return 20000;
|
|
1446
|
-
})();
|
|
1447
|
-
|
|
1448
1513
|
const TRACE_MATERIALIZE_OFFTHREAD_ENABLED = (() => {
|
|
1449
1514
|
const env = String(process.env.REPRO_TRACE_MATERIALIZE_OFFTHREAD || '').trim().toLowerCase();
|
|
1450
1515
|
if (!env) return true;
|
|
@@ -1774,24 +1839,9 @@ function scheduleSdkBackgroundDrain(delayMs = 0): void {
|
|
|
1774
1839
|
}
|
|
1775
1840
|
|
|
1776
1841
|
async function runSdkBackgroundWork(work: () => Promise<void> | void): Promise<void> {
|
|
1777
|
-
|
|
1778
|
-
await work();
|
|
1779
|
-
return;
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
let timeout: NodeJS.Timeout | null = null;
|
|
1783
|
-
const workPromise = Promise.resolve()
|
|
1842
|
+
await Promise.resolve()
|
|
1784
1843
|
.then(work)
|
|
1785
1844
|
.catch(() => undefined);
|
|
1786
|
-
const timeoutPromise = new Promise<void>((resolve) => {
|
|
1787
|
-
timeout = setTimeout(resolve, SDK_BACKGROUND_WORK_TIMEOUT_MS);
|
|
1788
|
-
try { (timeout as any).unref?.(); } catch {}
|
|
1789
|
-
});
|
|
1790
|
-
|
|
1791
|
-
await Promise.race([workPromise, timeoutPromise]);
|
|
1792
|
-
if (timeout) {
|
|
1793
|
-
try { clearTimeout(timeout); } catch {}
|
|
1794
|
-
}
|
|
1795
1845
|
}
|
|
1796
1846
|
|
|
1797
1847
|
async function drainQueuedIngestBeforeBackgroundWork(): Promise<void> {
|
|
@@ -2783,7 +2833,11 @@ const TRACE_MAX_EVENTS_PER_REQUEST = (() => {
|
|
|
2783
2833
|
const TRACE_MAX_BATCHES_PER_REQUEST = (() => {
|
|
2784
2834
|
const env = Number(process.env.REPRO_TRACE_MAX_BATCHES_PER_REQUEST);
|
|
2785
2835
|
if (Number.isFinite(env) && env > 0) return Math.trunc(env);
|
|
2786
|
-
|
|
2836
|
+
// Byte-based splitting can legitimately produce one batch per event when
|
|
2837
|
+
// every event owns a distinct large value. The event limit remains the
|
|
2838
|
+
// hard upper bound; deriving this from the count-only batch size silently
|
|
2839
|
+
// dropped otherwise valid events after ten batches.
|
|
2840
|
+
return TRACE_MAX_EVENTS_PER_REQUEST;
|
|
2787
2841
|
})();
|
|
2788
2842
|
const TRACE_FLUSH_DELAY_MS = 20;
|
|
2789
2843
|
// Choose how to order trace events in payloads.
|
|
@@ -3135,6 +3189,17 @@ function sanitizeTraceValue(
|
|
|
3135
3189
|
const mongoPlaceholder = describeMongoPlaceholder(value);
|
|
3136
3190
|
if (mongoPlaceholder) return mongoPlaceholder;
|
|
3137
3191
|
|
|
3192
|
+
const semanticProjection = projectSemanticInfrastructureValue(value);
|
|
3193
|
+
if (semanticProjection) {
|
|
3194
|
+
return sanitizeTraceValue(
|
|
3195
|
+
semanticProjection.value,
|
|
3196
|
+
depth,
|
|
3197
|
+
seen,
|
|
3198
|
+
options,
|
|
3199
|
+
valuePath,
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3138
3203
|
if (isThenable(value)) {
|
|
3139
3204
|
return { __type: 'Promise', state: 'pending' };
|
|
3140
3205
|
}
|
|
@@ -3357,6 +3422,8 @@ async function materializePendingTraceEvents(
|
|
|
3357
3422
|
maskReq: MaskRequestContext;
|
|
3358
3423
|
masking: NormalizedMaskingConfig | null;
|
|
3359
3424
|
resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
|
|
3425
|
+
captureScope?: RequestCaptureScopeState | null;
|
|
3426
|
+
valueNamespace?: string;
|
|
3360
3427
|
},
|
|
3361
3428
|
): Promise<TraceEventRecord[]> {
|
|
3362
3429
|
for (const evt of events) {
|
|
@@ -3374,6 +3441,7 @@ async function materializePendingTraceEvents(
|
|
|
3374
3441
|
trace: candidate,
|
|
3375
3442
|
masking: params.masking,
|
|
3376
3443
|
privacy: activePrivacy,
|
|
3444
|
+
valueNamespace: params.valueNamespace,
|
|
3377
3445
|
};
|
|
3378
3446
|
const traceValueMetadata = {
|
|
3379
3447
|
fn: evt.fn,
|
|
@@ -3403,7 +3471,7 @@ async function materializePendingTraceEvents(
|
|
|
3403
3471
|
previewValue: evt.args,
|
|
3404
3472
|
capture: captureContext,
|
|
3405
3473
|
metadata: traceValueMetadata,
|
|
3406
|
-
});
|
|
3474
|
+
}, params.captureScope);
|
|
3407
3475
|
if (argsValueCapture?.entry) {
|
|
3408
3476
|
attachTraceValueEntry(evt, argsValueCapture.entry);
|
|
3409
3477
|
}
|
|
@@ -3432,7 +3500,7 @@ async function materializePendingTraceEvents(
|
|
|
3432
3500
|
previewValue: evt.returnValue,
|
|
3433
3501
|
capture: captureContext,
|
|
3434
3502
|
metadata: traceValueMetadata,
|
|
3435
|
-
});
|
|
3503
|
+
}, params.captureScope);
|
|
3436
3504
|
if (returnValueCapture?.entry) {
|
|
3437
3505
|
attachTraceValueEntry(evt, returnValueCapture.entry);
|
|
3438
3506
|
}
|
|
@@ -3461,7 +3529,7 @@ async function materializePendingTraceEvents(
|
|
|
3461
3529
|
previewValue: evt.error,
|
|
3462
3530
|
capture: captureContext,
|
|
3463
3531
|
metadata: traceValueMetadata,
|
|
3464
|
-
});
|
|
3532
|
+
}, params.captureScope);
|
|
3465
3533
|
if (errorValueCapture?.entry) {
|
|
3466
3534
|
attachTraceValueEntry(evt, errorValueCapture.entry);
|
|
3467
3535
|
}
|
|
@@ -3479,7 +3547,20 @@ function stripPendingTraceValues(
|
|
|
3479
3547
|
reason: string,
|
|
3480
3548
|
): TraceEventRecord[] {
|
|
3481
3549
|
for (const evt of events) {
|
|
3482
|
-
|
|
3550
|
+
const pending = evt.__reproPending;
|
|
3551
|
+
if (!pending) continue;
|
|
3552
|
+
if (pending.argsRaw !== undefined) {
|
|
3553
|
+
evt.args = makeOmittedValue(reason);
|
|
3554
|
+
evt.argsValueCapture = { status: 'failed-policy' };
|
|
3555
|
+
}
|
|
3556
|
+
if (pending.returnValueRaw !== undefined) {
|
|
3557
|
+
evt.returnValue = makeOmittedValue(reason);
|
|
3558
|
+
evt.returnValueCapture = { status: 'failed-policy' };
|
|
3559
|
+
}
|
|
3560
|
+
if (pending.errorRaw !== undefined) {
|
|
3561
|
+
evt.error = makeOmittedValue(reason);
|
|
3562
|
+
evt.errorValueCapture = { status: 'failed-policy' };
|
|
3563
|
+
}
|
|
3483
3564
|
delete evt.__reproPending;
|
|
3484
3565
|
}
|
|
3485
3566
|
return events;
|
|
@@ -3496,6 +3577,8 @@ async function materializePendingTraceEventsAsync(
|
|
|
3496
3577
|
maskReq: MaskRequestContext;
|
|
3497
3578
|
masking: NormalizedMaskingConfig | null;
|
|
3498
3579
|
resolvePrivacy: () => NormalizedRuntimePrivacyPolicy | null;
|
|
3580
|
+
captureScope?: RequestCaptureScopeState | null;
|
|
3581
|
+
valueNamespace?: string;
|
|
3499
3582
|
},
|
|
3500
3583
|
): Promise<TraceEventRecord[]> {
|
|
3501
3584
|
if (!events.some((evt) => evt.__reproPending)) {
|
|
@@ -3510,6 +3593,12 @@ async function materializePendingTraceEventsAsync(
|
|
|
3510
3593
|
resolvePrivacy: () => privacy,
|
|
3511
3594
|
});
|
|
3512
3595
|
} catch {
|
|
3596
|
+
recordRequestCaptureFailure(
|
|
3597
|
+
params.captureScope ?? getCtx().captureScope,
|
|
3598
|
+
'privacy.trace',
|
|
3599
|
+
'trace.values',
|
|
3600
|
+
'main_thread_materialization_failed',
|
|
3601
|
+
);
|
|
3513
3602
|
return stripPendingTraceValues(events, 'main_thread_failed');
|
|
3514
3603
|
}
|
|
3515
3604
|
}
|
|
@@ -3517,6 +3606,7 @@ async function materializePendingTraceEventsAsync(
|
|
|
3517
3606
|
return await materializePendingTraceEventsOffThread(events, {
|
|
3518
3607
|
events,
|
|
3519
3608
|
cfg: {
|
|
3609
|
+
tenantId: params.cfg.tenantId,
|
|
3520
3610
|
appId: params.cfg.appId,
|
|
3521
3611
|
appSecret: params.cfg.appSecret,
|
|
3522
3612
|
appName: params.cfg.appName,
|
|
@@ -3526,18 +3616,27 @@ async function materializePendingTraceEventsAsync(
|
|
|
3526
3616
|
masking: params.masking,
|
|
3527
3617
|
privacy,
|
|
3528
3618
|
fullValueCaptureEnabled: __FULL_VALUE_CAPTURE_ENABLED,
|
|
3619
|
+
valueNamespace: params.valueNamespace,
|
|
3529
3620
|
});
|
|
3530
3621
|
} catch (err) {
|
|
3531
|
-
if (isTraceMaterializerTimeoutError(err)) {
|
|
3532
|
-
return stripPendingTraceValues(events, 'worker_timeout');
|
|
3533
|
-
}
|
|
3534
3622
|
try {
|
|
3535
3623
|
return await materializePendingTraceEvents(events, {
|
|
3536
3624
|
...params,
|
|
3537
3625
|
resolvePrivacy: () => privacy,
|
|
3538
3626
|
});
|
|
3539
3627
|
} catch {
|
|
3540
|
-
|
|
3628
|
+
recordRequestCaptureFailure(
|
|
3629
|
+
params.captureScope ?? getCtx().captureScope,
|
|
3630
|
+
'privacy.trace',
|
|
3631
|
+
'trace.values',
|
|
3632
|
+
isTraceMaterializerTimeoutError(err)
|
|
3633
|
+
? 'worker_timeout_fallback_failed'
|
|
3634
|
+
: 'worker_and_fallback_failed',
|
|
3635
|
+
);
|
|
3636
|
+
return stripPendingTraceValues(
|
|
3637
|
+
events,
|
|
3638
|
+
isTraceMaterializerTimeoutError(err) ? 'worker_timeout_fallback_failed' : 'worker_failed',
|
|
3639
|
+
);
|
|
3541
3640
|
}
|
|
3542
3641
|
}
|
|
3543
3642
|
}
|
|
@@ -3553,6 +3652,7 @@ export async function __materializePendingTraceEventsForWorker(
|
|
|
3553
3652
|
maskReq: payload.maskReq,
|
|
3554
3653
|
masking: payload.masking,
|
|
3555
3654
|
resolvePrivacy: () => payload.privacy,
|
|
3655
|
+
valueNamespace: payload.valueNamespace,
|
|
3556
3656
|
});
|
|
3557
3657
|
return materialized.map((event) => {
|
|
3558
3658
|
const entries = getEventTraceValueEntries(event);
|
|
@@ -4137,6 +4237,7 @@ function materializeInlinePrivacyValue(
|
|
|
4137
4237
|
masking: NormalizedMaskingConfig | null,
|
|
4138
4238
|
privacy: NormalizedRuntimePrivacyPolicy | null,
|
|
4139
4239
|
db?: RuntimePrivacyDbContext | null,
|
|
4240
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
4140
4241
|
): InlinePrivacyMaterializationResult {
|
|
4141
4242
|
if (value === undefined) {
|
|
4142
4243
|
return { value };
|
|
@@ -4149,6 +4250,12 @@ function materializeInlinePrivacyValue(
|
|
|
4149
4250
|
const valueAfterPrivacy = applyPrivacyThenMask(target, value, cfg, req, trace, masking, privacy, db);
|
|
4150
4251
|
return { value: valueAfterPrivacy };
|
|
4151
4252
|
} catch {
|
|
4253
|
+
recordRequestCaptureFailure(
|
|
4254
|
+
captureScope ?? getCtx().captureScope,
|
|
4255
|
+
'privacy.inline',
|
|
4256
|
+
target,
|
|
4257
|
+
'privacy_transform_failed',
|
|
4258
|
+
);
|
|
4152
4259
|
return {
|
|
4153
4260
|
value: undefined,
|
|
4154
4261
|
skipped: {
|
|
@@ -4169,6 +4276,7 @@ async function materializeInlinePrivacyValueAsync(
|
|
|
4169
4276
|
masking: NormalizedMaskingConfig | null,
|
|
4170
4277
|
privacy: NormalizedRuntimePrivacyPolicy | null,
|
|
4171
4278
|
db?: RuntimePrivacyDbContext | null,
|
|
4279
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
4172
4280
|
): Promise<InlinePrivacyMaterializationResult> {
|
|
4173
4281
|
if (value === undefined) {
|
|
4174
4282
|
return { value };
|
|
@@ -4181,6 +4289,12 @@ async function materializeInlinePrivacyValueAsync(
|
|
|
4181
4289
|
const valueAfterPrivacy = await applyPrivacyThenMaskAsync(target, value, cfg, req, trace, masking, privacy, db);
|
|
4182
4290
|
return { value: valueAfterPrivacy };
|
|
4183
4291
|
} catch {
|
|
4292
|
+
recordRequestCaptureFailure(
|
|
4293
|
+
captureScope ?? getCtx().captureScope,
|
|
4294
|
+
'privacy.inline',
|
|
4295
|
+
target,
|
|
4296
|
+
'privacy_transform_failed',
|
|
4297
|
+
);
|
|
4184
4298
|
return {
|
|
4185
4299
|
value: undefined,
|
|
4186
4300
|
skipped: {
|
|
@@ -4347,6 +4461,7 @@ type FullValueCaptureContext = {
|
|
|
4347
4461
|
masking: NormalizedMaskingConfig | null;
|
|
4348
4462
|
privacy: NormalizedRuntimePrivacyPolicy | null;
|
|
4349
4463
|
db?: RuntimePrivacyDbContext | null;
|
|
4464
|
+
valueNamespace?: string;
|
|
4350
4465
|
};
|
|
4351
4466
|
|
|
4352
4467
|
const TRACE_FULL_VALUE_ENTRY_SYMBOL = Symbol('repro.traceFullValueEntries');
|
|
@@ -4752,6 +4867,18 @@ function cloneFullValueCandidate(
|
|
|
4752
4867
|
const mongoPlaceholder = describeMongoPlaceholder(value);
|
|
4753
4868
|
if (mongoPlaceholder) return undefined;
|
|
4754
4869
|
|
|
4870
|
+
const semanticProjection = projectSemanticInfrastructureValue(value);
|
|
4871
|
+
if (semanticProjection) {
|
|
4872
|
+
return cloneFullValueCandidate(
|
|
4873
|
+
semanticProjection.value,
|
|
4874
|
+
ctx,
|
|
4875
|
+
limits,
|
|
4876
|
+
depth,
|
|
4877
|
+
seen,
|
|
4878
|
+
valuePath,
|
|
4879
|
+
);
|
|
4880
|
+
}
|
|
4881
|
+
|
|
4755
4882
|
if (isThenable(value)) return undefined;
|
|
4756
4883
|
if (shouldSkipFullValueObject(value)) return undefined;
|
|
4757
4884
|
|
|
@@ -4979,6 +5106,18 @@ async function cloneFullValueCandidateAsync(
|
|
|
4979
5106
|
const mongoPlaceholder = describeMongoPlaceholder(value);
|
|
4980
5107
|
if (mongoPlaceholder) return undefined;
|
|
4981
5108
|
|
|
5109
|
+
const semanticProjection = projectSemanticInfrastructureValue(value);
|
|
5110
|
+
if (semanticProjection) {
|
|
5111
|
+
return cloneFullValueCandidateAsync(
|
|
5112
|
+
semanticProjection.value,
|
|
5113
|
+
ctx,
|
|
5114
|
+
limits,
|
|
5115
|
+
depth,
|
|
5116
|
+
seen,
|
|
5117
|
+
valuePath,
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
|
|
4982
5121
|
if (isThenable(value)) return undefined;
|
|
4983
5122
|
if (shouldSkipFullValueObject(value)) return undefined;
|
|
4984
5123
|
|
|
@@ -5125,12 +5264,18 @@ function getEventTraceValueEntries(event: TraceEventRecord | PendingTraceEventRe
|
|
|
5125
5264
|
return undefined;
|
|
5126
5265
|
}
|
|
5127
5266
|
|
|
5128
|
-
function collectBatchTraceValueEntries(
|
|
5267
|
+
function collectBatchTraceValueEntries(
|
|
5268
|
+
batch: TraceEventRecord[],
|
|
5269
|
+
batchIndex: number,
|
|
5270
|
+
emittedValueIds: Set<string> = new Set(),
|
|
5271
|
+
): TraceValueBatchEntry[] {
|
|
5129
5272
|
const collected: TraceValueBatchEntry[] = [];
|
|
5130
5273
|
batch.forEach((event, eventIndex) => {
|
|
5131
5274
|
const entries = getEventTraceValueEntries(event);
|
|
5132
5275
|
if (!Array.isArray(entries) || !entries.length) return;
|
|
5133
5276
|
entries.forEach((entry) => {
|
|
5277
|
+
if (emittedValueIds.has(entry.id)) return;
|
|
5278
|
+
emittedValueIds.add(entry.id);
|
|
5134
5279
|
collected.push({
|
|
5135
5280
|
...entry,
|
|
5136
5281
|
batchIndex,
|
|
@@ -5149,13 +5294,44 @@ function serializedByteLength(value: any): number {
|
|
|
5149
5294
|
}
|
|
5150
5295
|
}
|
|
5151
5296
|
|
|
5297
|
+
function contentAddressedValueId(
|
|
5298
|
+
target: TraceValueBatchEntry['target'],
|
|
5299
|
+
candidateSerialized: string,
|
|
5300
|
+
capture: FullValueCaptureContext,
|
|
5301
|
+
): string {
|
|
5302
|
+
const config = capture.runtimeConfig;
|
|
5303
|
+
const signingKey = String(
|
|
5304
|
+
config.appSecret ||
|
|
5305
|
+
`${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
|
|
5306
|
+
);
|
|
5307
|
+
const namespace = String(
|
|
5308
|
+
capture.valueNamespace ||
|
|
5309
|
+
`${config.tenantId || 'tenant'}:${config.appId || 'app'}`,
|
|
5310
|
+
);
|
|
5311
|
+
const digest = createHmac('sha256', signingKey)
|
|
5312
|
+
.update('repro-captured-value-v1\0')
|
|
5313
|
+
.update(namespace)
|
|
5314
|
+
.update('\0')
|
|
5315
|
+
.update(target)
|
|
5316
|
+
.update('\0')
|
|
5317
|
+
.update(candidateSerialized)
|
|
5318
|
+
.digest('hex')
|
|
5319
|
+
.slice(0, 40);
|
|
5320
|
+
return `rv1_${digest}`;
|
|
5321
|
+
}
|
|
5322
|
+
|
|
5152
5323
|
function estimateTraceBatchSerializedBytes(
|
|
5153
5324
|
batch: TraceEventRecord[],
|
|
5154
5325
|
requestRid: string,
|
|
5155
5326
|
actionId: string | null | undefined,
|
|
5156
5327
|
batchIndex: number,
|
|
5328
|
+
previouslyEmittedValueIds: ReadonlySet<string> = new Set(),
|
|
5157
5329
|
): number {
|
|
5158
|
-
const traceValues = collectBatchTraceValueEntries(
|
|
5330
|
+
const traceValues = collectBatchTraceValueEntries(
|
|
5331
|
+
batch,
|
|
5332
|
+
batchIndex,
|
|
5333
|
+
new Set(previouslyEmittedValueIds),
|
|
5334
|
+
);
|
|
5159
5335
|
return serializedByteLength({
|
|
5160
5336
|
actionId: actionId ?? null,
|
|
5161
5337
|
trace: batch,
|
|
@@ -5179,12 +5355,30 @@ function chunkTraceEventsForTransport(
|
|
|
5179
5355
|
const boundedEvents = boundTraceEventsForTransport(events);
|
|
5180
5356
|
const countBatches = chunkArray(boundedEvents, TRACE_BATCH_SIZE);
|
|
5181
5357
|
const sizedBatches: TraceEventRecord[][] = [];
|
|
5358
|
+
const emittedValueIds = new Set<string>();
|
|
5182
5359
|
let current: TraceEventRecord[] = [];
|
|
5183
5360
|
|
|
5361
|
+
const commitCurrent = (): void => {
|
|
5362
|
+
if (!current.length) return;
|
|
5363
|
+
collectBatchTraceValueEntries(
|
|
5364
|
+
current,
|
|
5365
|
+
sizedBatches.length,
|
|
5366
|
+
emittedValueIds,
|
|
5367
|
+
);
|
|
5368
|
+
sizedBatches.push(current);
|
|
5369
|
+
current = [];
|
|
5370
|
+
};
|
|
5371
|
+
|
|
5184
5372
|
for (const event of countBatches.flat()) {
|
|
5185
5373
|
if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
|
|
5186
5374
|
break;
|
|
5187
5375
|
}
|
|
5376
|
+
if (current.length >= TRACE_BATCH_SIZE) {
|
|
5377
|
+
commitCurrent();
|
|
5378
|
+
if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
|
|
5379
|
+
break;
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5188
5382
|
const candidate = current.concat(event);
|
|
5189
5383
|
const batchIndex = sizedBatches.length;
|
|
5190
5384
|
const estimatedBytes = estimateTraceBatchSerializedBytes(
|
|
@@ -5192,15 +5386,15 @@ function chunkTraceEventsForTransport(
|
|
|
5192
5386
|
requestRid,
|
|
5193
5387
|
actionId,
|
|
5194
5388
|
batchIndex,
|
|
5389
|
+
emittedValueIds,
|
|
5195
5390
|
);
|
|
5196
5391
|
|
|
5197
5392
|
if (
|
|
5198
5393
|
current.length > 0 &&
|
|
5199
5394
|
estimatedBytes > TRACE_BATCH_MAX_SERIALIZED_BYTES
|
|
5200
5395
|
) {
|
|
5201
|
-
|
|
5396
|
+
commitCurrent();
|
|
5202
5397
|
if (sizedBatches.length >= TRACE_MAX_BATCHES_PER_REQUEST) {
|
|
5203
|
-
current = [];
|
|
5204
5398
|
break;
|
|
5205
5399
|
}
|
|
5206
5400
|
current = [event];
|
|
@@ -5214,7 +5408,7 @@ function chunkTraceEventsForTransport(
|
|
|
5214
5408
|
current.length > 0 &&
|
|
5215
5409
|
sizedBatches.length < TRACE_MAX_BATCHES_PER_REQUEST
|
|
5216
5410
|
) {
|
|
5217
|
-
|
|
5411
|
+
commitCurrent();
|
|
5218
5412
|
}
|
|
5219
5413
|
|
|
5220
5414
|
return sizedBatches;
|
|
@@ -5286,7 +5480,11 @@ function createCapturedValueEntry(
|
|
|
5286
5480
|
return undefined;
|
|
5287
5481
|
}
|
|
5288
5482
|
|
|
5289
|
-
const valueId =
|
|
5483
|
+
const valueId = contentAddressedValueId(
|
|
5484
|
+
params.target,
|
|
5485
|
+
candidateSerialized,
|
|
5486
|
+
params.capture,
|
|
5487
|
+
);
|
|
5290
5488
|
const projectedKind =
|
|
5291
5489
|
candidate && typeof candidate === 'object'
|
|
5292
5490
|
? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
|
|
@@ -5368,7 +5566,11 @@ async function createCapturedValueEntryAsync(
|
|
|
5368
5566
|
return undefined;
|
|
5369
5567
|
}
|
|
5370
5568
|
|
|
5371
|
-
const valueId =
|
|
5569
|
+
const valueId = contentAddressedValueId(
|
|
5570
|
+
params.target,
|
|
5571
|
+
candidateSerialized,
|
|
5572
|
+
params.capture,
|
|
5573
|
+
);
|
|
5372
5574
|
const projectedKind =
|
|
5373
5575
|
candidate && typeof candidate === 'object'
|
|
5374
5576
|
? ((candidate as Record<string, any>).__kind || (candidate as Record<string, any>).__type)
|
|
@@ -5391,6 +5593,18 @@ async function createCapturedValueEntryAsync(
|
|
|
5391
5593
|
};
|
|
5392
5594
|
}
|
|
5393
5595
|
|
|
5596
|
+
function appendCapturedValueDefinition(
|
|
5597
|
+
sink: TraceValueBatchEntry[],
|
|
5598
|
+
entry: TraceValueBatchEntry | undefined,
|
|
5599
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
5600
|
+
): void {
|
|
5601
|
+
if (!entry) return;
|
|
5602
|
+
const emitted = captureScope?.emittedValueDefinitionIds;
|
|
5603
|
+
if (emitted?.has(entry.id)) return;
|
|
5604
|
+
emitted?.add(entry.id);
|
|
5605
|
+
sink.push(entry);
|
|
5606
|
+
}
|
|
5607
|
+
|
|
5394
5608
|
function maybeCaptureRequestValue(
|
|
5395
5609
|
params: {
|
|
5396
5610
|
target: 'request.headers' | 'request.body' | 'request.params' | 'request.query' | 'response.body';
|
|
@@ -5402,9 +5616,7 @@ function maybeCaptureRequestValue(
|
|
|
5402
5616
|
): TraceValueCaptureRef | undefined {
|
|
5403
5617
|
const result = createCapturedValueEntry(params);
|
|
5404
5618
|
if (!result) return undefined;
|
|
5405
|
-
|
|
5406
|
-
sink.push(result.entry);
|
|
5407
|
-
}
|
|
5619
|
+
appendCapturedValueDefinition(sink, result.entry);
|
|
5408
5620
|
return result.captureRef;
|
|
5409
5621
|
}
|
|
5410
5622
|
|
|
@@ -5416,12 +5628,22 @@ async function maybeCaptureRequestValueAsync(
|
|
|
5416
5628
|
capture: FullValueCaptureContext;
|
|
5417
5629
|
},
|
|
5418
5630
|
sink: TraceValueBatchEntry[],
|
|
5631
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
5419
5632
|
): Promise<TraceValueCaptureRef | undefined> {
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5633
|
+
let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
|
|
5634
|
+
try {
|
|
5635
|
+
result = await createCapturedValueEntryAsync(params);
|
|
5636
|
+
} catch {
|
|
5637
|
+
recordRequestCaptureFailure(
|
|
5638
|
+
captureScope ?? getCtx().captureScope,
|
|
5639
|
+
'privacy.full_value',
|
|
5640
|
+
params.target,
|
|
5641
|
+
'privacy_transform_failed',
|
|
5642
|
+
);
|
|
5643
|
+
return { status: 'failed-policy' };
|
|
5424
5644
|
}
|
|
5645
|
+
if (!result) return undefined;
|
|
5646
|
+
appendCapturedValueDefinition(sink, result.entry, captureScope);
|
|
5425
5647
|
return result.captureRef;
|
|
5426
5648
|
}
|
|
5427
5649
|
|
|
@@ -5445,8 +5667,19 @@ async function maybeCaptureTraceValueAsync(
|
|
|
5445
5667
|
capture: FullValueCaptureContext;
|
|
5446
5668
|
metadata?: Partial<TraceValueBatchEntry>;
|
|
5447
5669
|
},
|
|
5670
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
5448
5671
|
): Promise<{ captureRef: TraceValueCaptureRef; entry?: TraceValueBatchEntry } | undefined> {
|
|
5449
|
-
|
|
5672
|
+
try {
|
|
5673
|
+
return await createCapturedValueEntryAsync(params);
|
|
5674
|
+
} catch {
|
|
5675
|
+
recordRequestCaptureFailure(
|
|
5676
|
+
captureScope ?? getCtx().captureScope,
|
|
5677
|
+
'privacy.full_value',
|
|
5678
|
+
params.target,
|
|
5679
|
+
'privacy_transform_failed',
|
|
5680
|
+
);
|
|
5681
|
+
return { captureRef: { status: 'failed-policy' } };
|
|
5682
|
+
}
|
|
5450
5683
|
}
|
|
5451
5684
|
|
|
5452
5685
|
function maybeCaptureDbValue(
|
|
@@ -5460,9 +5693,7 @@ function maybeCaptureDbValue(
|
|
|
5460
5693
|
): TraceValueCaptureRef | undefined {
|
|
5461
5694
|
const result = createCapturedValueEntry(params);
|
|
5462
5695
|
if (!result) return undefined;
|
|
5463
|
-
|
|
5464
|
-
sink.push(result.entry);
|
|
5465
|
-
}
|
|
5696
|
+
appendCapturedValueDefinition(sink, result.entry);
|
|
5466
5697
|
return result.captureRef;
|
|
5467
5698
|
}
|
|
5468
5699
|
|
|
@@ -5474,12 +5705,22 @@ async function maybeCaptureDbValueAsync(
|
|
|
5474
5705
|
capture: FullValueCaptureContext;
|
|
5475
5706
|
},
|
|
5476
5707
|
sink: TraceValueBatchEntry[],
|
|
5708
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
5477
5709
|
): Promise<TraceValueCaptureRef | undefined> {
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5710
|
+
let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
|
|
5711
|
+
try {
|
|
5712
|
+
result = await createCapturedValueEntryAsync(params);
|
|
5713
|
+
} catch {
|
|
5714
|
+
recordRequestCaptureFailure(
|
|
5715
|
+
captureScope ?? getCtx().captureScope,
|
|
5716
|
+
'privacy.full_value',
|
|
5717
|
+
params.target,
|
|
5718
|
+
'privacy_transform_failed',
|
|
5719
|
+
);
|
|
5720
|
+
return { status: 'failed-policy' };
|
|
5482
5721
|
}
|
|
5722
|
+
if (!result) return undefined;
|
|
5723
|
+
appendCapturedValueDefinition(sink, result.entry, captureScope);
|
|
5483
5724
|
return result.captureRef;
|
|
5484
5725
|
}
|
|
5485
5726
|
|
|
@@ -5511,18 +5752,30 @@ async function maybeCaptureDbValueFromCandidatesAsync(
|
|
|
5511
5752
|
capture: FullValueCaptureContext;
|
|
5512
5753
|
},
|
|
5513
5754
|
sink: TraceValueBatchEntry[],
|
|
5755
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
5514
5756
|
): Promise<TraceValueCaptureRef | undefined> {
|
|
5515
5757
|
let skippedRef: TraceValueCaptureRef | undefined;
|
|
5516
5758
|
for (const rawValue of params.rawValues) {
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5759
|
+
let result: Awaited<ReturnType<typeof createCapturedValueEntryAsync>>;
|
|
5760
|
+
try {
|
|
5761
|
+
result = await createCapturedValueEntryAsync({
|
|
5762
|
+
target: params.target,
|
|
5763
|
+
rawValue,
|
|
5764
|
+
previewValue: params.previewValue,
|
|
5765
|
+
capture: params.capture,
|
|
5766
|
+
});
|
|
5767
|
+
} catch {
|
|
5768
|
+
recordRequestCaptureFailure(
|
|
5769
|
+
captureScope ?? getCtx().captureScope,
|
|
5770
|
+
'privacy.full_value',
|
|
5771
|
+
params.target,
|
|
5772
|
+
'privacy_transform_failed',
|
|
5773
|
+
);
|
|
5774
|
+
return { status: 'failed-policy' };
|
|
5775
|
+
}
|
|
5523
5776
|
if (!result) continue;
|
|
5524
5777
|
if (result.entry) {
|
|
5525
|
-
sink
|
|
5778
|
+
appendCapturedValueDefinition(sink, result.entry, captureScope);
|
|
5526
5779
|
return result.captureRef;
|
|
5527
5780
|
}
|
|
5528
5781
|
if (!skippedRef) {
|
|
@@ -5827,7 +6080,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
5827
6080
|
|
|
5828
6081
|
const clockSkewMs = headerTs !== null ? headerTs - requestStartRaw : 0;
|
|
5829
6082
|
const rid = nextRequestId(requestEpochMs);
|
|
5830
|
-
const captureScope = createRequestCaptureScope(rid, 'http');
|
|
6083
|
+
const captureScope = createRequestCaptureScope(rid, 'http', captureCfg);
|
|
5831
6084
|
emitRequestCaptureStart(captureCfg, sid, aid, captureScope, requestEpochMs);
|
|
5832
6085
|
const t0 = requestStartRaw;
|
|
5833
6086
|
const url = (req as any).originalUrl || req.url || '/';
|
|
@@ -6029,9 +6282,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6029
6282
|
endpointTraceCtx,
|
|
6030
6283
|
masking,
|
|
6031
6284
|
activePrivacy,
|
|
6285
|
+
undefined,
|
|
6286
|
+
captureScope,
|
|
6032
6287
|
);
|
|
6033
6288
|
const requestBody = requestBodyMaterialization.value;
|
|
6034
|
-
const
|
|
6289
|
+
const requestParamsMaterialization = await materializeInlinePrivacyValueAsync(
|
|
6035
6290
|
'request.params',
|
|
6036
6291
|
sanitizeRequestSnapshot((req as any).params),
|
|
6037
6292
|
captureCfg,
|
|
@@ -6039,8 +6294,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6039
6294
|
endpointTraceCtx,
|
|
6040
6295
|
masking,
|
|
6041
6296
|
activePrivacy,
|
|
6297
|
+
undefined,
|
|
6298
|
+
captureScope,
|
|
6042
6299
|
);
|
|
6043
|
-
const
|
|
6300
|
+
const requestParams = requestParamsMaterialization.value;
|
|
6301
|
+
const requestQueryMaterialization = await materializeInlinePrivacyValueAsync(
|
|
6044
6302
|
'request.query',
|
|
6045
6303
|
sanitizeRequestSnapshot((req as any).query),
|
|
6046
6304
|
captureCfg,
|
|
@@ -6048,8 +6306,11 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6048
6306
|
endpointTraceCtx,
|
|
6049
6307
|
masking,
|
|
6050
6308
|
activePrivacy,
|
|
6309
|
+
undefined,
|
|
6310
|
+
captureScope,
|
|
6051
6311
|
);
|
|
6052
|
-
const
|
|
6312
|
+
const requestQuery = requestQueryMaterialization.value;
|
|
6313
|
+
const requestHeadersMaterialization = await materializeInlinePrivacyValueAsync(
|
|
6053
6314
|
'request.headers',
|
|
6054
6315
|
requestHeaders,
|
|
6055
6316
|
captureCfg,
|
|
@@ -6057,7 +6318,10 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6057
6318
|
endpointTraceCtx,
|
|
6058
6319
|
masking,
|
|
6059
6320
|
activePrivacy,
|
|
6321
|
+
undefined,
|
|
6322
|
+
captureScope,
|
|
6060
6323
|
);
|
|
6324
|
+
const maskedHeaders = requestHeadersMaterialization.value;
|
|
6061
6325
|
const responseBodyMaterialization = capturedBody === undefined
|
|
6062
6326
|
? { value: undefined }
|
|
6063
6327
|
: limitRawInlinePrivacyValue('response.body', capturedBody)
|
|
@@ -6069,6 +6333,8 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6069
6333
|
endpointTraceCtx,
|
|
6070
6334
|
masking,
|
|
6071
6335
|
activePrivacy,
|
|
6336
|
+
undefined,
|
|
6337
|
+
captureScope,
|
|
6072
6338
|
);
|
|
6073
6339
|
const responseBody = responseBodyMaterialization.value;
|
|
6074
6340
|
const requestValueEntries: TraceValueBatchEntry[] = [];
|
|
@@ -6085,8 +6351,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6085
6351
|
trace: endpointTraceCtx,
|
|
6086
6352
|
masking,
|
|
6087
6353
|
privacy: activePrivacy,
|
|
6354
|
+
valueNamespace: sid,
|
|
6088
6355
|
},
|
|
6089
|
-
}, requestValueEntries);
|
|
6356
|
+
}, requestValueEntries, captureScope);
|
|
6090
6357
|
const paramsValueCapture = await maybeCaptureRequestValueAsync({
|
|
6091
6358
|
target: 'request.params',
|
|
6092
6359
|
rawValue: (req as any).params,
|
|
@@ -6098,8 +6365,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6098
6365
|
trace: endpointTraceCtx,
|
|
6099
6366
|
masking,
|
|
6100
6367
|
privacy: activePrivacy,
|
|
6368
|
+
valueNamespace: sid,
|
|
6101
6369
|
},
|
|
6102
|
-
}, requestValueEntries);
|
|
6370
|
+
}, requestValueEntries, captureScope);
|
|
6103
6371
|
const queryValueCapture = await maybeCaptureRequestValueAsync({
|
|
6104
6372
|
target: 'request.query',
|
|
6105
6373
|
rawValue: (req as any).query,
|
|
@@ -6111,8 +6379,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6111
6379
|
trace: endpointTraceCtx,
|
|
6112
6380
|
masking,
|
|
6113
6381
|
privacy: activePrivacy,
|
|
6382
|
+
valueNamespace: sid,
|
|
6114
6383
|
},
|
|
6115
|
-
}, requestValueEntries);
|
|
6384
|
+
}, requestValueEntries, captureScope);
|
|
6116
6385
|
const headersValueCapture = await maybeCaptureRequestValueAsync({
|
|
6117
6386
|
target: 'request.headers',
|
|
6118
6387
|
rawValue: req.headers,
|
|
@@ -6124,8 +6393,9 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6124
6393
|
trace: endpointTraceCtx,
|
|
6125
6394
|
masking,
|
|
6126
6395
|
privacy: activePrivacy,
|
|
6396
|
+
valueNamespace: sid,
|
|
6127
6397
|
},
|
|
6128
|
-
}, requestValueEntries);
|
|
6398
|
+
}, requestValueEntries, captureScope);
|
|
6129
6399
|
const respBodyValueCapture = capturedBody === undefined
|
|
6130
6400
|
? undefined
|
|
6131
6401
|
: await maybeCaptureRequestValueAsync({
|
|
@@ -6139,15 +6409,16 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6139
6409
|
trace: endpointTraceCtx,
|
|
6140
6410
|
masking,
|
|
6141
6411
|
privacy: activePrivacy,
|
|
6412
|
+
valueNamespace: sid,
|
|
6142
6413
|
},
|
|
6143
|
-
}, requestValueEntries);
|
|
6414
|
+
}, requestValueEntries, captureScope);
|
|
6144
6415
|
|
|
6145
6416
|
const responseDurationMs =
|
|
6146
6417
|
finishedAt !== null && Number.isFinite(finishedAt)
|
|
6147
6418
|
? Math.max(0, finishedAt - t0)
|
|
6148
6419
|
: Date.now() - t0;
|
|
6149
6420
|
const requestPayload: Record<string, any> = {
|
|
6150
|
-
|
|
6421
|
+
rid,
|
|
6151
6422
|
method: req.method,
|
|
6152
6423
|
url,
|
|
6153
6424
|
path,
|
|
@@ -6201,9 +6472,43 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6201
6472
|
t: requestEpochMs,
|
|
6202
6473
|
}]
|
|
6203
6474
|
});
|
|
6204
|
-
captureScope.backendRequestCount += 1;
|
|
6205
6475
|
} catch {
|
|
6206
|
-
|
|
6476
|
+
recordRequestCaptureFailure(
|
|
6477
|
+
captureScope,
|
|
6478
|
+
'request.capture',
|
|
6479
|
+
'backend_request',
|
|
6480
|
+
'request_materialization_failed',
|
|
6481
|
+
);
|
|
6482
|
+
// Preserve non-sensitive request metadata so the manifest cannot
|
|
6483
|
+
// silently turn a failed policy capture into a zero-request scope.
|
|
6484
|
+
post(captureCfg, sid, {
|
|
6485
|
+
entries: [{
|
|
6486
|
+
actionId: aid,
|
|
6487
|
+
request: {
|
|
6488
|
+
rid,
|
|
6489
|
+
method: req.method,
|
|
6490
|
+
url,
|
|
6491
|
+
path,
|
|
6492
|
+
status: res.statusCode,
|
|
6493
|
+
durMs:
|
|
6494
|
+
finishedAt !== null && Number.isFinite(finishedAt)
|
|
6495
|
+
? Math.max(0, finishedAt - t0)
|
|
6496
|
+
: Date.now() - t0,
|
|
6497
|
+
key,
|
|
6498
|
+
bodyMaterialization: {
|
|
6499
|
+
status: 'skipped',
|
|
6500
|
+
reason: 'request_materialization_failed',
|
|
6501
|
+
target: 'request.body',
|
|
6502
|
+
},
|
|
6503
|
+
respBodyMaterialization: {
|
|
6504
|
+
status: 'skipped',
|
|
6505
|
+
reason: 'request_materialization_failed',
|
|
6506
|
+
target: 'response.body',
|
|
6507
|
+
},
|
|
6508
|
+
},
|
|
6509
|
+
t: requestEpochMs,
|
|
6510
|
+
}],
|
|
6511
|
+
});
|
|
6207
6512
|
}
|
|
6208
6513
|
};
|
|
6209
6514
|
|
|
@@ -6326,6 +6631,8 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6326
6631
|
maskReq,
|
|
6327
6632
|
masking,
|
|
6328
6633
|
resolvePrivacy,
|
|
6634
|
+
captureScope,
|
|
6635
|
+
valueNamespace: sid,
|
|
6329
6636
|
});
|
|
6330
6637
|
const baseEvents = balanceTraceEvents(materializedEvents.slice());
|
|
6331
6638
|
const orderedEvents = TRACE_ORDER_MODE === 'tree'
|
|
@@ -6344,16 +6651,30 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6344
6651
|
}
|
|
6345
6652
|
|
|
6346
6653
|
} catch {
|
|
6654
|
+
recordRequestCaptureFailure(
|
|
6655
|
+
captureScope,
|
|
6656
|
+
'trace.capture',
|
|
6657
|
+
'trace.batches',
|
|
6658
|
+
'trace_capture_failed',
|
|
6659
|
+
);
|
|
6347
6660
|
captureScope.traceBatchCount = 0;
|
|
6348
6661
|
traceBatches = [];
|
|
6349
|
-
} finally {
|
|
6350
|
-
emitRequestCaptureManifest(traceBatches.length);
|
|
6351
6662
|
}
|
|
6352
6663
|
|
|
6664
|
+
// Finalize policy/materialization status before publishing the
|
|
6665
|
+
// high-priority manifest. The manifest must lead its evidence
|
|
6666
|
+
// on the wire so a large trace queue cannot starve completion.
|
|
6667
|
+
emitRequestCaptureManifest(traceBatches.length);
|
|
6668
|
+
|
|
6353
6669
|
try {
|
|
6670
|
+
const emittedValueIds = new Set<string>();
|
|
6354
6671
|
for (let i = 0; i < traceBatches.length; i++) {
|
|
6355
6672
|
const batch = traceBatches[i];
|
|
6356
|
-
const traceValues = collectBatchTraceValueEntries(
|
|
6673
|
+
const traceValues = collectBatchTraceValueEntries(
|
|
6674
|
+
batch,
|
|
6675
|
+
i,
|
|
6676
|
+
emittedValueIds,
|
|
6677
|
+
);
|
|
6357
6678
|
|
|
6358
6679
|
post(captureCfg, sid, {
|
|
6359
6680
|
entries: [{
|
|
@@ -6370,7 +6691,12 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
6370
6691
|
});
|
|
6371
6692
|
}
|
|
6372
6693
|
} catch {
|
|
6373
|
-
|
|
6694
|
+
recordRequestCaptureFailure(
|
|
6695
|
+
captureScope,
|
|
6696
|
+
'trace.enqueue',
|
|
6697
|
+
'trace.batches',
|
|
6698
|
+
'trace_enqueue_failed',
|
|
6699
|
+
);
|
|
6374
6700
|
}
|
|
6375
6701
|
};
|
|
6376
6702
|
}
|
|
@@ -6958,12 +7284,6 @@ function dehydrateComplexValue(value: any) {
|
|
|
6958
7284
|
}
|
|
6959
7285
|
} catch {}
|
|
6960
7286
|
|
|
6961
|
-
const ctor = (value as any)?.constructor?.name;
|
|
6962
|
-
if (ctor && ctor !== 'Object') {
|
|
6963
|
-
const plain = safeJson(value);
|
|
6964
|
-
if (plain !== undefined) return plain;
|
|
6965
|
-
}
|
|
6966
|
-
|
|
6967
7287
|
return value;
|
|
6968
7288
|
}
|
|
6969
7289
|
|
|
@@ -6971,6 +7291,11 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
|
|
|
6971
7291
|
const ctx = getCtx() as Ctx;
|
|
6972
7292
|
const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
|
|
6973
7293
|
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
7294
|
+
if (sid && ctx.captureScope) {
|
|
7295
|
+
// Count the observed database operation before asynchronous privacy
|
|
7296
|
+
// materialization. A later failure must remain visible to completeness.
|
|
7297
|
+
ctx.captureScope.dbChangeCount += 1;
|
|
7298
|
+
}
|
|
6974
7299
|
const runCtx: Ctx = {
|
|
6975
7300
|
sid: ctx.sid,
|
|
6976
7301
|
aid: ctx.aid,
|
|
@@ -6982,7 +7307,14 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
|
|
|
6982
7307
|
};
|
|
6983
7308
|
scheduleSdkBackgroundWork(async () => {
|
|
6984
7309
|
await als.run(runCtx, async () => {
|
|
6985
|
-
await emitDbQueryAsync(cfg, sid, aid, payload).catch(() =>
|
|
7310
|
+
await emitDbQueryAsync(cfg, sid, aid, payload).catch(() => {
|
|
7311
|
+
recordRequestCaptureFailure(
|
|
7312
|
+
runCtx.captureScope,
|
|
7313
|
+
'db.capture',
|
|
7314
|
+
'db.change',
|
|
7315
|
+
'db_capture_failed',
|
|
7316
|
+
);
|
|
7317
|
+
});
|
|
6986
7318
|
});
|
|
6987
7319
|
}, { priority: 'high' });
|
|
6988
7320
|
}
|
|
@@ -7026,6 +7358,7 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
|
|
|
7026
7358
|
masking: null,
|
|
7027
7359
|
privacy,
|
|
7028
7360
|
db: dbContext,
|
|
7361
|
+
valueNamespace: sid,
|
|
7029
7362
|
};
|
|
7030
7363
|
const dbValues: TraceValueBatchEntry[] = [];
|
|
7031
7364
|
const pkValueCapture = pkMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
|
|
@@ -7033,37 +7366,37 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
|
|
|
7033
7366
|
rawValue: payload.pk,
|
|
7034
7367
|
previewValue: pkPreview,
|
|
7035
7368
|
capture,
|
|
7036
|
-
}, dbValues);
|
|
7369
|
+
}, dbValues, ctx.captureScope);
|
|
7037
7370
|
const beforeValueCapture = beforeMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
|
|
7038
7371
|
target: 'db.before',
|
|
7039
7372
|
rawValue: payload.before,
|
|
7040
7373
|
previewValue: beforePreview,
|
|
7041
7374
|
capture,
|
|
7042
|
-
}, dbValues);
|
|
7375
|
+
}, dbValues, ctx.captureScope);
|
|
7043
7376
|
const afterValueCapture = afterMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
|
|
7044
7377
|
target: 'db.after',
|
|
7045
7378
|
rawValue: payload.after,
|
|
7046
7379
|
previewValue: afterPreview,
|
|
7047
7380
|
capture,
|
|
7048
|
-
}, dbValues);
|
|
7381
|
+
}, dbValues, ctx.captureScope);
|
|
7049
7382
|
const queryValueCapture = queryMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
|
|
7050
7383
|
target: 'db.query',
|
|
7051
7384
|
rawValue: querySource,
|
|
7052
7385
|
previewValue: queryPreview,
|
|
7053
7386
|
capture,
|
|
7054
|
-
}, dbValues);
|
|
7387
|
+
}, dbValues, ctx.captureScope);
|
|
7055
7388
|
const resultMetaValueCapture = resultMetaMaterialization.skipped ? undefined : await maybeCaptureDbValueFromCandidatesAsync({
|
|
7056
7389
|
target: 'db.resultMeta',
|
|
7057
7390
|
rawValues: buildResultMetaFullValueCandidates(payload.resultMetaRaw, resultMetaSource),
|
|
7058
7391
|
previewValue: resultMetaPreview,
|
|
7059
7392
|
capture,
|
|
7060
|
-
}, dbValues);
|
|
7393
|
+
}, dbValues, ctx.captureScope);
|
|
7061
7394
|
const errorValueCapture = errorMaterialization.skipped ? undefined : await maybeCaptureDbValueAsync({
|
|
7062
7395
|
target: 'db.error',
|
|
7063
7396
|
rawValue: payload.error,
|
|
7064
7397
|
previewValue: errorPreview,
|
|
7065
7398
|
capture,
|
|
7066
|
-
}, dbValues);
|
|
7399
|
+
}, dbValues, ctx.captureScope);
|
|
7067
7400
|
const dbEntry = attachSpanContext({
|
|
7068
7401
|
type: payload.type ?? 'mongo',
|
|
7069
7402
|
collection: payload.collection,
|
|
@@ -7097,8 +7430,13 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
|
|
|
7097
7430
|
t: payload.t,
|
|
7098
7431
|
}]
|
|
7099
7432
|
});
|
|
7100
|
-
if (enqueued
|
|
7101
|
-
|
|
7433
|
+
if (!enqueued) {
|
|
7434
|
+
recordRequestCaptureFailure(
|
|
7435
|
+
ctx.captureScope,
|
|
7436
|
+
'db.enqueue',
|
|
7437
|
+
'db.change',
|
|
7438
|
+
'db_enqueue_failed',
|
|
7439
|
+
);
|
|
7102
7440
|
}
|
|
7103
7441
|
}
|
|
7104
7442
|
|
|
@@ -7534,6 +7872,7 @@ async function materializeKafkaRequestBody(
|
|
|
7534
7872
|
cfg: ReproMiddlewareConfig,
|
|
7535
7873
|
maskReq: MaskRequestContext,
|
|
7536
7874
|
body: Record<string, any>,
|
|
7875
|
+
captureScope?: RequestCaptureScopeState | null,
|
|
7537
7876
|
): Promise<InlinePrivacyMaterializationResult> {
|
|
7538
7877
|
return materializeInlinePrivacyValueAsync(
|
|
7539
7878
|
'request.body',
|
|
@@ -7543,6 +7882,8 @@ async function materializeKafkaRequestBody(
|
|
|
7543
7882
|
null,
|
|
7544
7883
|
normalizeMaskingConfig(cfg.masking),
|
|
7545
7884
|
getRuntimePrivacyState(cfg).policy ?? null,
|
|
7885
|
+
undefined,
|
|
7886
|
+
captureScope,
|
|
7546
7887
|
);
|
|
7547
7888
|
}
|
|
7548
7889
|
|
|
@@ -7660,6 +8001,7 @@ function recordKafkaTraceEvent(
|
|
|
7660
8001
|
trace: candidate,
|
|
7661
8002
|
masking,
|
|
7662
8003
|
privacy,
|
|
8004
|
+
valueNamespace: getCtx().sid,
|
|
7663
8005
|
};
|
|
7664
8006
|
const traceValueMetadata = {
|
|
7665
8007
|
fn: evt.fn,
|
|
@@ -7828,6 +8170,7 @@ async function recordKafkaTraceEventAsync(
|
|
|
7828
8170
|
maskReq,
|
|
7829
8171
|
masking,
|
|
7830
8172
|
resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
|
|
8173
|
+
valueNamespace: getCtx().sid,
|
|
7831
8174
|
});
|
|
7832
8175
|
sink.push(...materializedEvents);
|
|
7833
8176
|
}
|
|
@@ -7848,10 +8191,11 @@ function buildKafkaTraceEntries(
|
|
|
7848
8191
|
requestRid,
|
|
7849
8192
|
actionId,
|
|
7850
8193
|
);
|
|
8194
|
+
const emittedValueIds = new Set<string>();
|
|
7851
8195
|
return batches.map((batch, index) => ({
|
|
7852
8196
|
actionId: actionId ?? null,
|
|
7853
8197
|
trace: batch,
|
|
7854
|
-
traceValues: collectBatchTraceValueEntries(batch, index),
|
|
8198
|
+
traceValues: collectBatchTraceValueEntries(batch, index, emittedValueIds),
|
|
7855
8199
|
traceBatch: {
|
|
7856
8200
|
rid: requestRid,
|
|
7857
8201
|
index,
|
|
@@ -7882,22 +8226,32 @@ function scheduleKafkaTraceFlush(
|
|
|
7882
8226
|
const evt = buildPendingKafkaTraceEvent(rawTraceEvent);
|
|
7883
8227
|
if (evt) pendingEvents.push(evt);
|
|
7884
8228
|
}
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
8229
|
+
let entries: LegacyEntry[] = [];
|
|
8230
|
+
try {
|
|
8231
|
+
if (pendingEvents.length) {
|
|
8232
|
+
const masking = normalizeMaskingConfig(cfg.masking);
|
|
8233
|
+
const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
|
|
8234
|
+
const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
|
|
8235
|
+
cfg,
|
|
8236
|
+
maskReq,
|
|
8237
|
+
masking,
|
|
8238
|
+
resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
|
|
8239
|
+
captureScope,
|
|
8240
|
+
valueNamespace: sid,
|
|
8241
|
+
});
|
|
8242
|
+
entries = buildKafkaTraceEntries(actionId, requestRid, materializedEvents);
|
|
8243
|
+
}
|
|
8244
|
+
} catch {
|
|
8245
|
+
recordRequestCaptureFailure(
|
|
8246
|
+
captureScope,
|
|
8247
|
+
'trace.capture',
|
|
8248
|
+
'trace.batches',
|
|
8249
|
+
'trace_capture_failed',
|
|
8250
|
+
);
|
|
8251
|
+
}
|
|
7895
8252
|
if (captureScope) {
|
|
7896
8253
|
captureScope.traceBatchCount = entries.length;
|
|
7897
8254
|
}
|
|
7898
|
-
if (entries.length) {
|
|
7899
|
-
post(cfg, sid, { entries });
|
|
7900
|
-
}
|
|
7901
8255
|
if (captureScope) {
|
|
7902
8256
|
post(cfg, sid, {
|
|
7903
8257
|
entries: [{
|
|
@@ -7909,7 +8263,10 @@ function scheduleKafkaTraceFlush(
|
|
|
7909
8263
|
),
|
|
7910
8264
|
t: alignedNow(),
|
|
7911
8265
|
}],
|
|
7912
|
-
});
|
|
8266
|
+
}, { priority: 'high' });
|
|
8267
|
+
}
|
|
8268
|
+
if (entries.length) {
|
|
8269
|
+
post(cfg, sid, { entries });
|
|
7913
8270
|
}
|
|
7914
8271
|
});
|
|
7915
8272
|
}, { priority: 'high' });
|
|
@@ -7928,7 +8285,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
7928
8285
|
const aid = ctx.aid ?? fallback.aid ?? null;
|
|
7929
8286
|
const span = captureSpanContextFromTracer();
|
|
7930
8287
|
const publishRid = nextRequestId(alignedNow());
|
|
7931
|
-
const captureScope = createRequestCaptureScope(publishRid, 'kafka');
|
|
8288
|
+
const captureScope = createRequestCaptureScope(publishRid, 'kafka', cfg);
|
|
7932
8289
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
7933
8290
|
const payloadObj = payload && typeof payload === 'object' ? payload : {};
|
|
7934
8291
|
const topic = sanitizeKafkaTopic((payloadObj as any).topic);
|
|
@@ -7987,7 +8344,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
7987
8344
|
traceId: span?.traceId ?? null,
|
|
7988
8345
|
spanId: span?.spanId ?? null,
|
|
7989
8346
|
parentSpanId: span?.parentSpanId ?? null,
|
|
7990
|
-
});
|
|
8347
|
+
}, captureScope);
|
|
7991
8348
|
|
|
7992
8349
|
const requestPayload: Record<string, any> = {
|
|
7993
8350
|
rid: publishRid,
|
|
@@ -8018,7 +8375,6 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
8018
8375
|
},
|
|
8019
8376
|
],
|
|
8020
8377
|
});
|
|
8021
|
-
captureScope.backendRequestCount += 1;
|
|
8022
8378
|
post(cfg, sid, {
|
|
8023
8379
|
entries: [{
|
|
8024
8380
|
actionId: aid ?? null,
|
|
@@ -8043,7 +8399,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
8043
8399
|
const aid = ctx.aid ?? fallback.aid ?? null;
|
|
8044
8400
|
const span = captureSpanContextFromTracer();
|
|
8045
8401
|
const publishRid = nextRequestId(alignedNow());
|
|
8046
|
-
const captureScope = createRequestCaptureScope(publishRid, 'kafka');
|
|
8402
|
+
const captureScope = createRequestCaptureScope(publishRid, 'kafka', cfg);
|
|
8047
8403
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
8048
8404
|
|
|
8049
8405
|
const payloadObj = payload && typeof payload === 'object' ? payload : {};
|
|
@@ -8137,7 +8493,7 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
8137
8493
|
traceId: span?.traceId ?? null,
|
|
8138
8494
|
spanId: span?.spanId ?? null,
|
|
8139
8495
|
parentSpanId: span?.parentSpanId ?? null,
|
|
8140
|
-
});
|
|
8496
|
+
}, captureScope);
|
|
8141
8497
|
const requestPayload: Record<string, any> = {
|
|
8142
8498
|
rid: publishRid,
|
|
8143
8499
|
method: 'KAFKA_PUBLISH_BATCH',
|
|
@@ -8167,7 +8523,6 @@ function patchKafkaProducerInstance(producer: any, cfg: KafkaJsPatchConfig) {
|
|
|
8167
8523
|
},
|
|
8168
8524
|
],
|
|
8169
8525
|
});
|
|
8170
|
-
captureScope.backendRequestCount += 1;
|
|
8171
8526
|
post(cfg, sid, {
|
|
8172
8527
|
entries: [{
|
|
8173
8528
|
actionId: aid ?? null,
|
|
@@ -8194,7 +8549,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
|
|
|
8194
8549
|
const aid = runtime.aid;
|
|
8195
8550
|
const consumeRid = nextRequestId(`${topic}-${message?.offset ?? alignedNow()}`);
|
|
8196
8551
|
const traceId = consumeRid;
|
|
8197
|
-
const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
|
|
8552
|
+
const captureScope = createRequestCaptureScope(consumeRid, 'kafka', cfg);
|
|
8198
8553
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
8199
8554
|
const rawTraceEvents: any[] = [];
|
|
8200
8555
|
const maskReq: MaskRequestContext = {
|
|
@@ -8266,7 +8621,7 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
|
|
|
8266
8621
|
valueEncoding: messagePayload.valueEncoding,
|
|
8267
8622
|
valueBytes: messagePayload.valueBytes,
|
|
8268
8623
|
message: messagePayload,
|
|
8269
|
-
});
|
|
8624
|
+
}, captureScope);
|
|
8270
8625
|
const requestPayload: Record<string, any> = {
|
|
8271
8626
|
rid: consumeRid,
|
|
8272
8627
|
method: 'KAFKA_CONSUME',
|
|
@@ -8288,7 +8643,6 @@ function wrapKafkaEachMessageHandler(eachMessage: any, cfg: KafkaJsPatchConfig,
|
|
|
8288
8643
|
t: alignedNow(),
|
|
8289
8644
|
}],
|
|
8290
8645
|
});
|
|
8291
|
-
captureScope.backendRequestCount += 1;
|
|
8292
8646
|
scheduleKafkaTraceFlush(cfg, kafkaCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
|
|
8293
8647
|
}
|
|
8294
8648
|
};
|
|
@@ -8305,7 +8659,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
|
|
|
8305
8659
|
const aid = runtime.aid;
|
|
8306
8660
|
const consumeRid = nextRequestId(`${topic}-batch-${alignedNow()}`);
|
|
8307
8661
|
const traceId = consumeRid;
|
|
8308
|
-
const captureScope = createRequestCaptureScope(consumeRid, 'kafka');
|
|
8662
|
+
const captureScope = createRequestCaptureScope(consumeRid, 'kafka', cfg);
|
|
8309
8663
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
8310
8664
|
const rawTraceEvents: any[] = [];
|
|
8311
8665
|
const maskReq: MaskRequestContext = {
|
|
@@ -8375,7 +8729,7 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
|
|
|
8375
8729
|
parentSpanId: runtime.parentSpanId,
|
|
8376
8730
|
firstMessageKey: firstMessagePayload?.key ?? null,
|
|
8377
8731
|
messages: messages.map((message: any) => buildKafkaMessagePayload(message)),
|
|
8378
|
-
});
|
|
8732
|
+
}, captureScope);
|
|
8379
8733
|
const requestPayload: Record<string, any> = {
|
|
8380
8734
|
rid: consumeRid,
|
|
8381
8735
|
method: 'KAFKA_CONSUME_BATCH',
|
|
@@ -8397,7 +8751,6 @@ function wrapKafkaEachBatchHandler(eachBatch: any, cfg: KafkaJsPatchConfig, cons
|
|
|
8397
8751
|
t: alignedNow(),
|
|
8398
8752
|
}],
|
|
8399
8753
|
});
|
|
8400
|
-
captureScope.backendRequestCount += 1;
|
|
8401
8754
|
scheduleKafkaTraceFlush(cfg, kafkaCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
|
|
8402
8755
|
}
|
|
8403
8756
|
};
|
|
@@ -8820,24 +9173,28 @@ function scheduleAmqpTraceFlush(
|
|
|
8820
9173
|
}
|
|
8821
9174
|
|
|
8822
9175
|
let entries: LegacyEntry[] = [];
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
post(cfg, sid, { entries });
|
|
9176
|
+
try {
|
|
9177
|
+
if (pendingEvents.length) {
|
|
9178
|
+
const masking = normalizeMaskingConfig(cfg.masking);
|
|
9179
|
+
const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
|
|
9180
|
+
const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
|
|
9181
|
+
cfg,
|
|
9182
|
+
maskReq,
|
|
9183
|
+
masking,
|
|
9184
|
+
resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
|
|
9185
|
+
captureScope,
|
|
9186
|
+
});
|
|
9187
|
+
entries = buildAmqpTraceEntries(actionId, requestRid, materializedEvents);
|
|
8836
9188
|
}
|
|
8837
|
-
}
|
|
8838
|
-
|
|
9189
|
+
} catch {
|
|
9190
|
+
recordRequestCaptureFailure(
|
|
9191
|
+
captureScope,
|
|
9192
|
+
'trace.capture',
|
|
9193
|
+
'trace.batches',
|
|
9194
|
+
'trace_capture_failed',
|
|
9195
|
+
);
|
|
8839
9196
|
}
|
|
8840
|
-
|
|
9197
|
+
captureScope.traceBatchCount = entries.length;
|
|
8841
9198
|
post(cfg, sid, {
|
|
8842
9199
|
entries: [{
|
|
8843
9200
|
actionId: actionId ?? null,
|
|
@@ -8848,7 +9205,10 @@ function scheduleAmqpTraceFlush(
|
|
|
8848
9205
|
),
|
|
8849
9206
|
t: alignedNow(),
|
|
8850
9207
|
}],
|
|
8851
|
-
});
|
|
9208
|
+
}, { priority: 'high' });
|
|
9209
|
+
if (entries.length) {
|
|
9210
|
+
post(cfg, sid, { entries });
|
|
9211
|
+
}
|
|
8852
9212
|
});
|
|
8853
9213
|
}, { priority: 'high' });
|
|
8854
9214
|
}
|
|
@@ -8920,7 +9280,12 @@ function scheduleAmqpPublishCapture(params: {
|
|
|
8920
9280
|
};
|
|
8921
9281
|
|
|
8922
9282
|
scheduleSdkBackgroundWork(async () => {
|
|
8923
|
-
const bodyMaterialization = await materializeKafkaRequestBody(
|
|
9283
|
+
const bodyMaterialization = await materializeKafkaRequestBody(
|
|
9284
|
+
params.cfg,
|
|
9285
|
+
maskReq,
|
|
9286
|
+
body,
|
|
9287
|
+
params.captureScope,
|
|
9288
|
+
);
|
|
8924
9289
|
const requestPayload: Record<string, any> = {
|
|
8925
9290
|
rid: params.publishRid,
|
|
8926
9291
|
method: params.method,
|
|
@@ -8951,7 +9316,6 @@ function scheduleAmqpPublishCapture(params: {
|
|
|
8951
9316
|
t: alignedNow(),
|
|
8952
9317
|
}],
|
|
8953
9318
|
});
|
|
8954
|
-
params.captureScope.backendRequestCount += 1;
|
|
8955
9319
|
post(params.cfg, params.sid!, {
|
|
8956
9320
|
entries: [{
|
|
8957
9321
|
actionId: params.aid ?? null,
|
|
@@ -9013,7 +9377,7 @@ function patchAmqplibPublishMethod(
|
|
|
9013
9377
|
: `amqp://queue/${queue}`;
|
|
9014
9378
|
const key = `${method} ${methodName === 'publish' ? `${exchange || 'default'}/${routingKey}` : queue}`;
|
|
9015
9379
|
const content = methodName === 'publish' ? args[2] : args[1];
|
|
9016
|
-
const captureScope = createRequestCaptureScope(publishRid, 'amqp');
|
|
9380
|
+
const captureScope = createRequestCaptureScope(publishRid, 'amqp', cfg);
|
|
9017
9381
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
9018
9382
|
const t0 = Date.now();
|
|
9019
9383
|
let finished = false;
|
|
@@ -9103,7 +9467,7 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
|
|
|
9103
9467
|
const deliveryTag = message?.fields?.deliveryTag ?? alignedNow();
|
|
9104
9468
|
const consumeRid = nextRequestId(`${queueName}-${deliveryTag}`);
|
|
9105
9469
|
const traceId = consumeRid;
|
|
9106
|
-
const captureScope = createRequestCaptureScope(consumeRid, 'amqp');
|
|
9470
|
+
const captureScope = createRequestCaptureScope(consumeRid, 'amqp', cfg);
|
|
9107
9471
|
emitRequestCaptureStart(cfg, sid, aid, captureScope);
|
|
9108
9472
|
const rawTraceEvents: any[] = [];
|
|
9109
9473
|
const maskReq: MaskRequestContext = {
|
|
@@ -9188,7 +9552,12 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
|
|
|
9188
9552
|
};
|
|
9189
9553
|
|
|
9190
9554
|
scheduleSdkBackgroundWork(async () => {
|
|
9191
|
-
const bodyMaterialization = await materializeKafkaRequestBody(
|
|
9555
|
+
const bodyMaterialization = await materializeKafkaRequestBody(
|
|
9556
|
+
cfg,
|
|
9557
|
+
maskReq,
|
|
9558
|
+
body,
|
|
9559
|
+
captureScope,
|
|
9560
|
+
);
|
|
9192
9561
|
const requestPayload: Record<string, any> = {
|
|
9193
9562
|
rid: consumeRid,
|
|
9194
9563
|
method: 'AMQP_CONSUME',
|
|
@@ -9210,7 +9579,6 @@ function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queu
|
|
|
9210
9579
|
t: alignedNow(),
|
|
9211
9580
|
}],
|
|
9212
9581
|
});
|
|
9213
|
-
captureScope.backendRequestCount += 1;
|
|
9214
9582
|
scheduleAmqpTraceFlush(cfg, amqpCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
|
|
9215
9583
|
}, { priority: 'high' });
|
|
9216
9584
|
};
|
|
@@ -11526,5 +11894,6 @@ export const __reproTestHooks = {
|
|
|
11526
11894
|
estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
|
|
11527
11895
|
sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
|
|
11528
11896
|
sanitizeMaterializedTraceValueForTest: sanitizeMaterializedTraceValue,
|
|
11897
|
+
projectSemanticInfrastructureValueForTest: projectSemanticInfrastructureValue,
|
|
11529
11898
|
getEventTraceValueEntriesForTest: getEventTraceValueEntries,
|
|
11530
11899
|
};
|