repro-nest 0.0.212 → 0.0.214
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.js +59 -3
- package/package.json +1 -1
- package/src/index.ts +51 -4
- package/tracer/runtime.js +7 -1
package/dist/index.js
CHANGED
|
@@ -491,6 +491,26 @@ function isReproTracingEnabled() { return __TRACER_READY; }
|
|
|
491
491
|
exports.isReproTracingEnabled = isReproTracingEnabled;
|
|
492
492
|
function captureSpanContextFromTracer(source) {
|
|
493
493
|
try {
|
|
494
|
+
// Prefer a preserved store captured at the call-site for thenables (e.g., Mongoose Query),
|
|
495
|
+
// because the tracer intentionally detaches spans before the query is actually executed.
|
|
496
|
+
const promiseStore = source && source[Symbol.for('__repro_promise_store')];
|
|
497
|
+
if (promiseStore) {
|
|
498
|
+
const stack = Array.isArray(promiseStore.__repro_span_stack) ? promiseStore.__repro_span_stack : [];
|
|
499
|
+
const top = stack.length ? stack[stack.length - 1] : null;
|
|
500
|
+
const spanId = top && top.id != null ? top.id : null;
|
|
501
|
+
const parentSpanId = top && top.parentId != null
|
|
502
|
+
? top.parentId
|
|
503
|
+
: (stack.length >= 2 ? (stack[stack.length - 2]?.id ?? null) : null);
|
|
504
|
+
const depth = top && top.depth != null
|
|
505
|
+
? top.depth
|
|
506
|
+
: (typeof promiseStore.depth === 'number'
|
|
507
|
+
? promiseStore.depth
|
|
508
|
+
: (stack.length ? stack.length : null));
|
|
509
|
+
const traceId = promiseStore.traceId ?? null;
|
|
510
|
+
if (traceId || spanId !== null || parentSpanId !== null) {
|
|
511
|
+
return { traceId, spanId, parentSpanId, depth: depth == null ? null : depth };
|
|
512
|
+
}
|
|
513
|
+
}
|
|
494
514
|
const fromSource = source && source.__repro_span_context;
|
|
495
515
|
if (fromSource) {
|
|
496
516
|
return {
|
|
@@ -522,6 +542,26 @@ function captureSpanContextFromTracer(source) {
|
|
|
522
542
|
catch { }
|
|
523
543
|
return null;
|
|
524
544
|
}
|
|
545
|
+
function isExcludedSpanId(spanId) {
|
|
546
|
+
if (spanId === null || spanId === undefined)
|
|
547
|
+
return false;
|
|
548
|
+
try {
|
|
549
|
+
const excluded = getCtx().excludedSpanIds;
|
|
550
|
+
if (!excluded || excluded.size === 0)
|
|
551
|
+
return false;
|
|
552
|
+
return excluded.has(String(spanId));
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function shouldCaptureDbSpan(span) {
|
|
559
|
+
if (!span || span.spanId === null || span.spanId === undefined)
|
|
560
|
+
return false;
|
|
561
|
+
if (isExcludedSpanId(span.spanId))
|
|
562
|
+
return false;
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
525
565
|
function attachSpanContext(target, span) {
|
|
526
566
|
if (!target)
|
|
527
567
|
return target;
|
|
@@ -1274,7 +1314,7 @@ function reproMiddleware(cfg) {
|
|
|
1274
1314
|
}
|
|
1275
1315
|
return fn();
|
|
1276
1316
|
};
|
|
1277
|
-
runInTrace(() => als.run({ sid, aid, clockSkewMs }, () => {
|
|
1317
|
+
runInTrace(() => als.run({ sid, aid, clockSkewMs, excludedSpanIds: new Set() }, () => {
|
|
1278
1318
|
const events = [];
|
|
1279
1319
|
let endpointTrace = null;
|
|
1280
1320
|
let preferredAppTrace = null;
|
|
@@ -1423,6 +1463,7 @@ function reproMiddleware(cfg) {
|
|
|
1423
1463
|
depth: evt.depth,
|
|
1424
1464
|
library: inferLibraryNameFromFile(evt.file),
|
|
1425
1465
|
};
|
|
1466
|
+
const dropEvent = shouldDropTraceEvent(candidate);
|
|
1426
1467
|
const spanKey = normalizeSpanId(evt.spanId);
|
|
1427
1468
|
if (evt.type === 'enter') {
|
|
1428
1469
|
lastEventAt = Date.now();
|
|
@@ -1442,7 +1483,13 @@ function reproMiddleware(cfg) {
|
|
|
1442
1483
|
anonymousSpanDepth = Math.max(0, anonymousSpanDepth - 1);
|
|
1443
1484
|
}
|
|
1444
1485
|
}
|
|
1445
|
-
if (
|
|
1486
|
+
if (dropEvent) {
|
|
1487
|
+
if (evt.type === 'enter' && spanKey) {
|
|
1488
|
+
try {
|
|
1489
|
+
getCtx().excludedSpanIds?.add(spanKey);
|
|
1490
|
+
}
|
|
1491
|
+
catch { }
|
|
1492
|
+
}
|
|
1446
1493
|
if (finished) {
|
|
1447
1494
|
scheduleIdleFlush();
|
|
1448
1495
|
}
|
|
@@ -1626,6 +1673,8 @@ function reproMongoosePlugin(cfg) {
|
|
|
1626
1673
|
const after = this.toObject({ depopulate: true });
|
|
1627
1674
|
const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
|
|
1628
1675
|
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
1676
|
+
if (!shouldCaptureDbSpan(spanContext))
|
|
1677
|
+
return;
|
|
1629
1678
|
const query = meta.wasNew
|
|
1630
1679
|
? { op: 'insertOne', doc: after }
|
|
1631
1680
|
: { filter: { _id: this._id }, update: buildMinimalUpdate(before, after), options: { upsert: false } };
|
|
@@ -1668,6 +1717,8 @@ function reproMongoosePlugin(cfg) {
|
|
|
1668
1717
|
const after = res ?? null;
|
|
1669
1718
|
const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
1670
1719
|
const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
|
|
1720
|
+
if (!shouldCaptureDbSpan(spanContext))
|
|
1721
|
+
return;
|
|
1671
1722
|
const pk = after?._id ?? before?._id;
|
|
1672
1723
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
|
|
1673
1724
|
entries: [{
|
|
@@ -1708,6 +1759,8 @@ function reproMongoosePlugin(cfg) {
|
|
|
1708
1759
|
const collection = this.__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
1709
1760
|
const filter = this.__repro_filter ?? { _id: before._id };
|
|
1710
1761
|
const spanContext = this.__repro_spanContext || captureSpanContextFromTracer(this);
|
|
1762
|
+
if (!shouldCaptureDbSpan(spanContext))
|
|
1763
|
+
return;
|
|
1711
1764
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, getCtx().sid, {
|
|
1712
1765
|
entries: [{
|
|
1713
1766
|
actionId: getCtx().aid,
|
|
@@ -2019,6 +2072,9 @@ function dehydrateComplexValue(value) {
|
|
|
2019
2072
|
function emitDbQuery(cfg, sid, aid, payload) {
|
|
2020
2073
|
if (!sid)
|
|
2021
2074
|
return;
|
|
2075
|
+
const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
|
|
2076
|
+
if (!shouldCaptureDbSpan(spanContext))
|
|
2077
|
+
return;
|
|
2022
2078
|
const dbEntry = attachSpanContext({
|
|
2023
2079
|
collection: payload.collection,
|
|
2024
2080
|
op: payload.op,
|
|
@@ -2027,7 +2083,7 @@ function emitDbQuery(cfg, sid, aid, payload) {
|
|
|
2027
2083
|
durMs: payload.durMs ?? undefined,
|
|
2028
2084
|
pk: null, before: null, after: null,
|
|
2029
2085
|
error: payload.error ?? undefined,
|
|
2030
|
-
},
|
|
2086
|
+
}, spanContext);
|
|
2031
2087
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
|
|
2032
2088
|
entries: [{
|
|
2033
2089
|
actionId: aid ?? null,
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -684,6 +684,27 @@ export function isReproTracingEnabled() { return __TRACER_READY; }
|
|
|
684
684
|
|
|
685
685
|
function captureSpanContextFromTracer(source?: any): SpanContext | null {
|
|
686
686
|
try {
|
|
687
|
+
// Prefer a preserved store captured at the call-site for thenables (e.g., Mongoose Query),
|
|
688
|
+
// because the tracer intentionally detaches spans before the query is actually executed.
|
|
689
|
+
const promiseStore = source && source[Symbol.for('__repro_promise_store')];
|
|
690
|
+
if (promiseStore) {
|
|
691
|
+
const stack = Array.isArray(promiseStore.__repro_span_stack) ? promiseStore.__repro_span_stack : [];
|
|
692
|
+
const top = stack.length ? stack[stack.length - 1] : null;
|
|
693
|
+
const spanId = top && top.id != null ? top.id : null;
|
|
694
|
+
const parentSpanId = top && top.parentId != null
|
|
695
|
+
? top.parentId
|
|
696
|
+
: (stack.length >= 2 ? (stack[stack.length - 2]?.id ?? null) : null);
|
|
697
|
+
const depth = top && top.depth != null
|
|
698
|
+
? top.depth
|
|
699
|
+
: (typeof promiseStore.depth === 'number'
|
|
700
|
+
? promiseStore.depth
|
|
701
|
+
: (stack.length ? stack.length : null));
|
|
702
|
+
const traceId = promiseStore.traceId ?? null;
|
|
703
|
+
if (traceId || spanId !== null || parentSpanId !== null) {
|
|
704
|
+
return { traceId, spanId, parentSpanId, depth: depth == null ? null : depth };
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
687
708
|
const fromSource = source && source.__repro_span_context;
|
|
688
709
|
if (fromSource) {
|
|
689
710
|
return {
|
|
@@ -715,6 +736,23 @@ function captureSpanContextFromTracer(source?: any): SpanContext | null {
|
|
|
715
736
|
return null;
|
|
716
737
|
}
|
|
717
738
|
|
|
739
|
+
function isExcludedSpanId(spanId: string | number | null | undefined): boolean {
|
|
740
|
+
if (spanId === null || spanId === undefined) return false;
|
|
741
|
+
try {
|
|
742
|
+
const excluded = (getCtx() as Ctx).excludedSpanIds;
|
|
743
|
+
if (!excluded || excluded.size === 0) return false;
|
|
744
|
+
return excluded.has(String(spanId));
|
|
745
|
+
} catch {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function shouldCaptureDbSpan(span: SpanContext | null | undefined): span is SpanContext {
|
|
751
|
+
if (!span || span.spanId === null || span.spanId === undefined) return false;
|
|
752
|
+
if (isExcludedSpanId(span.spanId)) return false;
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
|
|
718
756
|
function attachSpanContext<T extends Record<string, any>>(target: T, span?: SpanContext | null): T {
|
|
719
757
|
if (!target) return target;
|
|
720
758
|
const ctx = span ?? captureSpanContextFromTracer();
|
|
@@ -724,7 +762,7 @@ function attachSpanContext<T extends Record<string, any>>(target: T, span?: Span
|
|
|
724
762
|
return target;
|
|
725
763
|
}
|
|
726
764
|
|
|
727
|
-
type Ctx = { sid?: string; aid?: string; clockSkewMs?: number };
|
|
765
|
+
type Ctx = { sid?: string; aid?: string; clockSkewMs?: number; excludedSpanIds?: Set<string> };
|
|
728
766
|
const als = new AsyncLocalStorage<Ctx>();
|
|
729
767
|
const getCtx = () => als.getStore() || {};
|
|
730
768
|
|
|
@@ -1504,7 +1542,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
1504
1542
|
return fn();
|
|
1505
1543
|
};
|
|
1506
1544
|
|
|
1507
|
-
runInTrace(() => als.run({ sid, aid, clockSkewMs }, () => {
|
|
1545
|
+
runInTrace(() => als.run({ sid, aid, clockSkewMs, excludedSpanIds: new Set<string>() }, () => {
|
|
1508
1546
|
const events: TraceEventRecord[] = [];
|
|
1509
1547
|
let endpointTrace: EndpointTraceInfo | null = null;
|
|
1510
1548
|
let preferredAppTrace: EndpointTraceInfo | null = null;
|
|
@@ -1639,6 +1677,7 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
1639
1677
|
library: inferLibraryNameFromFile(evt.file),
|
|
1640
1678
|
};
|
|
1641
1679
|
|
|
1680
|
+
const dropEvent = shouldDropTraceEvent(candidate);
|
|
1642
1681
|
const spanKey = normalizeSpanId(evt.spanId);
|
|
1643
1682
|
if (evt.type === 'enter') {
|
|
1644
1683
|
lastEventAt = Date.now();
|
|
@@ -1656,7 +1695,10 @@ export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
|
1656
1695
|
}
|
|
1657
1696
|
}
|
|
1658
1697
|
|
|
1659
|
-
if (
|
|
1698
|
+
if (dropEvent) {
|
|
1699
|
+
if (evt.type === 'enter' && spanKey) {
|
|
1700
|
+
try { (getCtx() as Ctx).excludedSpanIds?.add(spanKey); } catch {}
|
|
1701
|
+
}
|
|
1660
1702
|
if (finished) {
|
|
1661
1703
|
scheduleIdleFlush();
|
|
1662
1704
|
}
|
|
@@ -1844,6 +1886,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
|
|
|
1844
1886
|
const after = this.toObject({ depopulate: true });
|
|
1845
1887
|
const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
|
|
1846
1888
|
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
1889
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
1847
1890
|
|
|
1848
1891
|
const query = meta.wasNew
|
|
1849
1892
|
? { op: 'insertOne', doc: after }
|
|
@@ -1888,6 +1931,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
|
|
|
1888
1931
|
const after = res ?? null;
|
|
1889
1932
|
const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
1890
1933
|
const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
|
|
1934
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
1891
1935
|
const pk = after?._id ?? before?._id;
|
|
1892
1936
|
|
|
1893
1937
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
|
|
@@ -1925,6 +1969,7 @@ export function reproMongoosePlugin(cfg: { appId: string; tenantId: string; appS
|
|
|
1925
1969
|
const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
1926
1970
|
const filter = (this as any).__repro_filter ?? { _id: before._id };
|
|
1927
1971
|
const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
|
|
1972
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
1928
1973
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, (getCtx() as Ctx).sid!, {
|
|
1929
1974
|
entries: [{
|
|
1930
1975
|
actionId: (getCtx() as Ctx).aid!,
|
|
@@ -2240,6 +2285,8 @@ function dehydrateComplexValue(value: any) {
|
|
|
2240
2285
|
|
|
2241
2286
|
function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
|
|
2242
2287
|
if (!sid) return;
|
|
2288
|
+
const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
|
|
2289
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
2243
2290
|
const dbEntry = attachSpanContext({
|
|
2244
2291
|
collection: payload.collection,
|
|
2245
2292
|
op: payload.op,
|
|
@@ -2248,7 +2295,7 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
|
|
|
2248
2295
|
durMs: payload.durMs ?? undefined,
|
|
2249
2296
|
pk: null, before: null, after: null,
|
|
2250
2297
|
error: payload.error ?? undefined,
|
|
2251
|
-
},
|
|
2298
|
+
}, spanContext);
|
|
2252
2299
|
post(cfg.apiBase, cfg.tenantId, cfg.appId, cfg.appSecret, sid, {
|
|
2253
2300
|
entries: [{
|
|
2254
2301
|
actionId: aid ?? null,
|
package/tracer/runtime.js
CHANGED
|
@@ -545,7 +545,13 @@ if (!global.__repro_call) {
|
|
|
545
545
|
const perCallStore = cloneStore(baseStoreSnapshot);
|
|
546
546
|
let result;
|
|
547
547
|
als.run(perCallStore, () => {
|
|
548
|
-
|
|
548
|
+
// Support both callbacks and constructors: some libraries (e.g., class-transformer)
|
|
549
|
+
// pass class constructors as args and invoke them with `new`.
|
|
550
|
+
if (new.target) {
|
|
551
|
+
result = Reflect.construct(arg, arguments, arg);
|
|
552
|
+
} else {
|
|
553
|
+
result = arg.apply(this, arguments);
|
|
554
|
+
}
|
|
549
555
|
});
|
|
550
556
|
return result;
|
|
551
557
|
};
|