@reproapp/node-sdk 0.0.23 → 0.0.25
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 -0
- package/dist/index.js +786 -6
- package/dist/ingest/client.d.ts +1 -1
- package/dist/ingest/client.js +2 -1
- package/package.json +2 -2
- package/src/index.ts +879 -8
- package/src/ingest/client.ts +3 -2
- package/test/amqplib-integration.test.js +340 -0
package/src/index.ts
CHANGED
|
@@ -1158,7 +1158,7 @@ type Ctx = {
|
|
|
1158
1158
|
|
|
1159
1159
|
type RequestCaptureScopeState = {
|
|
1160
1160
|
requestRid: string;
|
|
1161
|
-
transport: 'http' | 'kafka' | 'background';
|
|
1161
|
+
transport: 'http' | 'kafka' | 'amqp' | 'background';
|
|
1162
1162
|
backendRequestCount: number;
|
|
1163
1163
|
traceBatchCount: number;
|
|
1164
1164
|
dbChangeCount: number;
|
|
@@ -1921,11 +1921,12 @@ function post(
|
|
|
1921
1921
|
sessionId: string,
|
|
1922
1922
|
body: any,
|
|
1923
1923
|
options?: EnqueueIngestOptions,
|
|
1924
|
-
) {
|
|
1924
|
+
): boolean {
|
|
1925
1925
|
try {
|
|
1926
1926
|
const entries = Array.isArray(body?.entries) ? (body.entries as LegacyEntry[]) : [];
|
|
1927
|
-
enqueueIngestEntries(cfg, sessionId, entries, options);
|
|
1927
|
+
return enqueueIngestEntries(cfg, sessionId, entries, options);
|
|
1928
1928
|
} catch { /* swallow in SDK */ }
|
|
1929
|
+
return false;
|
|
1929
1930
|
}
|
|
1930
1931
|
|
|
1931
1932
|
function toIngestConfig(cfg: IngestClientConfig): IngestClientConfig {
|
|
@@ -5858,9 +5859,6 @@ function emitDbQuery(cfg: any, sid?: string, aid?: string, payload?: any) {
|
|
|
5858
5859
|
const ctx = getCtx() as Ctx;
|
|
5859
5860
|
const spanContext = payload?.spanContext ?? captureSpanContextFromTracer();
|
|
5860
5861
|
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
5861
|
-
if (ctx.captureScope) {
|
|
5862
|
-
ctx.captureScope.dbChangeCount += 1;
|
|
5863
|
-
}
|
|
5864
5862
|
const runCtx: Ctx = {
|
|
5865
5863
|
sid: ctx.sid,
|
|
5866
5864
|
aid: ctx.aid,
|
|
@@ -5978,7 +5976,7 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
|
|
|
5978
5976
|
resultMetaMaterialization: resultMetaMaterialization.skipped,
|
|
5979
5977
|
errorMaterialization: errorMaterialization.skipped,
|
|
5980
5978
|
}, spanContext);
|
|
5981
|
-
post(cfg, sid, {
|
|
5979
|
+
const enqueued = post(cfg, sid, {
|
|
5982
5980
|
entries: [{
|
|
5983
5981
|
actionId: aid ?? null,
|
|
5984
5982
|
requestRid: ctx.rid ?? null,
|
|
@@ -5987,6 +5985,9 @@ async function emitDbQueryAsync(cfg: any, sid?: string, aid?: string, payload?:
|
|
|
5987
5985
|
t: payload.t,
|
|
5988
5986
|
}]
|
|
5989
5987
|
});
|
|
5988
|
+
if (enqueued && ctx.captureScope) {
|
|
5989
|
+
ctx.captureScope.dbChangeCount += 1;
|
|
5990
|
+
}
|
|
5990
5991
|
}
|
|
5991
5992
|
|
|
5992
5993
|
function buildMinimalUpdate(before: any, after: any) {
|
|
@@ -6418,7 +6419,7 @@ function cloneKafkaTelemetryValue(value: any): any {
|
|
|
6418
6419
|
}
|
|
6419
6420
|
|
|
6420
6421
|
async function materializeKafkaRequestBody(
|
|
6421
|
-
cfg:
|
|
6422
|
+
cfg: ReproMiddlewareConfig,
|
|
6422
6423
|
maskReq: MaskRequestContext,
|
|
6423
6424
|
body: Record<string, any>,
|
|
6424
6425
|
): Promise<InlinePrivacyMaterializationResult> {
|
|
@@ -7379,6 +7380,871 @@ export function patchKafkaJs(cfg: KafkaJsPatchConfig) {
|
|
|
7379
7380
|
}
|
|
7380
7381
|
}
|
|
7381
7382
|
|
|
7383
|
+
type AmqpHeaderMap = Record<string, any>;
|
|
7384
|
+
type AmqpContextResolver = () => { sid?: string; aid?: string } | undefined;
|
|
7385
|
+
type AmqpPropagationContext = {
|
|
7386
|
+
sid: string | null;
|
|
7387
|
+
aid: string | null;
|
|
7388
|
+
traceId: string | null;
|
|
7389
|
+
parentSpanId: string | null;
|
|
7390
|
+
requestRid: string | null;
|
|
7391
|
+
};
|
|
7392
|
+
|
|
7393
|
+
export type AmqplibPatchConfig = ReproMiddlewareConfig & {
|
|
7394
|
+
resolveContext?: AmqpContextResolver;
|
|
7395
|
+
};
|
|
7396
|
+
|
|
7397
|
+
function decodeAmqpHeaderValue(value: any): string | null {
|
|
7398
|
+
if (value === null || value === undefined) return null;
|
|
7399
|
+
if (typeof value === 'string') {
|
|
7400
|
+
const trimmed = value.trim();
|
|
7401
|
+
return trimmed.length ? trimmed : null;
|
|
7402
|
+
}
|
|
7403
|
+
if (Buffer.isBuffer(value)) {
|
|
7404
|
+
const decoded = value.toString('utf8').trim();
|
|
7405
|
+
return decoded.length ? decoded : null;
|
|
7406
|
+
}
|
|
7407
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
7408
|
+
return String(value);
|
|
7409
|
+
}
|
|
7410
|
+
return null;
|
|
7411
|
+
}
|
|
7412
|
+
|
|
7413
|
+
function readAmqpHeader(headers: any, key: string): string | null {
|
|
7414
|
+
if (!headers || typeof headers !== 'object') return null;
|
|
7415
|
+
const direct = decodeAmqpHeaderValue((headers as Record<string, any>)[key]);
|
|
7416
|
+
if (direct) return direct;
|
|
7417
|
+
const normalized = key.toLowerCase();
|
|
7418
|
+
for (const [entryKey, entryValue] of Object.entries(headers as Record<string, any>)) {
|
|
7419
|
+
if (entryKey.toLowerCase() !== normalized) continue;
|
|
7420
|
+
return decodeAmqpHeaderValue(entryValue);
|
|
7421
|
+
}
|
|
7422
|
+
return null;
|
|
7423
|
+
}
|
|
7424
|
+
|
|
7425
|
+
function cloneAmqpHeaders(headers: any): AmqpHeaderMap {
|
|
7426
|
+
if (!headers || typeof headers !== 'object' || Buffer.isBuffer(headers)) return {};
|
|
7427
|
+
const out: AmqpHeaderMap = {};
|
|
7428
|
+
for (const [key, value] of Object.entries(headers as Record<string, any>)) {
|
|
7429
|
+
out[key] = value;
|
|
7430
|
+
}
|
|
7431
|
+
return out;
|
|
7432
|
+
}
|
|
7433
|
+
|
|
7434
|
+
function withAmqpPropagationHeaders(
|
|
7435
|
+
headers: any,
|
|
7436
|
+
values: Record<string, string | null | undefined>,
|
|
7437
|
+
): AmqpHeaderMap {
|
|
7438
|
+
const out = cloneAmqpHeaders(headers);
|
|
7439
|
+
for (const [key, value] of Object.entries(values)) {
|
|
7440
|
+
if (value === null || value === undefined) continue;
|
|
7441
|
+
const str = String(value).trim();
|
|
7442
|
+
if (!str.length) continue;
|
|
7443
|
+
out[key] = str;
|
|
7444
|
+
}
|
|
7445
|
+
return out;
|
|
7446
|
+
}
|
|
7447
|
+
|
|
7448
|
+
function normalizeAmqpHeadersForPayload(headers: any): Record<string, any> {
|
|
7449
|
+
const out: Record<string, any> = {};
|
|
7450
|
+
if (!headers || typeof headers !== 'object' || Buffer.isBuffer(headers)) return out;
|
|
7451
|
+
for (const [key, value] of Object.entries(headers as Record<string, any>)) {
|
|
7452
|
+
const decoded = decodeAmqpHeaderValue(value);
|
|
7453
|
+
if (decoded !== null) {
|
|
7454
|
+
out[key] = decoded;
|
|
7455
|
+
} else {
|
|
7456
|
+
const sanitized = sanitizeResultForMeta(value);
|
|
7457
|
+
if (sanitized !== undefined) out[key] = sanitized;
|
|
7458
|
+
}
|
|
7459
|
+
}
|
|
7460
|
+
return out;
|
|
7461
|
+
}
|
|
7462
|
+
|
|
7463
|
+
function hasAmqpPropagationValues(values: Record<string, string | null | undefined>): boolean {
|
|
7464
|
+
return Object.values(values).some((value) => {
|
|
7465
|
+
if (value === null || value === undefined) return false;
|
|
7466
|
+
return String(value).trim().length > 0;
|
|
7467
|
+
});
|
|
7468
|
+
}
|
|
7469
|
+
|
|
7470
|
+
function isAmqpOptionsObject(value: any): boolean {
|
|
7471
|
+
return Boolean(value && typeof value === 'object' && !Buffer.isBuffer(value) && typeof value !== 'function');
|
|
7472
|
+
}
|
|
7473
|
+
|
|
7474
|
+
function withAmqpPropagationOptions(
|
|
7475
|
+
options: any,
|
|
7476
|
+
values: Record<string, string | null | undefined>,
|
|
7477
|
+
): Record<string, any> {
|
|
7478
|
+
const base = isAmqpOptionsObject(options) ? { ...options } : {};
|
|
7479
|
+
return {
|
|
7480
|
+
...base,
|
|
7481
|
+
headers: withAmqpPropagationHeaders(base.headers, values),
|
|
7482
|
+
};
|
|
7483
|
+
}
|
|
7484
|
+
|
|
7485
|
+
function amqpContentSize(content: any): number | null {
|
|
7486
|
+
if (content === null || content === undefined) return null;
|
|
7487
|
+
if (typeof content === 'string') return Buffer.byteLength(content, 'utf8');
|
|
7488
|
+
if (Buffer.isBuffer(content)) return content.length;
|
|
7489
|
+
if (content instanceof Uint8Array) return content.byteLength;
|
|
7490
|
+
return null;
|
|
7491
|
+
}
|
|
7492
|
+
|
|
7493
|
+
function decodeAmqpContent(content: any): any {
|
|
7494
|
+
if (content === null || content === undefined) return null;
|
|
7495
|
+
if (typeof content === 'string') return content;
|
|
7496
|
+
if (Buffer.isBuffer(content)) return content.toString('utf8');
|
|
7497
|
+
if (content instanceof Uint8Array) return Buffer.from(content).toString('utf8');
|
|
7498
|
+
if (typeof content === 'number' || typeof content === 'boolean') return content;
|
|
7499
|
+
return sanitizeResultForMeta(content);
|
|
7500
|
+
}
|
|
7501
|
+
|
|
7502
|
+
function contentTypeLooksJson(contentType: any): boolean {
|
|
7503
|
+
return typeof contentType === 'string' && /\bjson\b/i.test(contentType);
|
|
7504
|
+
}
|
|
7505
|
+
|
|
7506
|
+
function parseAmqpJsonString(
|
|
7507
|
+
value: string,
|
|
7508
|
+
contentType?: any,
|
|
7509
|
+
): { parsed: true; value: any } | { parsed: false; value: string } {
|
|
7510
|
+
const trimmed = value.trim();
|
|
7511
|
+
if (!trimmed) return { parsed: false, value };
|
|
7512
|
+
if (!contentTypeLooksJson(contentType) && !'{['.includes(trimmed[0]!)) {
|
|
7513
|
+
return { parsed: false, value };
|
|
7514
|
+
}
|
|
7515
|
+
try {
|
|
7516
|
+
return { parsed: true, value: JSON.parse(value) };
|
|
7517
|
+
} catch {
|
|
7518
|
+
return { parsed: false, value };
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
|
|
7522
|
+
function decodeAmqpMessageContent(content: any, propertiesOrOptions?: any): {
|
|
7523
|
+
value: any;
|
|
7524
|
+
rawValue?: string;
|
|
7525
|
+
valueEncoding: string | null;
|
|
7526
|
+
} {
|
|
7527
|
+
const decoded = decodeAmqpContent(content);
|
|
7528
|
+
if (typeof decoded !== 'string') {
|
|
7529
|
+
return {
|
|
7530
|
+
value: decoded,
|
|
7531
|
+
valueEncoding: decoded === null ? null : typeof decoded,
|
|
7532
|
+
};
|
|
7533
|
+
}
|
|
7534
|
+
const parsed = parseAmqpJsonString(decoded, propertiesOrOptions?.contentType);
|
|
7535
|
+
if (parsed.parsed) {
|
|
7536
|
+
return {
|
|
7537
|
+
value: parsed.value,
|
|
7538
|
+
rawValue: decoded,
|
|
7539
|
+
valueEncoding: 'json',
|
|
7540
|
+
};
|
|
7541
|
+
}
|
|
7542
|
+
return {
|
|
7543
|
+
value: decoded,
|
|
7544
|
+
valueEncoding: Buffer.isBuffer(content) || content instanceof Uint8Array ? 'utf8' : 'string',
|
|
7545
|
+
};
|
|
7546
|
+
}
|
|
7547
|
+
|
|
7548
|
+
function buildAmqpMessagePayload(content: any, propertiesOrOptions?: any): Record<string, any> {
|
|
7549
|
+
const decoded = decodeAmqpMessageContent(content, propertiesOrOptions);
|
|
7550
|
+
const payload: Record<string, any> = {
|
|
7551
|
+
value: decoded.value,
|
|
7552
|
+
valueEncoding: decoded.valueEncoding,
|
|
7553
|
+
valueBytes: amqpContentSize(content),
|
|
7554
|
+
headers: normalizeAmqpHeadersForPayload(propertiesOrOptions?.headers),
|
|
7555
|
+
};
|
|
7556
|
+
if (decoded.rawValue !== undefined) {
|
|
7557
|
+
payload.rawValue = decoded.rawValue;
|
|
7558
|
+
}
|
|
7559
|
+
if (propertiesOrOptions?.contentType !== undefined) {
|
|
7560
|
+
payload.contentType = propertiesOrOptions.contentType;
|
|
7561
|
+
}
|
|
7562
|
+
if (propertiesOrOptions?.contentEncoding !== undefined) {
|
|
7563
|
+
payload.contentEncoding = propertiesOrOptions.contentEncoding;
|
|
7564
|
+
}
|
|
7565
|
+
if (propertiesOrOptions?.messageId !== undefined) {
|
|
7566
|
+
payload.messageId = propertiesOrOptions.messageId;
|
|
7567
|
+
}
|
|
7568
|
+
if (propertiesOrOptions?.correlationId !== undefined) {
|
|
7569
|
+
payload.correlationId = propertiesOrOptions.correlationId;
|
|
7570
|
+
}
|
|
7571
|
+
if (propertiesOrOptions?.replyTo !== undefined) {
|
|
7572
|
+
payload.replyTo = propertiesOrOptions.replyTo;
|
|
7573
|
+
}
|
|
7574
|
+
if (propertiesOrOptions?.type !== undefined) {
|
|
7575
|
+
payload.type = propertiesOrOptions.type;
|
|
7576
|
+
}
|
|
7577
|
+
return payload;
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7580
|
+
function buildAmqpMessagePropertiesPayload(properties: any): Record<string, any> {
|
|
7581
|
+
if (!properties || typeof properties !== 'object') return {};
|
|
7582
|
+
const payload: Record<string, any> = {};
|
|
7583
|
+
const copyKeys = [
|
|
7584
|
+
'contentType',
|
|
7585
|
+
'contentEncoding',
|
|
7586
|
+
'deliveryMode',
|
|
7587
|
+
'priority',
|
|
7588
|
+
'correlationId',
|
|
7589
|
+
'replyTo',
|
|
7590
|
+
'expiration',
|
|
7591
|
+
'messageId',
|
|
7592
|
+
'timestamp',
|
|
7593
|
+
'type',
|
|
7594
|
+
'userId',
|
|
7595
|
+
'appId',
|
|
7596
|
+
'clusterId',
|
|
7597
|
+
];
|
|
7598
|
+
for (const key of copyKeys) {
|
|
7599
|
+
if (properties[key] !== undefined) payload[key] = sanitizeResultForMeta(properties[key]);
|
|
7600
|
+
}
|
|
7601
|
+
payload.headers = normalizeAmqpHeadersForPayload(properties.headers);
|
|
7602
|
+
return payload;
|
|
7603
|
+
}
|
|
7604
|
+
|
|
7605
|
+
function sanitizeAmqpQueue(queue: any): string {
|
|
7606
|
+
const value = typeof queue === 'string' ? queue.trim() : '';
|
|
7607
|
+
return value.length ? value : 'unknown-queue';
|
|
7608
|
+
}
|
|
7609
|
+
|
|
7610
|
+
function sanitizeAmqpExchange(exchange: any): string {
|
|
7611
|
+
if (exchange === undefined || exchange === null) return '';
|
|
7612
|
+
const value = typeof exchange === 'string' ? exchange.trim() : String(exchange).trim();
|
|
7613
|
+
return value;
|
|
7614
|
+
}
|
|
7615
|
+
|
|
7616
|
+
function sanitizeAmqpRoutingKey(routingKey: any): string {
|
|
7617
|
+
const value = typeof routingKey === 'string' ? routingKey.trim() : '';
|
|
7618
|
+
return value.length ? value : 'unknown-routing-key';
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
function summarizeAmqpError(error: any): Record<string, unknown> {
|
|
7622
|
+
const message =
|
|
7623
|
+
typeof error?.message === 'string'
|
|
7624
|
+
? error.message
|
|
7625
|
+
: error === null || error === undefined
|
|
7626
|
+
? 'Unknown AMQP error'
|
|
7627
|
+
: String(error);
|
|
7628
|
+
return {
|
|
7629
|
+
name: typeof error?.name === 'string' ? error.name : undefined,
|
|
7630
|
+
message,
|
|
7631
|
+
code: error?.code ?? undefined,
|
|
7632
|
+
};
|
|
7633
|
+
}
|
|
7634
|
+
|
|
7635
|
+
function resolveAmqpContext(cfg: AmqplibPatchConfig, headers: any): AmqpPropagationContext {
|
|
7636
|
+
const ctx = getCtx();
|
|
7637
|
+
const fallback = cfg.resolveContext?.() ?? {};
|
|
7638
|
+
const tracerSpan = captureSpanContextFromTracer();
|
|
7639
|
+
|
|
7640
|
+
const sid = readAmqpHeader(headers, KAFKA_HEADER_SESSION_ID) ?? ctx.sid ?? fallback.sid ?? null;
|
|
7641
|
+
const aid = readAmqpHeader(headers, KAFKA_HEADER_ACTION_ID) ?? ctx.aid ?? fallback.aid ?? null;
|
|
7642
|
+
const traceId =
|
|
7643
|
+
readAmqpHeader(headers, KAFKA_HEADER_TRACE_ID) ??
|
|
7644
|
+
tracerSpan?.traceId ??
|
|
7645
|
+
__TRACER__?.getCurrentTraceId?.() ??
|
|
7646
|
+
null;
|
|
7647
|
+
const parentSpanId =
|
|
7648
|
+
readAmqpHeader(headers, KAFKA_HEADER_SPAN_ID) ??
|
|
7649
|
+
readAmqpHeader(headers, KAFKA_HEADER_PARENT_SPAN_ID) ??
|
|
7650
|
+
null;
|
|
7651
|
+
const requestRid = readAmqpHeader(headers, KAFKA_HEADER_REQUEST_ID);
|
|
7652
|
+
|
|
7653
|
+
return { sid, aid, traceId, parentSpanId, requestRid };
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
function withOptionalTraceContextSync<T>(traceId: string | null, fn: () => T): T {
|
|
7657
|
+
const tracerWithTrace = (__TRACER__ as (TracerApi & { withTrace?: <R>(id: string, runner: () => R) => R }) | null)?.withTrace;
|
|
7658
|
+
if (traceId && typeof tracerWithTrace === 'function') {
|
|
7659
|
+
let invoked = false;
|
|
7660
|
+
try {
|
|
7661
|
+
return tracerWithTrace(traceId, () => {
|
|
7662
|
+
invoked = true;
|
|
7663
|
+
return fn();
|
|
7664
|
+
});
|
|
7665
|
+
} catch (error) {
|
|
7666
|
+
if (invoked) throw error;
|
|
7667
|
+
return fn();
|
|
7668
|
+
}
|
|
7669
|
+
}
|
|
7670
|
+
return fn();
|
|
7671
|
+
}
|
|
7672
|
+
|
|
7673
|
+
function buildAmqpTraceEntries(
|
|
7674
|
+
actionId: string | null | undefined,
|
|
7675
|
+
requestRid: string,
|
|
7676
|
+
events: TraceEventRecord[],
|
|
7677
|
+
): LegacyEntry[] {
|
|
7678
|
+
return buildKafkaTraceEntries(actionId, requestRid, events);
|
|
7679
|
+
}
|
|
7680
|
+
|
|
7681
|
+
function scheduleAmqpTraceFlush(
|
|
7682
|
+
cfg: AmqplibPatchConfig,
|
|
7683
|
+
amqpCtx: Ctx,
|
|
7684
|
+
actionId: string | null,
|
|
7685
|
+
requestRid: string,
|
|
7686
|
+
rawTraceEvents: any[],
|
|
7687
|
+
maskReq: MaskRequestContext,
|
|
7688
|
+
captureScope: RequestCaptureScopeState,
|
|
7689
|
+
): void {
|
|
7690
|
+
const sid = amqpCtx.sid;
|
|
7691
|
+
if (!sid) return;
|
|
7692
|
+
const rawEvents = rawTraceEvents.slice();
|
|
7693
|
+
|
|
7694
|
+
scheduleSdkBackgroundWork(async () => {
|
|
7695
|
+
await als.run(amqpCtx, async () => {
|
|
7696
|
+
await drainPrivacyTasks(amqpCtx);
|
|
7697
|
+
const pendingEvents: PendingTraceEventRecord[] = [];
|
|
7698
|
+
for (const rawTraceEvent of rawEvents) {
|
|
7699
|
+
const evt = buildPendingKafkaTraceEvent(rawTraceEvent);
|
|
7700
|
+
if (evt) pendingEvents.push(evt);
|
|
7701
|
+
}
|
|
7702
|
+
|
|
7703
|
+
let entries: LegacyEntry[] = [];
|
|
7704
|
+
if (pendingEvents.length) {
|
|
7705
|
+
const masking = normalizeMaskingConfig(cfg.masking);
|
|
7706
|
+
const preparedEvents = preparePendingTraceEventsForFlush(pendingEvents);
|
|
7707
|
+
const materializedEvents = await materializePendingTraceEventsAsync(preparedEvents, {
|
|
7708
|
+
cfg,
|
|
7709
|
+
maskReq,
|
|
7710
|
+
masking,
|
|
7711
|
+
resolvePrivacy: () => getRuntimePrivacyState(cfg).policy ?? null,
|
|
7712
|
+
});
|
|
7713
|
+
entries = buildAmqpTraceEntries(actionId, requestRid, materializedEvents);
|
|
7714
|
+
captureScope.traceBatchCount = entries.length;
|
|
7715
|
+
if (entries.length) {
|
|
7716
|
+
post(cfg, sid, { entries });
|
|
7717
|
+
}
|
|
7718
|
+
} else {
|
|
7719
|
+
captureScope.traceBatchCount = 0;
|
|
7720
|
+
}
|
|
7721
|
+
|
|
7722
|
+
post(cfg, sid, {
|
|
7723
|
+
entries: [{
|
|
7724
|
+
actionId: actionId ?? null,
|
|
7725
|
+
requestRid,
|
|
7726
|
+
requestCaptureManifest: buildRequestCaptureManifestPayload(
|
|
7727
|
+
captureScope,
|
|
7728
|
+
entries.length,
|
|
7729
|
+
),
|
|
7730
|
+
t: alignedNow(),
|
|
7731
|
+
}],
|
|
7732
|
+
});
|
|
7733
|
+
});
|
|
7734
|
+
}, { priority: 'high' });
|
|
7735
|
+
}
|
|
7736
|
+
|
|
7737
|
+
function setAmqpPublishOptionsArg(
|
|
7738
|
+
args: any[],
|
|
7739
|
+
optionsIndex: number,
|
|
7740
|
+
nextOptions: Record<string, any>,
|
|
7741
|
+
callbackIndex: number,
|
|
7742
|
+
wrappedCallback?: (...callbackArgs: any[]) => any,
|
|
7743
|
+
): any[] {
|
|
7744
|
+
const nextArgs = args.slice();
|
|
7745
|
+
while (nextArgs.length <= optionsIndex) {
|
|
7746
|
+
nextArgs.push(undefined);
|
|
7747
|
+
}
|
|
7748
|
+
nextArgs[optionsIndex] = nextOptions;
|
|
7749
|
+
if (callbackIndex >= 0 && wrappedCallback) {
|
|
7750
|
+
if (callbackIndex === optionsIndex) {
|
|
7751
|
+
nextArgs[optionsIndex + 1] = wrappedCallback;
|
|
7752
|
+
} else {
|
|
7753
|
+
nextArgs[callbackIndex] = wrappedCallback;
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7756
|
+
return nextArgs;
|
|
7757
|
+
}
|
|
7758
|
+
|
|
7759
|
+
function scheduleAmqpPublishCapture(params: {
|
|
7760
|
+
cfg: AmqplibPatchConfig;
|
|
7761
|
+
sid: string | null;
|
|
7762
|
+
aid: string | null;
|
|
7763
|
+
publishRid: string;
|
|
7764
|
+
captureScope: RequestCaptureScopeState;
|
|
7765
|
+
method: 'AMQP_PUBLISH' | 'AMQP_SEND_TO_QUEUE';
|
|
7766
|
+
path: string;
|
|
7767
|
+
key: string;
|
|
7768
|
+
exchange?: string;
|
|
7769
|
+
routingKey?: string;
|
|
7770
|
+
queue?: string;
|
|
7771
|
+
content: any;
|
|
7772
|
+
options: any;
|
|
7773
|
+
span: SpanContext | null;
|
|
7774
|
+
t0: number;
|
|
7775
|
+
status: number;
|
|
7776
|
+
error?: any;
|
|
7777
|
+
result?: any;
|
|
7778
|
+
}): void {
|
|
7779
|
+
if (!params.sid) return;
|
|
7780
|
+
const maskReq: MaskRequestContext = {
|
|
7781
|
+
method: params.method,
|
|
7782
|
+
path: params.path,
|
|
7783
|
+
key: params.key,
|
|
7784
|
+
};
|
|
7785
|
+
const messagePayload = buildAmqpMessagePayload(params.content, params.options);
|
|
7786
|
+
const body = {
|
|
7787
|
+
transport: 'amqp',
|
|
7788
|
+
exchange: params.exchange,
|
|
7789
|
+
routingKey: params.routingKey,
|
|
7790
|
+
queue: params.queue,
|
|
7791
|
+
messageBytes: amqpContentSize(params.content),
|
|
7792
|
+
message: messagePayload,
|
|
7793
|
+
value: cloneKafkaTelemetryValue(messagePayload.value),
|
|
7794
|
+
rawValue: messagePayload.rawValue,
|
|
7795
|
+
valueEncoding: messagePayload.valueEncoding,
|
|
7796
|
+
valueBytes: messagePayload.valueBytes,
|
|
7797
|
+
properties: buildAmqpMessagePropertiesPayload(params.options),
|
|
7798
|
+
traceId: params.span?.traceId ?? null,
|
|
7799
|
+
spanId: params.span?.spanId ?? null,
|
|
7800
|
+
parentSpanId: params.span?.parentSpanId ?? null,
|
|
7801
|
+
};
|
|
7802
|
+
|
|
7803
|
+
scheduleSdkBackgroundWork(async () => {
|
|
7804
|
+
const bodyMaterialization = await materializeKafkaRequestBody(params.cfg, maskReq, body);
|
|
7805
|
+
const requestPayload: Record<string, any> = {
|
|
7806
|
+
rid: params.publishRid,
|
|
7807
|
+
method: params.method,
|
|
7808
|
+
url: params.path,
|
|
7809
|
+
path: params.path,
|
|
7810
|
+
status: params.status,
|
|
7811
|
+
durMs: Date.now() - params.t0,
|
|
7812
|
+
key: params.key,
|
|
7813
|
+
headers: {
|
|
7814
|
+
transport: 'amqp',
|
|
7815
|
+
exchange: params.exchange,
|
|
7816
|
+
routingKey: params.routingKey,
|
|
7817
|
+
queue: params.queue,
|
|
7818
|
+
messageBytes: amqpContentSize(params.content),
|
|
7819
|
+
backpressure: params.result === false ? true : undefined,
|
|
7820
|
+
},
|
|
7821
|
+
body: bodyMaterialization.value,
|
|
7822
|
+
bodyMaterialization: bodyMaterialization.skipped,
|
|
7823
|
+
respBody: params.status >= 400
|
|
7824
|
+
? { error: summarizeAmqpError(params.error) }
|
|
7825
|
+
: { accepted: params.result !== false, backpressure: params.result === false ? true : undefined },
|
|
7826
|
+
};
|
|
7827
|
+
|
|
7828
|
+
post(params.cfg, params.sid!, {
|
|
7829
|
+
entries: [{
|
|
7830
|
+
actionId: params.aid ?? null,
|
|
7831
|
+
request: requestPayload,
|
|
7832
|
+
t: alignedNow(),
|
|
7833
|
+
}],
|
|
7834
|
+
});
|
|
7835
|
+
params.captureScope.backendRequestCount += 1;
|
|
7836
|
+
post(params.cfg, params.sid!, {
|
|
7837
|
+
entries: [{
|
|
7838
|
+
actionId: params.aid ?? null,
|
|
7839
|
+
requestRid: params.publishRid,
|
|
7840
|
+
requestCaptureManifest: buildRequestCaptureManifestPayload(
|
|
7841
|
+
params.captureScope,
|
|
7842
|
+
0,
|
|
7843
|
+
),
|
|
7844
|
+
t: alignedNow(),
|
|
7845
|
+
}],
|
|
7846
|
+
});
|
|
7847
|
+
}, { priority: 'high' });
|
|
7848
|
+
}
|
|
7849
|
+
|
|
7850
|
+
function patchAmqplibPublishMethod(
|
|
7851
|
+
channel: any,
|
|
7852
|
+
cfg: AmqplibPatchConfig,
|
|
7853
|
+
methodName: 'publish' | 'sendToQueue',
|
|
7854
|
+
) {
|
|
7855
|
+
const orig = channel?.[methodName];
|
|
7856
|
+
if (typeof orig !== 'function' || orig.__repro_amqplib_patched) return;
|
|
7857
|
+
|
|
7858
|
+
const optionsIndex = methodName === 'publish' ? 3 : 2;
|
|
7859
|
+
const method = methodName === 'publish' ? 'AMQP_PUBLISH' : 'AMQP_SEND_TO_QUEUE';
|
|
7860
|
+
|
|
7861
|
+
const wrapped = function reproPatchedAmqplibPublish(this: any, ...args: any[]) {
|
|
7862
|
+
const ctx = getCtx();
|
|
7863
|
+
const fallback = cfg.resolveContext?.() ?? {};
|
|
7864
|
+
const sid = ctx.sid ?? fallback.sid ?? null;
|
|
7865
|
+
const aid = ctx.aid ?? fallback.aid ?? null;
|
|
7866
|
+
const span = captureSpanContextFromTracer();
|
|
7867
|
+
const publishRid = nextRequestId(alignedNow());
|
|
7868
|
+
const propagationValues = {
|
|
7869
|
+
[KAFKA_HEADER_TRACE_ID]: span?.traceId ?? null,
|
|
7870
|
+
[KAFKA_HEADER_SPAN_ID]: span?.spanId === null || span?.spanId === undefined ? null : String(span.spanId),
|
|
7871
|
+
[KAFKA_HEADER_PARENT_SPAN_ID]:
|
|
7872
|
+
span?.parentSpanId === null || span?.parentSpanId === undefined ? null : String(span.parentSpanId),
|
|
7873
|
+
[KAFKA_HEADER_REQUEST_ID]: publishRid,
|
|
7874
|
+
[KAFKA_HEADER_SESSION_ID]: sid,
|
|
7875
|
+
[KAFKA_HEADER_ACTION_ID]: aid,
|
|
7876
|
+
};
|
|
7877
|
+
const shouldPropagate = hasAmqpPropagationValues(propagationValues);
|
|
7878
|
+
const callbackIndex = args.length > 0 && typeof args[args.length - 1] === 'function'
|
|
7879
|
+
? args.length - 1
|
|
7880
|
+
: -1;
|
|
7881
|
+
const originalOptions = callbackIndex === optionsIndex ? undefined : args[optionsIndex];
|
|
7882
|
+
const nextOptions = shouldPropagate
|
|
7883
|
+
? withAmqpPropagationOptions(originalOptions, propagationValues)
|
|
7884
|
+
: originalOptions;
|
|
7885
|
+
const exchange = methodName === 'publish' ? sanitizeAmqpExchange(args[0]) : '';
|
|
7886
|
+
const routingKey = methodName === 'publish' ? sanitizeAmqpRoutingKey(args[1]) : undefined;
|
|
7887
|
+
const queue = methodName === 'sendToQueue' ? sanitizeAmqpQueue(args[0]) : undefined;
|
|
7888
|
+
const path = methodName === 'publish'
|
|
7889
|
+
? `amqp://${exchange || 'default'}/${routingKey}`
|
|
7890
|
+
: `amqp://queue/${queue}`;
|
|
7891
|
+
const key = `${method} ${methodName === 'publish' ? `${exchange || 'default'}/${routingKey}` : queue}`;
|
|
7892
|
+
const content = methodName === 'publish' ? args[2] : args[1];
|
|
7893
|
+
const captureScope = createRequestCaptureScope(publishRid, 'amqp');
|
|
7894
|
+
const t0 = Date.now();
|
|
7895
|
+
let finished = false;
|
|
7896
|
+
|
|
7897
|
+
const finish = (status: number, error?: any, result?: any) => {
|
|
7898
|
+
if (finished) return;
|
|
7899
|
+
finished = true;
|
|
7900
|
+
scheduleAmqpPublishCapture({
|
|
7901
|
+
cfg,
|
|
7902
|
+
sid,
|
|
7903
|
+
aid,
|
|
7904
|
+
publishRid,
|
|
7905
|
+
captureScope,
|
|
7906
|
+
method,
|
|
7907
|
+
path,
|
|
7908
|
+
key,
|
|
7909
|
+
exchange,
|
|
7910
|
+
routingKey,
|
|
7911
|
+
queue,
|
|
7912
|
+
content,
|
|
7913
|
+
options: nextOptions,
|
|
7914
|
+
span,
|
|
7915
|
+
t0,
|
|
7916
|
+
status,
|
|
7917
|
+
error,
|
|
7918
|
+
result,
|
|
7919
|
+
});
|
|
7920
|
+
};
|
|
7921
|
+
|
|
7922
|
+
const originalCallback = callbackIndex >= 0 ? args[callbackIndex] : null;
|
|
7923
|
+
const wrappedCallback = typeof originalCallback === 'function'
|
|
7924
|
+
? function reproAmqplibPublishCallback(this: any, ...callbackArgs: any[]) {
|
|
7925
|
+
const error = callbackArgs[0];
|
|
7926
|
+
finish(error ? 500 : 200, error, callbackArgs[1]);
|
|
7927
|
+
return originalCallback.apply(this, callbackArgs);
|
|
7928
|
+
}
|
|
7929
|
+
: undefined;
|
|
7930
|
+
|
|
7931
|
+
const nextArgs = shouldPropagate
|
|
7932
|
+
? setAmqpPublishOptionsArg(args, optionsIndex, nextOptions, callbackIndex, wrappedCallback)
|
|
7933
|
+
: (wrappedCallback
|
|
7934
|
+
? setAmqpPublishOptionsArg(args, optionsIndex, nextOptions, callbackIndex, wrappedCallback)
|
|
7935
|
+
: args);
|
|
7936
|
+
|
|
7937
|
+
try {
|
|
7938
|
+
const result = orig.apply(this, nextArgs);
|
|
7939
|
+
if (!wrappedCallback) {
|
|
7940
|
+
finish(200, undefined, result);
|
|
7941
|
+
}
|
|
7942
|
+
return result;
|
|
7943
|
+
} catch (error) {
|
|
7944
|
+
finish(500, error);
|
|
7945
|
+
throw error;
|
|
7946
|
+
}
|
|
7947
|
+
};
|
|
7948
|
+
|
|
7949
|
+
(wrapped as any).__repro_amqplib_patched = true;
|
|
7950
|
+
channel[methodName] = wrapped;
|
|
7951
|
+
}
|
|
7952
|
+
|
|
7953
|
+
function wrapAmqplibConsumeHandler(onMessage: any, cfg: AmqplibPatchConfig, queue: any) {
|
|
7954
|
+
return function reproAmqplibConsumeMessage(this: any, message: any) {
|
|
7955
|
+
if (message === null || message === undefined) {
|
|
7956
|
+
return onMessage.call(this, message);
|
|
7957
|
+
}
|
|
7958
|
+
|
|
7959
|
+
const queueName = sanitizeAmqpQueue(queue);
|
|
7960
|
+
const properties = message?.properties ?? {};
|
|
7961
|
+
const headers = properties?.headers;
|
|
7962
|
+
const runtime = resolveAmqpContext(cfg, headers);
|
|
7963
|
+
const sid = runtime.sid;
|
|
7964
|
+
const aid = runtime.aid;
|
|
7965
|
+
const deliveryTag = message?.fields?.deliveryTag ?? alignedNow();
|
|
7966
|
+
const consumeRid = nextRequestId(`${queueName}-${deliveryTag}`);
|
|
7967
|
+
const traceId = consumeRid;
|
|
7968
|
+
const captureScope = createRequestCaptureScope(consumeRid, 'amqp');
|
|
7969
|
+
const rawTraceEvents: any[] = [];
|
|
7970
|
+
const maskReq: MaskRequestContext = {
|
|
7971
|
+
method: 'AMQP_CONSUME',
|
|
7972
|
+
path: `amqp://queue/${queueName}`,
|
|
7973
|
+
key: `AMQP_CONSUME ${queueName}`,
|
|
7974
|
+
};
|
|
7975
|
+
const amqpCtx: Ctx = {
|
|
7976
|
+
sid: sid ?? undefined,
|
|
7977
|
+
aid: aid ?? undefined,
|
|
7978
|
+
rid: consumeRid,
|
|
7979
|
+
clockSkewMs: 0,
|
|
7980
|
+
excludedSpanIds: new Set<string>(),
|
|
7981
|
+
maskReq,
|
|
7982
|
+
privacyTasks: new Set<Promise<void>>(),
|
|
7983
|
+
captureScope,
|
|
7984
|
+
};
|
|
7985
|
+
let unsubscribe: undefined | (() => void);
|
|
7986
|
+
|
|
7987
|
+
const subscribeTrace = () => {
|
|
7988
|
+
if (!__TRACER__?.tracer?.on) return;
|
|
7989
|
+
try {
|
|
7990
|
+
unsubscribe = __TRACER__.tracer.on((event: any) => {
|
|
7991
|
+
if (!event || event.traceId !== traceId) return;
|
|
7992
|
+
rawTraceEvents.push(event);
|
|
7993
|
+
});
|
|
7994
|
+
} catch {}
|
|
7995
|
+
};
|
|
7996
|
+
const unsubscribeTrace = () => {
|
|
7997
|
+
try { unsubscribe && unsubscribe(); } catch {}
|
|
7998
|
+
unsubscribe = undefined;
|
|
7999
|
+
};
|
|
8000
|
+
|
|
8001
|
+
const invoke = () => {
|
|
8002
|
+
subscribeTrace();
|
|
8003
|
+
try {
|
|
8004
|
+
const result = onMessage.call(this, message);
|
|
8005
|
+
if (result && typeof result.then === 'function') {
|
|
8006
|
+
return result.finally(() => {
|
|
8007
|
+
unsubscribeTrace();
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
8010
|
+
unsubscribeTrace();
|
|
8011
|
+
return result;
|
|
8012
|
+
} catch (error) {
|
|
8013
|
+
unsubscribeTrace();
|
|
8014
|
+
throw error;
|
|
8015
|
+
}
|
|
8016
|
+
};
|
|
8017
|
+
|
|
8018
|
+
const runInAls = () => {
|
|
8019
|
+
if (!sid && !aid) return invoke();
|
|
8020
|
+
return als.run(amqpCtx, () => invoke());
|
|
8021
|
+
};
|
|
8022
|
+
|
|
8023
|
+
const t0 = Date.now();
|
|
8024
|
+
let finished = false;
|
|
8025
|
+
const finish = (threw: boolean, error?: any) => {
|
|
8026
|
+
if (finished) return;
|
|
8027
|
+
finished = true;
|
|
8028
|
+
if (!sid) return;
|
|
8029
|
+
const fields = message?.fields ?? {};
|
|
8030
|
+
const messagePayload = buildAmqpMessagePayload(message?.content, properties);
|
|
8031
|
+
const body = {
|
|
8032
|
+
transport: 'amqp',
|
|
8033
|
+
queue: queueName,
|
|
8034
|
+
exchange: sanitizeAmqpExchange(fields.exchange),
|
|
8035
|
+
routingKey: sanitizeAmqpRoutingKey(fields.routingKey),
|
|
8036
|
+
deliveryTag: fields.deliveryTag ?? null,
|
|
8037
|
+
redelivered: fields.redelivered ?? null,
|
|
8038
|
+
consumerTag: fields.consumerTag ?? null,
|
|
8039
|
+
parentRequestRid: runtime.requestRid,
|
|
8040
|
+
parentTraceId: runtime.traceId,
|
|
8041
|
+
parentSpanId: runtime.parentSpanId,
|
|
8042
|
+
messageBytes: amqpContentSize(message?.content),
|
|
8043
|
+
message: messagePayload,
|
|
8044
|
+
value: cloneKafkaTelemetryValue(messagePayload.value),
|
|
8045
|
+
rawValue: messagePayload.rawValue,
|
|
8046
|
+
valueEncoding: messagePayload.valueEncoding,
|
|
8047
|
+
valueBytes: messagePayload.valueBytes,
|
|
8048
|
+
properties: buildAmqpMessagePropertiesPayload(properties),
|
|
8049
|
+
};
|
|
8050
|
+
|
|
8051
|
+
scheduleSdkBackgroundWork(async () => {
|
|
8052
|
+
const bodyMaterialization = await materializeKafkaRequestBody(cfg, maskReq, body);
|
|
8053
|
+
const requestPayload: Record<string, any> = {
|
|
8054
|
+
rid: consumeRid,
|
|
8055
|
+
method: 'AMQP_CONSUME',
|
|
8056
|
+
url: `amqp://queue/${queueName}`,
|
|
8057
|
+
path: `amqp://queue/${queueName}`,
|
|
8058
|
+
status: threw ? 500 : 200,
|
|
8059
|
+
durMs: Date.now() - t0,
|
|
8060
|
+
key: `AMQP_CONSUME ${queueName}`,
|
|
8061
|
+
headers: normalizeAmqpHeadersForPayload(headers),
|
|
8062
|
+
body: bodyMaterialization.value,
|
|
8063
|
+
bodyMaterialization: bodyMaterialization.skipped,
|
|
8064
|
+
respBody: threw ? { error: summarizeAmqpError(error) } : undefined,
|
|
8065
|
+
};
|
|
8066
|
+
|
|
8067
|
+
post(cfg, sid, {
|
|
8068
|
+
entries: [{
|
|
8069
|
+
actionId: aid ?? null,
|
|
8070
|
+
request: requestPayload,
|
|
8071
|
+
t: alignedNow(),
|
|
8072
|
+
}],
|
|
8073
|
+
});
|
|
8074
|
+
captureScope.backendRequestCount += 1;
|
|
8075
|
+
scheduleAmqpTraceFlush(cfg, amqpCtx, aid, consumeRid, rawTraceEvents, maskReq, captureScope);
|
|
8076
|
+
}, { priority: 'high' });
|
|
8077
|
+
};
|
|
8078
|
+
|
|
8079
|
+
try {
|
|
8080
|
+
const result = withOptionalTraceContextSync(traceId, runInAls);
|
|
8081
|
+
if (result && typeof (result as any).then === 'function') {
|
|
8082
|
+
return (result as Promise<any>).then(
|
|
8083
|
+
(value) => {
|
|
8084
|
+
finish(false);
|
|
8085
|
+
return value;
|
|
8086
|
+
},
|
|
8087
|
+
(error) => {
|
|
8088
|
+
finish(true, error);
|
|
8089
|
+
throw error;
|
|
8090
|
+
},
|
|
8091
|
+
);
|
|
8092
|
+
}
|
|
8093
|
+
finish(false);
|
|
8094
|
+
return result;
|
|
8095
|
+
} catch (error) {
|
|
8096
|
+
finish(true, error);
|
|
8097
|
+
throw error;
|
|
8098
|
+
}
|
|
8099
|
+
};
|
|
8100
|
+
}
|
|
8101
|
+
|
|
8102
|
+
function patchAmqplibChannel(channel: any, cfg: AmqplibPatchConfig) {
|
|
8103
|
+
if (!channel || channel.__repro_amqplib_channel_patched) return;
|
|
8104
|
+
channel.__repro_amqplib_channel_patched = true;
|
|
8105
|
+
|
|
8106
|
+
patchAmqplibPublishMethod(channel, cfg, 'publish');
|
|
8107
|
+
patchAmqplibPublishMethod(channel, cfg, 'sendToQueue');
|
|
8108
|
+
|
|
8109
|
+
const origConsume = channel.consume;
|
|
8110
|
+
if (typeof origConsume === 'function' && !origConsume.__repro_amqplib_patched) {
|
|
8111
|
+
const wrapped = function reproPatchedAmqplibConsume(this: any, queue: any, onMessage: any, ...rest: any[]) {
|
|
8112
|
+
if (typeof onMessage !== 'function') {
|
|
8113
|
+
return origConsume.call(this, queue, onMessage, ...rest);
|
|
8114
|
+
}
|
|
8115
|
+
return origConsume.call(this, queue, wrapAmqplibConsumeHandler(onMessage, cfg, queue), ...rest);
|
|
8116
|
+
};
|
|
8117
|
+
(wrapped as any).__repro_amqplib_patched = true;
|
|
8118
|
+
channel.consume = wrapped;
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8122
|
+
function wrapAmqplibChannelFactory(
|
|
8123
|
+
connection: any,
|
|
8124
|
+
cfg: AmqplibPatchConfig,
|
|
8125
|
+
methodName: 'createChannel' | 'createConfirmChannel',
|
|
8126
|
+
) {
|
|
8127
|
+
const orig = connection?.[methodName];
|
|
8128
|
+
if (typeof orig !== 'function' || orig.__repro_amqplib_patched) return;
|
|
8129
|
+
|
|
8130
|
+
const wrapped = function reproPatchedAmqplibCreateChannel(this: any, ...args: any[]) {
|
|
8131
|
+
const callbackIndex = args.length > 0 && typeof args[args.length - 1] === 'function'
|
|
8132
|
+
? args.length - 1
|
|
8133
|
+
: -1;
|
|
8134
|
+
const nextArgs = args.slice();
|
|
8135
|
+
if (callbackIndex >= 0) {
|
|
8136
|
+
const originalCallback = args[callbackIndex];
|
|
8137
|
+
nextArgs[callbackIndex] = function reproAmqplibCreateChannelCallback(this: any, error: any, channel: any, ...rest: any[]) {
|
|
8138
|
+
if (channel) patchAmqplibChannel(channel, cfg);
|
|
8139
|
+
return originalCallback.call(this, error, channel, ...rest);
|
|
8140
|
+
};
|
|
8141
|
+
}
|
|
8142
|
+
|
|
8143
|
+
const result = orig.apply(this, nextArgs);
|
|
8144
|
+
if (result && typeof result.then === 'function') {
|
|
8145
|
+
return result.then((channel: any) => {
|
|
8146
|
+
patchAmqplibChannel(channel, cfg);
|
|
8147
|
+
return channel;
|
|
8148
|
+
});
|
|
8149
|
+
}
|
|
8150
|
+
if (result) patchAmqplibChannel(result, cfg);
|
|
8151
|
+
return result;
|
|
8152
|
+
};
|
|
8153
|
+
|
|
8154
|
+
(wrapped as any).__repro_amqplib_patched = true;
|
|
8155
|
+
connection[methodName] = wrapped;
|
|
8156
|
+
}
|
|
8157
|
+
|
|
8158
|
+
function patchAmqplibConnection(connection: any, cfg: AmqplibPatchConfig) {
|
|
8159
|
+
if (!connection || connection.__repro_amqplib_connection_patched) return;
|
|
8160
|
+
connection.__repro_amqplib_connection_patched = true;
|
|
8161
|
+
wrapAmqplibChannelFactory(connection, cfg, 'createChannel');
|
|
8162
|
+
wrapAmqplibChannelFactory(connection, cfg, 'createConfirmChannel');
|
|
8163
|
+
}
|
|
8164
|
+
|
|
8165
|
+
function patchAmqplibModule(amqpModule: any, cfg: AmqplibPatchConfig) {
|
|
8166
|
+
if (!amqpModule || typeof amqpModule !== 'object') return;
|
|
8167
|
+
const targets = new Set<any>([amqpModule]);
|
|
8168
|
+
if (amqpModule.default && typeof amqpModule.default === 'object') {
|
|
8169
|
+
targets.add(amqpModule.default);
|
|
8170
|
+
}
|
|
8171
|
+
|
|
8172
|
+
for (const target of targets) {
|
|
8173
|
+
if (!target || target.__repro_amqplib_module_patched) continue;
|
|
8174
|
+
const origConnect = target.connect;
|
|
8175
|
+
if (typeof origConnect !== 'function') continue;
|
|
8176
|
+
target.__repro_amqplib_module_patched = true;
|
|
8177
|
+
target.connect = function reproPatchedAmqplibConnect(this: any, ...args: any[]) {
|
|
8178
|
+
const callbackIndex = args.length > 0 && typeof args[args.length - 1] === 'function'
|
|
8179
|
+
? args.length - 1
|
|
8180
|
+
: -1;
|
|
8181
|
+
const nextArgs = args.slice();
|
|
8182
|
+
if (callbackIndex >= 0) {
|
|
8183
|
+
const originalCallback = args[callbackIndex];
|
|
8184
|
+
nextArgs[callbackIndex] = function reproAmqplibConnectCallback(this: any, error: any, connection: any, ...rest: any[]) {
|
|
8185
|
+
if (connection) patchAmqplibConnection(connection, cfg);
|
|
8186
|
+
return originalCallback.call(this, error, connection, ...rest);
|
|
8187
|
+
};
|
|
8188
|
+
}
|
|
8189
|
+
|
|
8190
|
+
const result = origConnect.apply(this, nextArgs);
|
|
8191
|
+
if (result && typeof result.then === 'function') {
|
|
8192
|
+
return result.then((connection: any) => {
|
|
8193
|
+
patchAmqplibConnection(connection, cfg);
|
|
8194
|
+
return connection;
|
|
8195
|
+
});
|
|
8196
|
+
}
|
|
8197
|
+
if (result) patchAmqplibConnection(result, cfg);
|
|
8198
|
+
return result;
|
|
8199
|
+
};
|
|
8200
|
+
}
|
|
8201
|
+
}
|
|
8202
|
+
|
|
8203
|
+
const amqplibPatchConfigs: AmqplibPatchConfig[] = [];
|
|
8204
|
+
let amqplibRequireHookInstalled = false;
|
|
8205
|
+
|
|
8206
|
+
function installAmqplibRequireHook() {
|
|
8207
|
+
if (amqplibRequireHookInstalled) return;
|
|
8208
|
+
amqplibRequireHookInstalled = true;
|
|
8209
|
+
|
|
8210
|
+
try {
|
|
8211
|
+
const rim = requireOptionalHostModule('require-in-the-middle');
|
|
8212
|
+
const Hook = rim?.Hook ?? rim;
|
|
8213
|
+
if (typeof Hook !== 'function') return;
|
|
8214
|
+
|
|
8215
|
+
// eslint-disable-next-line no-new
|
|
8216
|
+
new Hook(['amqplib', 'amqplib/callback_api'], (exports: any) => {
|
|
8217
|
+
for (const cfg of amqplibPatchConfigs) {
|
|
8218
|
+
patchAmqplibModule(exports, cfg);
|
|
8219
|
+
}
|
|
8220
|
+
return exports;
|
|
8221
|
+
});
|
|
8222
|
+
} catch {}
|
|
8223
|
+
}
|
|
8224
|
+
|
|
8225
|
+
export function patchAmqplib(cfg: AmqplibPatchConfig) {
|
|
8226
|
+
amqplibPatchConfigs.push(cfg);
|
|
8227
|
+
installAmqplibRequireHook();
|
|
8228
|
+
|
|
8229
|
+
const amqpModules = new Set<any>();
|
|
8230
|
+
const resolvedAmqpModule = requireOptionalHostModule('amqplib');
|
|
8231
|
+
if (resolvedAmqpModule) {
|
|
8232
|
+
amqpModules.add(resolvedAmqpModule);
|
|
8233
|
+
}
|
|
8234
|
+
const resolvedAmqpCallbackModule = requireOptionalHostModule('amqplib/callback_api');
|
|
8235
|
+
if (resolvedAmqpCallbackModule) {
|
|
8236
|
+
amqpModules.add(resolvedAmqpCallbackModule);
|
|
8237
|
+
}
|
|
8238
|
+
for (const loadedAmqpModule of getLoadedModuleExportsByPackage('amqplib')) {
|
|
8239
|
+
amqpModules.add(loadedAmqpModule);
|
|
8240
|
+
}
|
|
8241
|
+
if (!amqpModules.size) return;
|
|
8242
|
+
|
|
8243
|
+
for (const amqpModule of amqpModules) {
|
|
8244
|
+
patchAmqplibModule(amqpModule, cfg);
|
|
8245
|
+
}
|
|
8246
|
+
}
|
|
8247
|
+
|
|
7382
8248
|
export type RedisPatchConfig = ReproMiddlewareConfig & {
|
|
7383
8249
|
resolveContext?: () => { sid?: string; aid?: string } | undefined;
|
|
7384
8250
|
};
|
|
@@ -8128,6 +8994,7 @@ export async function initRepro(cfg: ReproInitConfig): Promise<void> {
|
|
|
8128
8994
|
skipUrls: [resolveIngestUrl(ingestCfg)],
|
|
8129
8995
|
});
|
|
8130
8996
|
patchKafkaJs(integrationCfg);
|
|
8997
|
+
patchAmqplib(integrationCfg);
|
|
8131
8998
|
patchRedis(integrationCfg);
|
|
8132
8999
|
patchSendgridMail(integrationCfg);
|
|
8133
9000
|
patchMongooseIntegration(reproAutoMiddlewareConfig);
|
|
@@ -8176,6 +9043,10 @@ export const __reproTestHooks = {
|
|
|
8176
9043
|
materializeInlinePrivacyValueAsyncForTest: materializeInlinePrivacyValueAsync,
|
|
8177
9044
|
patchKafkaProducerInstanceForTest: patchKafkaProducerInstance,
|
|
8178
9045
|
wrapKafkaEachMessageHandlerForTest: wrapKafkaEachMessageHandler,
|
|
9046
|
+
patchAmqplibModuleForTest: patchAmqplibModule,
|
|
9047
|
+
patchAmqplibConnectionForTest: patchAmqplibConnection,
|
|
9048
|
+
patchAmqplibChannelForTest: patchAmqplibChannel,
|
|
9049
|
+
wrapAmqplibConsumeHandlerForTest: wrapAmqplibConsumeHandler,
|
|
8179
9050
|
chunkTraceEventsForTransportForTest: chunkTraceEventsForTransport,
|
|
8180
9051
|
estimateTraceBatchSerializedBytesForTest: estimateTraceBatchSerializedBytes,
|
|
8181
9052
|
sanitizeTraceValueForPrivacyForTest: sanitizeTraceValueForPrivacy,
|