raindrop-ai 0.2.1 → 0.2.2
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/README.md +34 -0
- package/dist/{chunk-UTCK3OAC.mjs → chunk-2EGS66V2.mjs} +624 -65
- package/dist/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +645 -63
- package/dist/index.mjs +29 -2
- package/dist/tracing/index.d.mts +16 -1
- package/dist/tracing/index.d.ts +16 -1
- package/dist/tracing/index.js +620 -63
- package/dist/tracing/index.mjs +1 -1
- package/package.json +5 -3
package/dist/tracing/index.js
CHANGED
|
@@ -35,6 +35,12 @@ __export(tracing_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(tracing_exports);
|
|
37
37
|
|
|
38
|
+
// src/lib/inject-als.ts
|
|
39
|
+
var import_node_async_hooks = require("async_hooks");
|
|
40
|
+
if (!globalThis.RAINDROP_ASYNC_LOCAL_STORAGE) {
|
|
41
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_node_async_hooks.AsyncLocalStorage;
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
// src/tracing/tracer-core.ts
|
|
39
45
|
var import_api4 = require("@opentelemetry/api");
|
|
40
46
|
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
@@ -47,10 +53,10 @@ var import_instrumentation_pinecone = require("@traceloop/instrumentation-pineco
|
|
|
47
53
|
var import_instrumentation_qdrant = require("@traceloop/instrumentation-qdrant");
|
|
48
54
|
var import_instrumentation_together = require("@traceloop/instrumentation-together");
|
|
49
55
|
var import_instrumentation_vertexai = require("@traceloop/instrumentation-vertexai");
|
|
50
|
-
var
|
|
56
|
+
var traceloop4 = __toESM(require("@traceloop/node-server-sdk"));
|
|
51
57
|
var import_weakref = require("weakref");
|
|
52
58
|
|
|
53
|
-
// ../core/dist/chunk-
|
|
59
|
+
// ../core/dist/chunk-ITAZONWL.js
|
|
54
60
|
function normalizeFeatureFlags(input) {
|
|
55
61
|
if (Array.isArray(input)) {
|
|
56
62
|
return Object.fromEntries(input.map((name) => [name, "true"]));
|
|
@@ -407,6 +413,171 @@ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
|
407
413
|
"ai.response.providerMetadata"
|
|
408
414
|
];
|
|
409
415
|
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
416
|
+
function getAsyncLocalStorageCtor() {
|
|
417
|
+
return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
|
|
418
|
+
}
|
|
419
|
+
function getWeakRefCtor() {
|
|
420
|
+
const ctor = globalThis.WeakRef;
|
|
421
|
+
return typeof ctor === "function" ? ctor : void 0;
|
|
422
|
+
}
|
|
423
|
+
var MAX_BINDING_STACK = 128;
|
|
424
|
+
var EMPTY_STACK = [];
|
|
425
|
+
var RoutingContextStorage = class {
|
|
426
|
+
constructor() {
|
|
427
|
+
this._als = null;
|
|
428
|
+
this._enterWithUsable = false;
|
|
429
|
+
this._syncStack = EMPTY_STACK;
|
|
430
|
+
this._adoptionChecked = false;
|
|
431
|
+
this._warnedNoAmbientBind = false;
|
|
432
|
+
}
|
|
433
|
+
maybeAdoptAsyncLocalStorage() {
|
|
434
|
+
if (this._adoptionChecked) return;
|
|
435
|
+
this._adoptionChecked = true;
|
|
436
|
+
const Ctor = getAsyncLocalStorageCtor();
|
|
437
|
+
if (!Ctor) return;
|
|
438
|
+
const als = new Ctor();
|
|
439
|
+
try {
|
|
440
|
+
als.run(EMPTY_STACK, () => {
|
|
441
|
+
});
|
|
442
|
+
} catch (e) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this._als = als;
|
|
446
|
+
if (typeof als.enterWith === "function") {
|
|
447
|
+
try {
|
|
448
|
+
als.enterWith(EMPTY_STACK);
|
|
449
|
+
this._enterWithUsable = true;
|
|
450
|
+
} catch (e) {
|
|
451
|
+
this._enterWithUsable = false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
getStack() {
|
|
456
|
+
var _a;
|
|
457
|
+
this.maybeAdoptAsyncLocalStorage();
|
|
458
|
+
if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
|
|
459
|
+
return this._syncStack;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Persist an ambient stack mutation. Returns false when an ALS is adopted but
|
|
463
|
+
* `enterWith` is unusable (workerd): callers must NOT fall back to the
|
|
464
|
+
* process-shared sync stack, since concurrent isolates would cross-stamp.
|
|
465
|
+
*/
|
|
466
|
+
setAmbientStack(stack) {
|
|
467
|
+
if (this._als) {
|
|
468
|
+
if (!this._enterWithUsable) return false;
|
|
469
|
+
this._als.enterWith(stack);
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
this._syncStack = stack;
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
push(binding) {
|
|
476
|
+
this.maybeAdoptAsyncLocalStorage();
|
|
477
|
+
if (this._als && !this._enterWithUsable) {
|
|
478
|
+
this.warnNoAmbientBindOnce();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
let stack = this.getStack();
|
|
482
|
+
if (stack.length >= MAX_BINDING_STACK) {
|
|
483
|
+
stack = stack.filter((b) => b.isLive());
|
|
484
|
+
if (stack.length >= MAX_BINDING_STACK) {
|
|
485
|
+
stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
this.setAmbientStack([...stack, binding]);
|
|
489
|
+
}
|
|
490
|
+
remove(binding) {
|
|
491
|
+
binding.markDead();
|
|
492
|
+
const stack = this.getStack();
|
|
493
|
+
const pruned = stack.filter((b) => b !== binding && b.isLive());
|
|
494
|
+
if (pruned.length !== stack.length) {
|
|
495
|
+
this.setAmbientStack(pruned);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
runWith(binding, callback) {
|
|
499
|
+
this.maybeAdoptAsyncLocalStorage();
|
|
500
|
+
if (this._als) {
|
|
501
|
+
return this._als.run([...this.getStack(), binding], callback);
|
|
502
|
+
}
|
|
503
|
+
const previous = this._syncStack;
|
|
504
|
+
this._syncStack = [...previous, binding];
|
|
505
|
+
try {
|
|
506
|
+
return callback();
|
|
507
|
+
} finally {
|
|
508
|
+
this._syncStack = previous;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
current() {
|
|
512
|
+
const stack = this.getStack();
|
|
513
|
+
if (stack.length === 0) return void 0;
|
|
514
|
+
const top = stack[stack.length - 1];
|
|
515
|
+
if (top.isLive()) return top;
|
|
516
|
+
const pruned = stack.filter((b) => b.isLive());
|
|
517
|
+
this.setAmbientStack(pruned);
|
|
518
|
+
return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
|
|
519
|
+
}
|
|
520
|
+
warnNoAmbientBindOnce() {
|
|
521
|
+
if (this._warnedNoAmbientBind) return;
|
|
522
|
+
this._warnedNoAmbientBind = true;
|
|
523
|
+
console.warn(
|
|
524
|
+
"[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime."
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
|
|
529
|
+
function routingStorage() {
|
|
530
|
+
const holder = globalThis;
|
|
531
|
+
let storage = holder[ROUTING_STORAGE_KEY];
|
|
532
|
+
if (!storage) {
|
|
533
|
+
storage = new RoutingContextStorage();
|
|
534
|
+
holder[ROUTING_STORAGE_KEY] = storage;
|
|
535
|
+
}
|
|
536
|
+
return storage;
|
|
537
|
+
}
|
|
538
|
+
function makeBoundContext(binding, owner) {
|
|
539
|
+
const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
|
|
540
|
+
const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
|
|
541
|
+
let dead = false;
|
|
542
|
+
return {
|
|
543
|
+
projectId: binding.projectId,
|
|
544
|
+
authHint: binding.authHint,
|
|
545
|
+
isLive() {
|
|
546
|
+
if (dead) return false;
|
|
547
|
+
return ref === void 0 || ref.deref() !== void 0;
|
|
548
|
+
},
|
|
549
|
+
markDead() {
|
|
550
|
+
dead = true;
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function bindRoutingContext(binding, owner) {
|
|
555
|
+
const bound = makeBoundContext(binding, owner);
|
|
556
|
+
try {
|
|
557
|
+
routingStorage().push(bound);
|
|
558
|
+
} catch (e) {
|
|
559
|
+
}
|
|
560
|
+
return bound;
|
|
561
|
+
}
|
|
562
|
+
function unbindRoutingContext(bound) {
|
|
563
|
+
if (!bound || !isBoundContext(bound)) return;
|
|
564
|
+
try {
|
|
565
|
+
routingStorage().remove(bound);
|
|
566
|
+
} catch (e) {
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function withRoutingContext(binding, callback) {
|
|
570
|
+
const bound = makeBoundContext(binding);
|
|
571
|
+
return routingStorage().runWith(bound, callback);
|
|
572
|
+
}
|
|
573
|
+
function currentRoutingBinding() {
|
|
574
|
+
const bound = routingStorage().current();
|
|
575
|
+
if (!bound) return void 0;
|
|
576
|
+
return { projectId: bound.projectId, authHint: bound.authHint };
|
|
577
|
+
}
|
|
578
|
+
function isBoundContext(value) {
|
|
579
|
+
return typeof value.isLive === "function";
|
|
580
|
+
}
|
|
410
581
|
|
|
411
582
|
// ../core/dist/index.node.js
|
|
412
583
|
var import_async_hooks = require("async_hooks");
|
|
@@ -593,7 +764,7 @@ function base64ToHex(base64) {
|
|
|
593
764
|
// package.json
|
|
594
765
|
var package_default = {
|
|
595
766
|
name: "raindrop-ai",
|
|
596
|
-
version: "0.2.
|
|
767
|
+
version: "0.2.2",
|
|
597
768
|
main: "dist/index.js",
|
|
598
769
|
module: "dist/index.mjs",
|
|
599
770
|
types: "dist/index.d.ts",
|
|
@@ -659,6 +830,7 @@ var package_default = {
|
|
|
659
830
|
dependencies: {
|
|
660
831
|
"@opentelemetry/api": "^1.9.0",
|
|
661
832
|
"@opentelemetry/exporter-trace-otlp-http": "^0.57.2",
|
|
833
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.57.2",
|
|
662
834
|
"@traceloop/node-server-sdk": "0.19.0-otel-v1",
|
|
663
835
|
weakref: "^0.2.1",
|
|
664
836
|
zod: "^3.23.8"
|
|
@@ -690,6 +862,7 @@ var package_default = {
|
|
|
690
862
|
"@traceloop/instrumentation-together",
|
|
691
863
|
"@opentelemetry/instrumentation",
|
|
692
864
|
"@opentelemetry/exporter-trace-otlp-http",
|
|
865
|
+
"@opentelemetry/exporter-trace-otlp-proto",
|
|
693
866
|
"@opentelemetry/sdk-trace-base"
|
|
694
867
|
],
|
|
695
868
|
noExternal: [
|
|
@@ -772,6 +945,190 @@ function getActiveTraceContext() {
|
|
|
772
945
|
};
|
|
773
946
|
}
|
|
774
947
|
|
|
948
|
+
// src/tracing/multi-project.ts
|
|
949
|
+
var import_node_crypto = require("crypto");
|
|
950
|
+
var import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
951
|
+
var PROJECT_ID_SPAN_ATTRIBUTE = "raindrop.project_id";
|
|
952
|
+
var AUTH_HINT_SPAN_ATTRIBUTE = "raindrop.auth_hint";
|
|
953
|
+
var EXPORT_SUCCESS = 0;
|
|
954
|
+
var EXPORT_FAILED = 1;
|
|
955
|
+
function authHintForKey(writeKey) {
|
|
956
|
+
if (!writeKey) return void 0;
|
|
957
|
+
return (0, import_node_crypto.createHash)("sha256").update(writeKey, "utf8").digest("hex").slice(0, 8);
|
|
958
|
+
}
|
|
959
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.multiProjectState");
|
|
960
|
+
function getState() {
|
|
961
|
+
const holder = globalThis;
|
|
962
|
+
let state = holder[STATE_KEY];
|
|
963
|
+
if (!state) {
|
|
964
|
+
state = {
|
|
965
|
+
foreignClientSeen: false,
|
|
966
|
+
seenClientHints: /* @__PURE__ */ new Set(),
|
|
967
|
+
ownerAuthHint: void 0,
|
|
968
|
+
ownerProjectId: void 0,
|
|
969
|
+
pipelineClaimed: false,
|
|
970
|
+
traceloopConfiguredWithoutClaim: false,
|
|
971
|
+
multiKeyWarned: false,
|
|
972
|
+
pendingMultiKeyWarning: false
|
|
973
|
+
};
|
|
974
|
+
holder[STATE_KEY] = state;
|
|
975
|
+
}
|
|
976
|
+
return state;
|
|
977
|
+
}
|
|
978
|
+
function emitMultiKeyWarningOnce() {
|
|
979
|
+
const state = getState();
|
|
980
|
+
if (state.multiKeyWarned) return;
|
|
981
|
+
state.multiKeyWarned = true;
|
|
982
|
+
state.pendingMultiKeyWarning = false;
|
|
983
|
+
console.warn(
|
|
984
|
+
"[raindrop] Multiple API keys detected in one process. The OTel export guard now requires positive attribution: spans not bound to any client (no begin()/asCurrent()) are dropped, and spans bound to a non-owner key are dropped. Manual events are unaffected."
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
function registerClientKey(authHint, tracingEnabled = false) {
|
|
988
|
+
if (!authHint) return;
|
|
989
|
+
const state = getState();
|
|
990
|
+
state.seenClientHints.add(authHint);
|
|
991
|
+
if (state.seenClientHints.size > 1) {
|
|
992
|
+
state.foreignClientSeen = true;
|
|
993
|
+
if (tracingEnabled || state.pipelineClaimed) {
|
|
994
|
+
emitMultiKeyWarningOnce();
|
|
995
|
+
} else {
|
|
996
|
+
state.pendingMultiKeyWarning = true;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
function markForeignClient() {
|
|
1001
|
+
getState().foreignClientSeen = true;
|
|
1002
|
+
}
|
|
1003
|
+
function foreignClientSeen() {
|
|
1004
|
+
return getState().foreignClientSeen;
|
|
1005
|
+
}
|
|
1006
|
+
function stampSpan(span, projectId, authHint) {
|
|
1007
|
+
try {
|
|
1008
|
+
if (projectId) {
|
|
1009
|
+
span.setAttribute(PROJECT_ID_SPAN_ATTRIBUTE, projectId);
|
|
1010
|
+
}
|
|
1011
|
+
if (authHint && foreignClientSeen()) {
|
|
1012
|
+
span.setAttribute(AUTH_HINT_SPAN_ATTRIBUTE, authHint);
|
|
1013
|
+
}
|
|
1014
|
+
} catch (e) {
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
var RaindropContextSpanProcessor = class {
|
|
1018
|
+
onStart(span) {
|
|
1019
|
+
try {
|
|
1020
|
+
const bound = currentRoutingBinding();
|
|
1021
|
+
if (!bound) return;
|
|
1022
|
+
stampSpan(span, bound.projectId, bound.authHint);
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
onEnd() {
|
|
1027
|
+
}
|
|
1028
|
+
async forceFlush() {
|
|
1029
|
+
}
|
|
1030
|
+
async shutdown() {
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
var GuardedSpanExporter = class {
|
|
1034
|
+
constructor(inner, ownerAuthHint) {
|
|
1035
|
+
this.droppedTotal = 0;
|
|
1036
|
+
this.inner = inner;
|
|
1037
|
+
this.ownerAuthHint = ownerAuthHint;
|
|
1038
|
+
}
|
|
1039
|
+
partition(spans) {
|
|
1040
|
+
var _a;
|
|
1041
|
+
const allowed = [];
|
|
1042
|
+
let dropped = 0;
|
|
1043
|
+
const requirePositive = foreignClientSeen();
|
|
1044
|
+
for (const span of spans) {
|
|
1045
|
+
const attrs = (_a = span.attributes) != null ? _a : {};
|
|
1046
|
+
const hint = attrs[AUTH_HINT_SPAN_ATTRIBUTE];
|
|
1047
|
+
const keep = requirePositive ? hint === this.ownerAuthHint : hint === void 0 || hint === this.ownerAuthHint;
|
|
1048
|
+
if (keep) {
|
|
1049
|
+
allowed.push(span);
|
|
1050
|
+
} else {
|
|
1051
|
+
dropped += 1;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return { allowed, dropped };
|
|
1055
|
+
}
|
|
1056
|
+
warnDropped(dropped) {
|
|
1057
|
+
this.droppedTotal += dropped;
|
|
1058
|
+
const total = this.droppedTotal;
|
|
1059
|
+
rateLimitedLog(
|
|
1060
|
+
"js-sdk.multi-project.dropped",
|
|
1061
|
+
() => console.warn(
|
|
1062
|
+
`[raindrop] Dropped ${dropped} span(s) produced under a different API key than the tracing pipeline's (total dropped: ${total}). One process has ONE OTel pipeline authenticating with the first tracing-enabled client's key; tracing for a different key/org in the same process is unsupported \u2014 run it in its own process. Manual events (track_ai/begin/finish/signals/identify) are unaffected.`
|
|
1063
|
+
)
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
export(spans, resultCallback) {
|
|
1067
|
+
let allowed;
|
|
1068
|
+
let dropped;
|
|
1069
|
+
try {
|
|
1070
|
+
({ allowed, dropped } = this.partition(spans));
|
|
1071
|
+
} catch (e) {
|
|
1072
|
+
if (foreignClientSeen()) {
|
|
1073
|
+
this.warnDropped(spans.length);
|
|
1074
|
+
resultCallback({ code: EXPORT_FAILED });
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
this.inner.export(spans, resultCallback);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (dropped) {
|
|
1081
|
+
this.warnDropped(dropped);
|
|
1082
|
+
}
|
|
1083
|
+
if (allowed.length === 0) {
|
|
1084
|
+
resultCallback({ code: EXPORT_SUCCESS });
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
this.inner.export(allowed, resultCallback);
|
|
1088
|
+
}
|
|
1089
|
+
shutdown() {
|
|
1090
|
+
return this.inner.shutdown();
|
|
1091
|
+
}
|
|
1092
|
+
forceFlush() {
|
|
1093
|
+
return this.inner.forceFlush ? this.inner.forceFlush() : Promise.resolve();
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
function buildGuardedExporter(baseUrl, headers, ownerAuthHint) {
|
|
1097
|
+
const inner = new import_exporter_trace_otlp_proto.OTLPTraceExporter({
|
|
1098
|
+
url: `${baseUrl}/v1/traces`,
|
|
1099
|
+
headers
|
|
1100
|
+
});
|
|
1101
|
+
return new GuardedSpanExporter(inner, ownerAuthHint);
|
|
1102
|
+
}
|
|
1103
|
+
function pipelineOwnerHint() {
|
|
1104
|
+
return getState().ownerAuthHint;
|
|
1105
|
+
}
|
|
1106
|
+
function claimPipeline(authHint, projectId) {
|
|
1107
|
+
const state = getState();
|
|
1108
|
+
if (state.pipelineClaimed) {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
state.pipelineClaimed = true;
|
|
1112
|
+
state.ownerAuthHint = authHint;
|
|
1113
|
+
state.ownerProjectId = projectId;
|
|
1114
|
+
if (state.foreignClientSeen && state.pendingMultiKeyWarning) {
|
|
1115
|
+
emitMultiKeyWarningOnce();
|
|
1116
|
+
}
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
function releasePipelineClaim() {
|
|
1120
|
+
const state = getState();
|
|
1121
|
+
state.pipelineClaimed = false;
|
|
1122
|
+
state.ownerAuthHint = void 0;
|
|
1123
|
+
state.ownerProjectId = void 0;
|
|
1124
|
+
}
|
|
1125
|
+
function markTraceloopConfiguredWithoutClaim() {
|
|
1126
|
+
getState().traceloopConfiguredWithoutClaim = true;
|
|
1127
|
+
}
|
|
1128
|
+
function traceloopConfiguredWithoutClaim() {
|
|
1129
|
+
return getState().traceloopConfiguredWithoutClaim;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
775
1132
|
// src/tracing/direct/shipper.ts
|
|
776
1133
|
function serializeAssociationValue(value) {
|
|
777
1134
|
return stringifyBounded(value);
|
|
@@ -831,7 +1188,11 @@ var DirectTraceShipper = class {
|
|
|
831
1188
|
attrString2("traceloop.entity.input", params.input),
|
|
832
1189
|
attrString2("traceloop.entity.output", params.output),
|
|
833
1190
|
// Duration tracking
|
|
834
|
-
attrInt2("traceloop.entity.duration_ms", params.durationMs)
|
|
1191
|
+
attrInt2("traceloop.entity.duration_ms", params.durationMs),
|
|
1192
|
+
// Per-span project routing: the ingest boundary routes on this attribute
|
|
1193
|
+
// (org-validated) and falls back to the request's project header. Direct
|
|
1194
|
+
// spans ship on this instance's own key, so no cross-key auth hint.
|
|
1195
|
+
attrString2(PROJECT_ID_SPAN_ATTRIBUTE, this.projectId)
|
|
835
1196
|
];
|
|
836
1197
|
for (const [key, value] of Object.entries(params.properties)) {
|
|
837
1198
|
if (value === void 0) {
|
|
@@ -1129,12 +1490,27 @@ var DirectToolSpan = class {
|
|
|
1129
1490
|
}
|
|
1130
1491
|
};
|
|
1131
1492
|
var LiveInteraction = class {
|
|
1132
|
-
constructor(context5, traceId, analytics, directShipper) {
|
|
1493
|
+
constructor(context5, traceId, analytics, directShipper, routing) {
|
|
1133
1494
|
this.context = context5;
|
|
1134
1495
|
this.analytics = analytics;
|
|
1135
1496
|
this.tracer = import_api2.trace.getTracer("traceloop.tracer");
|
|
1136
1497
|
this.traceId = traceId;
|
|
1137
1498
|
this.directShipper = directShipper;
|
|
1499
|
+
this.routing = routing;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Record the routing binding this interaction pushed onto the context stack
|
|
1503
|
+
* (via {@link bindRoutingContext} in `begin()`) so {@link finish} removes
|
|
1504
|
+
* exactly its own entry — non-LIFO safe, and leaving any still-open sibling's
|
|
1505
|
+
* binding intact.
|
|
1506
|
+
*/
|
|
1507
|
+
setBoundContext(bound) {
|
|
1508
|
+
this.boundContext = bound;
|
|
1509
|
+
}
|
|
1510
|
+
/** Stamp a manually-created OTel span with this interaction's routing. */
|
|
1511
|
+
stampRouting(span) {
|
|
1512
|
+
if (!this.routing) return;
|
|
1513
|
+
stampSpan(span, this.routing.projectId, this.routing.authHint);
|
|
1138
1514
|
}
|
|
1139
1515
|
ensureTraceId(preferred) {
|
|
1140
1516
|
if (preferred) {
|
|
@@ -1203,8 +1579,9 @@ var LiveInteraction = class {
|
|
|
1203
1579
|
import_api2.context.active(),
|
|
1204
1580
|
import_api2.trace.wrapSpanContext(spanContext)
|
|
1205
1581
|
);
|
|
1582
|
+
const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
|
|
1206
1583
|
try {
|
|
1207
|
-
const result = await import_api2.context.with(wrappedContext,
|
|
1584
|
+
const result = await import_api2.context.with(wrappedContext, invoke);
|
|
1208
1585
|
const endTimeMs = Date.now();
|
|
1209
1586
|
this.directShipper.sendSpan({
|
|
1210
1587
|
name: taskName,
|
|
@@ -1239,11 +1616,17 @@ var LiveInteraction = class {
|
|
|
1239
1616
|
}
|
|
1240
1617
|
}
|
|
1241
1618
|
const wrappedFn = (...fnArgs) => {
|
|
1242
|
-
|
|
1243
|
-
const activeTraceId =
|
|
1619
|
+
const activeSpan = import_api2.trace.getSpan(import_api2.context.active());
|
|
1620
|
+
const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
|
|
1244
1621
|
if (activeTraceId) {
|
|
1245
1622
|
this.traceId = activeTraceId;
|
|
1246
1623
|
}
|
|
1624
|
+
if (activeSpan) {
|
|
1625
|
+
this.stampRouting(activeSpan);
|
|
1626
|
+
}
|
|
1627
|
+
if (this.routing) {
|
|
1628
|
+
return withRoutingContext(this.routing, () => fn.apply(thisArg, fnArgs));
|
|
1629
|
+
}
|
|
1247
1630
|
return fn.apply(thisArg, fnArgs);
|
|
1248
1631
|
};
|
|
1249
1632
|
return traceloop.withTask(
|
|
@@ -1255,7 +1638,6 @@ var LiveInteraction = class {
|
|
|
1255
1638
|
suppressTracing: params.suppressTracing
|
|
1256
1639
|
},
|
|
1257
1640
|
wrappedFn,
|
|
1258
|
-
void 0,
|
|
1259
1641
|
...args
|
|
1260
1642
|
);
|
|
1261
1643
|
}
|
|
@@ -1292,10 +1674,11 @@ var LiveInteraction = class {
|
|
|
1292
1674
|
import_api2.context.active(),
|
|
1293
1675
|
import_api2.trace.wrapSpanContext(spanContext)
|
|
1294
1676
|
);
|
|
1677
|
+
const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
|
|
1295
1678
|
let result;
|
|
1296
1679
|
let error;
|
|
1297
1680
|
try {
|
|
1298
|
-
result = await import_api2.context.with(wrappedContext,
|
|
1681
|
+
result = await import_api2.context.with(wrappedContext, invoke);
|
|
1299
1682
|
} catch (err) {
|
|
1300
1683
|
error = err instanceof Error ? err.message : String(err);
|
|
1301
1684
|
const endTimeMs2 = Date.now();
|
|
@@ -1363,6 +1746,9 @@ var LiveInteraction = class {
|
|
|
1363
1746
|
const activeSpan = import_api2.trace.getSpan(import_api2.context.active());
|
|
1364
1747
|
const ctx = activeSpan == null ? void 0 : activeSpan.spanContext();
|
|
1365
1748
|
const liveSpanId = ctx && (0, import_api2.isValidSpanId)(ctx.spanId) ? ctx.spanId : void 0;
|
|
1749
|
+
if (activeSpan) {
|
|
1750
|
+
this.stampRouting(activeSpan);
|
|
1751
|
+
}
|
|
1366
1752
|
emitLive({
|
|
1367
1753
|
type: "tool_start",
|
|
1368
1754
|
spanId: liveSpanId,
|
|
@@ -1371,7 +1757,7 @@ var LiveInteraction = class {
|
|
|
1371
1757
|
metadata: params.inputParameters ? { args: params.inputParameters } : void 0
|
|
1372
1758
|
});
|
|
1373
1759
|
try {
|
|
1374
|
-
const r = await fn.apply(thisArg, wrappedArgs);
|
|
1760
|
+
const r = this.routing ? await withRoutingContext(this.routing, () => fn.apply(thisArg, wrappedArgs)) : await fn.apply(thisArg, wrappedArgs);
|
|
1375
1761
|
emitLive({
|
|
1376
1762
|
type: "tool_result",
|
|
1377
1763
|
spanId: liveSpanId,
|
|
@@ -1407,7 +1793,6 @@ var LiveInteraction = class {
|
|
|
1407
1793
|
suppressTracing: params.suppressTracing
|
|
1408
1794
|
},
|
|
1409
1795
|
wrapped,
|
|
1410
|
-
void 0,
|
|
1411
1796
|
...args
|
|
1412
1797
|
);
|
|
1413
1798
|
}
|
|
@@ -1418,6 +1803,7 @@ var LiveInteraction = class {
|
|
|
1418
1803
|
setAssociationProperties(span, contextProperties);
|
|
1419
1804
|
span.setAttribute("traceloop.span.kind", "task");
|
|
1420
1805
|
setAssociationProperties(span, properties);
|
|
1806
|
+
this.stampRouting(span);
|
|
1421
1807
|
return span;
|
|
1422
1808
|
}
|
|
1423
1809
|
startToolSpan(params) {
|
|
@@ -1459,6 +1845,7 @@ var LiveInteraction = class {
|
|
|
1459
1845
|
setAssociationProperties(span, contextProperties);
|
|
1460
1846
|
span.setAttribute("traceloop.span.kind", "tool");
|
|
1461
1847
|
setAssociationProperties(span, properties);
|
|
1848
|
+
this.stampRouting(span);
|
|
1462
1849
|
if (inputParameters !== void 0) {
|
|
1463
1850
|
span.setAttribute("traceloop.entity.input", serializeSpanValue(inputParameters));
|
|
1464
1851
|
}
|
|
@@ -1561,6 +1948,8 @@ var LiveInteraction = class {
|
|
|
1561
1948
|
}
|
|
1562
1949
|
async finish(resultEvent) {
|
|
1563
1950
|
var _a, _b, _c, _d;
|
|
1951
|
+
unbindRoutingContext(this.boundContext);
|
|
1952
|
+
this.boundContext = void 0;
|
|
1564
1953
|
if (!this.traceId) {
|
|
1565
1954
|
this.resolveLiveTraceId();
|
|
1566
1955
|
}
|
|
@@ -1648,6 +2037,7 @@ var LiveInteraction = class {
|
|
|
1648
2037
|
setAssociationProperties(span, contextProperties);
|
|
1649
2038
|
span.setAttribute("traceloop.span.kind", "tool");
|
|
1650
2039
|
setAssociationProperties(span, properties);
|
|
2040
|
+
this.stampRouting(span);
|
|
1651
2041
|
if (input !== void 0) {
|
|
1652
2042
|
span.setAttribute("traceloop.entity.input", stringifyBounded(input));
|
|
1653
2043
|
}
|
|
@@ -1693,10 +2083,16 @@ var LiveInteraction = class {
|
|
|
1693
2083
|
var import_api3 = require("@opentelemetry/api");
|
|
1694
2084
|
var traceloop2 = __toESM(require("@traceloop/node-server-sdk"));
|
|
1695
2085
|
var LiveTracer = class {
|
|
1696
|
-
constructor(globalProperties, directShipper) {
|
|
2086
|
+
constructor(globalProperties, directShipper, routing) {
|
|
1697
2087
|
this.tracer = import_api3.trace.getTracer("traceloop.tracer");
|
|
1698
2088
|
this.globalProperties = globalProperties || {};
|
|
1699
2089
|
this.directShipper = directShipper;
|
|
2090
|
+
this.routing = routing;
|
|
2091
|
+
}
|
|
2092
|
+
/** Stamp a manually-created OTel span with this tracer's client routing. */
|
|
2093
|
+
stampRouting(span) {
|
|
2094
|
+
if (!this.routing) return;
|
|
2095
|
+
stampSpan(span, this.routing.projectId, this.routing.authHint);
|
|
1700
2096
|
}
|
|
1701
2097
|
emitLiveEvent(event) {
|
|
1702
2098
|
var _a, _b;
|
|
@@ -1719,6 +2115,16 @@ var LiveTracer = class {
|
|
|
1719
2115
|
...params.properties || {}
|
|
1720
2116
|
};
|
|
1721
2117
|
const inputParameters = typeof params === "string" ? void 0 : params.inputParameters;
|
|
2118
|
+
const wrappedFn = (...fnArgs) => {
|
|
2119
|
+
const activeSpan = import_api3.trace.getSpan(import_api3.context.active());
|
|
2120
|
+
if (activeSpan) {
|
|
2121
|
+
this.stampRouting(activeSpan);
|
|
2122
|
+
}
|
|
2123
|
+
if (this.routing) {
|
|
2124
|
+
return withRoutingContext(this.routing, () => fn.apply(thisArg, fnArgs));
|
|
2125
|
+
}
|
|
2126
|
+
return fn.apply(thisArg, fnArgs);
|
|
2127
|
+
};
|
|
1722
2128
|
return traceloop2.withTask(
|
|
1723
2129
|
{
|
|
1724
2130
|
name: taskName,
|
|
@@ -1727,8 +2133,7 @@ var LiveTracer = class {
|
|
|
1727
2133
|
traceContent: params.traceContent,
|
|
1728
2134
|
suppressTracing: params.suppressTracing
|
|
1729
2135
|
},
|
|
1730
|
-
|
|
1731
|
-
thisArg,
|
|
2136
|
+
wrappedFn,
|
|
1732
2137
|
...args
|
|
1733
2138
|
);
|
|
1734
2139
|
}
|
|
@@ -1777,6 +2182,7 @@ var LiveTracer = class {
|
|
|
1777
2182
|
const span = this.tracer.startSpan(name, {
|
|
1778
2183
|
startTime: startTimeMs
|
|
1779
2184
|
});
|
|
2185
|
+
this.stampRouting(span);
|
|
1780
2186
|
Object.entries(this.globalProperties).forEach(([key, value]) => {
|
|
1781
2187
|
span.setAttribute("traceloop.association.properties." + key, String(value));
|
|
1782
2188
|
});
|
|
@@ -1821,6 +2227,59 @@ var LiveTracer = class {
|
|
|
1821
2227
|
}
|
|
1822
2228
|
};
|
|
1823
2229
|
|
|
2230
|
+
// src/tracing/exit-flush.ts
|
|
2231
|
+
var traceloop3 = __toESM(require("@traceloop/node-server-sdk"));
|
|
2232
|
+
var STATE_KEY2 = /* @__PURE__ */ Symbol.for("raindrop.tracing.exitFlushState");
|
|
2233
|
+
function getState2() {
|
|
2234
|
+
const holder = globalThis;
|
|
2235
|
+
let state = holder[STATE_KEY2];
|
|
2236
|
+
if (!state) {
|
|
2237
|
+
state = {
|
|
2238
|
+
clientFlushers: /* @__PURE__ */ new Set(),
|
|
2239
|
+
pipelineFlushNeeded: false,
|
|
2240
|
+
handlerRegistered: false
|
|
2241
|
+
};
|
|
2242
|
+
holder[STATE_KEY2] = state;
|
|
2243
|
+
}
|
|
2244
|
+
return state;
|
|
2245
|
+
}
|
|
2246
|
+
function ensureExitHandler(state) {
|
|
2247
|
+
if (state.handlerRegistered) return;
|
|
2248
|
+
if (typeof process === "undefined" || typeof process.once !== "function") return;
|
|
2249
|
+
state.handlerRegistered = true;
|
|
2250
|
+
process.once("beforeExit", () => {
|
|
2251
|
+
void flushAll();
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
function registerClientFlusher(flush) {
|
|
2255
|
+
const state = getState2();
|
|
2256
|
+
state.clientFlushers.add(flush);
|
|
2257
|
+
ensureExitHandler(state);
|
|
2258
|
+
return () => {
|
|
2259
|
+
state.clientFlushers.delete(flush);
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
function markPipelineForExitFlush() {
|
|
2263
|
+
const state = getState2();
|
|
2264
|
+
state.pipelineFlushNeeded = true;
|
|
2265
|
+
ensureExitHandler(state);
|
|
2266
|
+
}
|
|
2267
|
+
async function flushAll() {
|
|
2268
|
+
const state = getState2();
|
|
2269
|
+
const tasks = [];
|
|
2270
|
+
for (const flush of state.clientFlushers) {
|
|
2271
|
+
tasks.push(Promise.resolve().then(flush).catch(() => {
|
|
2272
|
+
}));
|
|
2273
|
+
}
|
|
2274
|
+
if (state.pipelineFlushNeeded) {
|
|
2275
|
+
tasks.push(
|
|
2276
|
+
Promise.resolve().then(() => traceloop3.forceFlush()).catch(() => {
|
|
2277
|
+
})
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
await Promise.all(tasks);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
1824
2283
|
// src/tracing/tracer-core.ts
|
|
1825
2284
|
if (localDebuggerEnabled()) {
|
|
1826
2285
|
if (!process.env.OTEL_EXPORTER_OTLP_PROTOCOL) {
|
|
@@ -1929,6 +2388,29 @@ function resolveLocalDebuggerExporterBaseUrl(baseUrlOpt) {
|
|
|
1929
2388
|
function createLocalDebuggerOtlpExporter(config) {
|
|
1930
2389
|
return new import_exporter_trace_otlp_http.OTLPTraceExporter(config);
|
|
1931
2390
|
}
|
|
2391
|
+
function parseTraceloopHeaders(value) {
|
|
2392
|
+
const headers = {};
|
|
2393
|
+
for (const entry of value.split(",")) {
|
|
2394
|
+
const keyPairPart = entry.split(";")[0];
|
|
2395
|
+
const sep = keyPairPart.indexOf("=");
|
|
2396
|
+
if (sep <= 0) continue;
|
|
2397
|
+
try {
|
|
2398
|
+
const key = decodeURIComponent(keyPairPart.slice(0, sep).trim());
|
|
2399
|
+
const val = decodeURIComponent(keyPairPart.slice(sep + 1).trim());
|
|
2400
|
+
if (key.length > 0 && val.length > 0) headers[key] = val;
|
|
2401
|
+
} catch (e) {
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
return headers;
|
|
2405
|
+
}
|
|
2406
|
+
function traceloopDefaultHeaders(writeKey) {
|
|
2407
|
+
const env = process.env.TRACELOOP_HEADERS;
|
|
2408
|
+
if (env) {
|
|
2409
|
+
const parsed = parseTraceloopHeaders(env);
|
|
2410
|
+
if (Object.keys(parsed).length > 0) return parsed;
|
|
2411
|
+
}
|
|
2412
|
+
return { Authorization: `Bearer ${writeKey}` };
|
|
2413
|
+
}
|
|
1932
2414
|
function createLocalDebuggerSpanProcessor(options) {
|
|
1933
2415
|
const localDebuggerUrl = resolveLocalDebuggerExporterBaseUrl(options.localDebuggerUrlOpt);
|
|
1934
2416
|
if (!localDebuggerUrl) {
|
|
@@ -1939,7 +2421,7 @@ function createLocalDebuggerSpanProcessor(options) {
|
|
|
1939
2421
|
headers: {}
|
|
1940
2422
|
});
|
|
1941
2423
|
try {
|
|
1942
|
-
return
|
|
2424
|
+
return traceloop4.createSpanProcessor({
|
|
1943
2425
|
apiKey: options.writeKey,
|
|
1944
2426
|
baseUrl: localDebuggerUrl,
|
|
1945
2427
|
disableBatch: options.disableBatching,
|
|
@@ -1955,12 +2437,6 @@ function createLocalDebuggerSpanProcessor(options) {
|
|
|
1955
2437
|
return void 0;
|
|
1956
2438
|
}
|
|
1957
2439
|
}
|
|
1958
|
-
function mergeWithBestEffortLocalDebuggerProcessor(processor, localDebuggerProcessor) {
|
|
1959
|
-
if (!localDebuggerProcessor) {
|
|
1960
|
-
return processor;
|
|
1961
|
-
}
|
|
1962
|
-
return new CompositeSpanProcessor(processor ? [processor] : [], [localDebuggerProcessor]);
|
|
1963
|
-
}
|
|
1964
2440
|
var warnedSpanNormalizeFailure = false;
|
|
1965
2441
|
var INVALID_OTEL_SPAN_ID = "0000000000000000";
|
|
1966
2442
|
function normalizeSpanForV1Exporter(span) {
|
|
@@ -2260,13 +2736,12 @@ function createManualInstrumentations(instrumentModules) {
|
|
|
2260
2736
|
}
|
|
2261
2737
|
return instrumentations;
|
|
2262
2738
|
}
|
|
2263
|
-
var activeInteractions = new import_weakref.WeakValueMap();
|
|
2264
|
-
var activeInteractionsByEventId = new import_weakref.WeakValueMap();
|
|
2265
2739
|
function getCurrentTraceId() {
|
|
2266
2740
|
var _a;
|
|
2267
2741
|
return (_a = import_api4.trace.getSpan(import_api4.context.active())) == null ? void 0 : _a.spanContext().traceId;
|
|
2268
2742
|
}
|
|
2269
2743
|
var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
2744
|
+
var _a;
|
|
2270
2745
|
process.env.TRACELOOP_TELEMETRY = "false";
|
|
2271
2746
|
const {
|
|
2272
2747
|
logLevel,
|
|
@@ -2281,12 +2756,15 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2281
2756
|
...otherOptions
|
|
2282
2757
|
} = options;
|
|
2283
2758
|
const cloudEnabledResolved = cloudEnabled != null ? cloudEnabled : true;
|
|
2759
|
+
const authHint = authHintForKey(writeKey);
|
|
2760
|
+
const clientTracingExports = !useExternalOtel && cloudEnabledResolved && tracingEnabled !== false;
|
|
2761
|
+
registerClientKey(authHint, clientTracingExports);
|
|
2284
2762
|
const withProjectIdHeaders = (existing) => {
|
|
2285
2763
|
if (!projectId) {
|
|
2286
2764
|
return existing;
|
|
2287
2765
|
}
|
|
2288
2766
|
return {
|
|
2289
|
-
|
|
2767
|
+
...traceloopDefaultHeaders(writeKey),
|
|
2290
2768
|
...existing != null ? existing : {},
|
|
2291
2769
|
...projectIdHeaders(projectId)
|
|
2292
2770
|
};
|
|
@@ -2308,9 +2786,12 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2308
2786
|
projectId
|
|
2309
2787
|
}) : void 0;
|
|
2310
2788
|
let traceloopInitialized = false;
|
|
2789
|
+
let claimedPipeline = false;
|
|
2790
|
+
const activeInteractions = new import_weakref.WeakValueMap();
|
|
2791
|
+
const activeInteractionsByEventId = new import_weakref.WeakValueMap();
|
|
2311
2792
|
try {
|
|
2312
2793
|
if (useExternalOtel) {
|
|
2313
|
-
|
|
2794
|
+
traceloop4.initialize({
|
|
2314
2795
|
baseUrl: apiUrl,
|
|
2315
2796
|
apiKey: writeKey,
|
|
2316
2797
|
logLevel,
|
|
@@ -2322,6 +2803,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2322
2803
|
// DO NOT create NodeSDK - customer has their own
|
|
2323
2804
|
});
|
|
2324
2805
|
traceloopInitialized = true;
|
|
2806
|
+
markTraceloopConfiguredWithoutClaim();
|
|
2325
2807
|
if (isDebug) {
|
|
2326
2808
|
console.log(
|
|
2327
2809
|
"[raindrop] External OTEL mode active. Add raindrop.createSpanProcessor() to your NodeSDK's spanProcessors. Register instrumentations (AnthropicInstrumentation, etc.) on your NodeSDK."
|
|
@@ -2338,12 +2820,12 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2338
2820
|
const localDebuggerProcessor = createLocalDebuggerSpanProcessor({
|
|
2339
2821
|
writeKey,
|
|
2340
2822
|
disableBatching,
|
|
2341
|
-
allowedInstrumentationLibraries:
|
|
2823
|
+
allowedInstrumentationLibraries: traceloop4.ALL_INSTRUMENTATION_LIBRARIES,
|
|
2342
2824
|
localDebuggerUrlOpt: localDebuggerUrl
|
|
2343
2825
|
});
|
|
2344
2826
|
if (!cloudEnabledResolved) {
|
|
2345
2827
|
if (localDebuggerProcessor) {
|
|
2346
|
-
|
|
2828
|
+
traceloop4.initialize({
|
|
2347
2829
|
baseUrl: apiUrl,
|
|
2348
2830
|
apiKey: writeKey,
|
|
2349
2831
|
...otherOptions,
|
|
@@ -2354,8 +2836,9 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2354
2836
|
processor: localDebuggerProcessor
|
|
2355
2837
|
});
|
|
2356
2838
|
traceloopInitialized = true;
|
|
2839
|
+
markTraceloopConfiguredWithoutClaim();
|
|
2357
2840
|
} else {
|
|
2358
|
-
|
|
2841
|
+
traceloop4.initialize({
|
|
2359
2842
|
baseUrl: apiUrl,
|
|
2360
2843
|
apiKey: writeKey,
|
|
2361
2844
|
logLevel,
|
|
@@ -2363,33 +2846,91 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2363
2846
|
tracingEnabled: false
|
|
2364
2847
|
});
|
|
2365
2848
|
traceloopInitialized = true;
|
|
2849
|
+
markTraceloopConfiguredWithoutClaim();
|
|
2366
2850
|
}
|
|
2851
|
+
} else if (tracingEnabled === false) {
|
|
2852
|
+
traceloopInitialized = false;
|
|
2367
2853
|
} else {
|
|
2368
|
-
const
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
)
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
})
|
|
2384
|
-
|
|
2854
|
+
const claimed = claimPipeline(authHint, projectId);
|
|
2855
|
+
claimedPipeline = claimed;
|
|
2856
|
+
const ownerHint = pipelineOwnerHint();
|
|
2857
|
+
if (!claimed && authHint !== ownerHint) {
|
|
2858
|
+
markForeignClient();
|
|
2859
|
+
console.warn(
|
|
2860
|
+
"[raindrop] The OTel tracing pipeline is already initialized with a DIFFERENT api key. One process supports one tracing key/org: auto-instrumented spans from this client will be dropped at export, and spans not bound to any client (no begin()/asCurrent()) will be dropped as unattributable (manual events are unaffected and route normally). Run this client in its own process for tracing."
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
if (claimed && traceloopConfiguredWithoutClaim()) {
|
|
2864
|
+
releasePipelineClaim();
|
|
2865
|
+
claimedPipeline = false;
|
|
2866
|
+
console.warn(
|
|
2867
|
+
"[raindrop] Cannot initialize the cloud tracing pipeline: OpenTelemetry was already configured in this process by an earlier Raindrop client (external-OTEL mode, or a local-only/Workshop client with no write key). Traceloop's tracer provider is fixed once per process, so this client's auto-instrumented spans will NOT be exported. Construct the cloud tracing-enabled client FIRST, or run it in its own process. Manual events (track_ai/begin/finish/signals/identify) are unaffected."
|
|
2868
|
+
);
|
|
2869
|
+
} else if (claimed) {
|
|
2870
|
+
const exporterHeaders = (_a = withProjectIdHeaders(otherOptions.headers)) != null ? _a : traceloopDefaultHeaders(writeKey);
|
|
2871
|
+
let guardedExporter;
|
|
2872
|
+
try {
|
|
2873
|
+
const callerExporter = otherOptions.exporter;
|
|
2874
|
+
guardedExporter = callerExporter ? new GuardedSpanExporter(callerExporter, ownerHint) : buildGuardedExporter(apiUrl, exporterHeaders, ownerHint);
|
|
2875
|
+
} catch (guardError) {
|
|
2876
|
+
releasePipelineClaim();
|
|
2877
|
+
claimedPipeline = false;
|
|
2878
|
+
console.warn(
|
|
2879
|
+
`[raindrop] could not install the guarded span exporter (${guardError instanceof Error ? guardError.message : guardError}); disabling tracing rather than running an unguarded pipeline. Manual events are unaffected.`
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
if (guardedExporter) {
|
|
2883
|
+
const contextProcessor = new RaindropContextSpanProcessor();
|
|
2884
|
+
const callerProcessor = otherOptions.processor;
|
|
2885
|
+
const processor = new CompositeSpanProcessor(
|
|
2886
|
+
callerProcessor ? [contextProcessor, callerProcessor] : [contextProcessor],
|
|
2887
|
+
localDebuggerProcessor ? [localDebuggerProcessor] : []
|
|
2888
|
+
);
|
|
2889
|
+
try {
|
|
2890
|
+
traceloop4.initialize({
|
|
2891
|
+
baseUrl: apiUrl,
|
|
2892
|
+
apiKey: writeKey,
|
|
2893
|
+
...otherOptions,
|
|
2894
|
+
logLevel,
|
|
2895
|
+
silenceInitializationMessage: true,
|
|
2896
|
+
// This branch only runs when tracing is enabled (the disabled
|
|
2897
|
+
// case is handled above), so tracing is always on here.
|
|
2898
|
+
tracingEnabled: true,
|
|
2899
|
+
disableBatch: disableBatching,
|
|
2900
|
+
processor,
|
|
2901
|
+
exporter: guardedExporter
|
|
2902
|
+
});
|
|
2903
|
+
traceloopInitialized = true;
|
|
2904
|
+
} catch (initError) {
|
|
2905
|
+
releasePipelineClaim();
|
|
2906
|
+
claimedPipeline = false;
|
|
2907
|
+
throw initError;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
} else {
|
|
2911
|
+
traceloopInitialized = true;
|
|
2912
|
+
}
|
|
2385
2913
|
}
|
|
2386
2914
|
}
|
|
2387
2915
|
} catch (error) {
|
|
2916
|
+
if (claimedPipeline && !traceloopInitialized) {
|
|
2917
|
+
releasePipelineClaim();
|
|
2918
|
+
claimedPipeline = false;
|
|
2919
|
+
}
|
|
2388
2920
|
console.warn(
|
|
2389
2921
|
"[raindrop] Failed to initialize traceloop. Spans will not be sent.",
|
|
2390
2922
|
error instanceof Error ? error.message : error
|
|
2391
2923
|
);
|
|
2392
2924
|
}
|
|
2925
|
+
if (traceloopInitialized) {
|
|
2926
|
+
markPipelineForExitFlush();
|
|
2927
|
+
}
|
|
2928
|
+
const deregisterExitFlusher = directShipper ? registerClientFlusher(async () => {
|
|
2929
|
+
try {
|
|
2930
|
+
await directShipper.flush();
|
|
2931
|
+
} catch (e) {
|
|
2932
|
+
}
|
|
2933
|
+
}) : void 0;
|
|
2393
2934
|
return {
|
|
2394
2935
|
/**
|
|
2395
2936
|
* Returns instrumentation instances for your NodeSDK.
|
|
@@ -2477,7 +3018,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2477
3018
|
);
|
|
2478
3019
|
}
|
|
2479
3020
|
const headers = withProjectIdHeaders(processorOptions == null ? void 0 : processorOptions.headers);
|
|
2480
|
-
const productionProcessor =
|
|
3021
|
+
const productionProcessor = traceloop4.createSpanProcessor({
|
|
2481
3022
|
baseUrl: apiUrl,
|
|
2482
3023
|
apiKey: writeKey,
|
|
2483
3024
|
disableBatch: disableBatching,
|
|
@@ -2490,12 +3031,16 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2490
3031
|
allowedInstrumentationLibraries: processorOptions == null ? void 0 : processorOptions.allowedInstrumentationLibraries,
|
|
2491
3032
|
localDebuggerUrlOpt: localDebuggerUrl
|
|
2492
3033
|
});
|
|
3034
|
+
const contextProcessor = new RaindropContextSpanProcessor();
|
|
2493
3035
|
return withV2SpanCompat(
|
|
2494
|
-
|
|
3036
|
+
new CompositeSpanProcessor(
|
|
3037
|
+
[contextProcessor, productionProcessor],
|
|
3038
|
+
localDebuggerProcessor ? [localDebuggerProcessor] : []
|
|
3039
|
+
)
|
|
2495
3040
|
);
|
|
2496
3041
|
},
|
|
2497
3042
|
begin(traceContext) {
|
|
2498
|
-
var
|
|
3043
|
+
var _a2;
|
|
2499
3044
|
if (!traceContext.eventId) {
|
|
2500
3045
|
traceContext.eventId = crypto.randomUUID();
|
|
2501
3046
|
}
|
|
@@ -2504,19 +3049,27 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2504
3049
|
traceContext,
|
|
2505
3050
|
traceId,
|
|
2506
3051
|
analytics,
|
|
2507
|
-
directShipper
|
|
3052
|
+
directShipper,
|
|
3053
|
+
{ projectId, authHint }
|
|
2508
3054
|
);
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
...
|
|
3055
|
+
const bound = bindRoutingContext({ projectId, authHint }, interaction);
|
|
3056
|
+
interaction.setBoundContext(bound);
|
|
3057
|
+
try {
|
|
3058
|
+
analytics._trackAiPartial({
|
|
3059
|
+
...traceContext,
|
|
3060
|
+
properties: {
|
|
3061
|
+
...(_a2 = traceContext.properties) != null ? _a2 : {},
|
|
3062
|
+
...interaction.getTraceId() ? { trace_id: interaction.getTraceId() } : {}
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
if (interaction.getTraceId()) {
|
|
3066
|
+
activeInteractions.set(interaction.getTraceId(), interaction);
|
|
2514
3067
|
}
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
3068
|
+
activeInteractionsByEventId.set(traceContext.eventId, interaction);
|
|
3069
|
+
} catch (error) {
|
|
3070
|
+
unbindRoutingContext(bound);
|
|
3071
|
+
throw error;
|
|
2518
3072
|
}
|
|
2519
|
-
activeInteractionsByEventId.set(traceContext.eventId, interaction);
|
|
2520
3073
|
return interaction;
|
|
2521
3074
|
},
|
|
2522
3075
|
getActiveInteraction(eventId) {
|
|
@@ -2531,12 +3084,15 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2531
3084
|
return interactionByTraceId;
|
|
2532
3085
|
}
|
|
2533
3086
|
}
|
|
2534
|
-
const interaction = new LiveInteraction({ eventId }, traceId, analytics, directShipper
|
|
3087
|
+
const interaction = new LiveInteraction({ eventId }, traceId, analytics, directShipper, {
|
|
3088
|
+
projectId,
|
|
3089
|
+
authHint
|
|
3090
|
+
});
|
|
2535
3091
|
activeInteractionsByEventId.set(eventId, interaction);
|
|
2536
3092
|
return interaction;
|
|
2537
3093
|
},
|
|
2538
3094
|
tracer(globalProperties) {
|
|
2539
|
-
return new LiveTracer(globalProperties, directShipper);
|
|
3095
|
+
return new LiveTracer(globalProperties, directShipper, { projectId, authHint });
|
|
2540
3096
|
},
|
|
2541
3097
|
/**
|
|
2542
3098
|
* Best-effort drain of in-flight OTel spans and direct shipper batches.
|
|
@@ -2551,18 +3107,19 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
2551
3107
|
}
|
|
2552
3108
|
if (traceloopInitialized) {
|
|
2553
3109
|
try {
|
|
2554
|
-
await
|
|
3110
|
+
await traceloop4.forceFlush();
|
|
2555
3111
|
} catch (e) {
|
|
2556
3112
|
}
|
|
2557
3113
|
}
|
|
2558
3114
|
},
|
|
2559
3115
|
async close() {
|
|
3116
|
+
deregisterExitFlusher == null ? void 0 : deregisterExitFlusher();
|
|
2560
3117
|
activeInteractions.clear();
|
|
2561
3118
|
activeInteractionsByEventId.clear();
|
|
2562
3119
|
await (directShipper == null ? void 0 : directShipper.shutdown());
|
|
2563
3120
|
if (traceloopInitialized) {
|
|
2564
3121
|
try {
|
|
2565
|
-
await
|
|
3122
|
+
await traceloop4.forceFlush();
|
|
2566
3123
|
} catch (e) {
|
|
2567
3124
|
}
|
|
2568
3125
|
}
|