@mastra/observability 0.0.0-span-scorring-test-20251124132129 → 0.0.0-top-level-fix-20251211103030
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/CHANGELOG.md +94 -3
- package/dist/config.d.ts +107 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/exporters/index.d.ts +1 -0
- package/dist/exporters/index.d.ts.map +1 -1
- package/dist/exporters/test.d.ts +13 -0
- package/dist/exporters/test.d.ts.map +1 -0
- package/dist/index.cjs +360 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +358 -33
- package/dist/index.js.map +1 -1
- package/dist/instances/base.d.ts +9 -5
- package/dist/instances/base.d.ts.map +1 -1
- package/dist/model-tracing.d.ts +4 -9
- package/dist/model-tracing.d.ts.map +1 -1
- package/dist/span_processors/sensitive-data-filter.d.ts +7 -0
- package/dist/span_processors/sensitive-data-filter.d.ts.map +1 -1
- package/dist/spans/base.d.ts +39 -0
- package/dist/spans/base.d.ts.map +1 -1
- package/dist/spans/default.d.ts.map +1 -1
- package/dist/tracing-options.d.ts +27 -0
- package/dist/tracing-options.d.ts.map +1 -0
- package/dist/usage.d.ts +21 -0
- package/dist/usage.d.ts.map +1 -0
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -37,18 +37,38 @@ var observabilityInstanceConfigSchema = zod.z.object({
|
|
|
37
37
|
serviceName: zod.z.string().min(1, "Service name is required"),
|
|
38
38
|
sampling: samplingStrategySchema.optional(),
|
|
39
39
|
exporters: zod.z.array(zod.z.any()).optional(),
|
|
40
|
+
bridge: zod.z.any().optional(),
|
|
40
41
|
spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
|
|
41
42
|
includeInternalSpans: zod.z.boolean().optional(),
|
|
42
43
|
requestContextKeys: zod.z.array(zod.z.string()).optional()
|
|
43
|
-
})
|
|
44
|
+
}).refine(
|
|
45
|
+
(data) => {
|
|
46
|
+
const hasExporters = data.exporters && data.exporters.length > 0;
|
|
47
|
+
const hasBridge = !!data.bridge;
|
|
48
|
+
return hasExporters || hasBridge;
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
message: "At least one exporter or a bridge is required"
|
|
52
|
+
}
|
|
53
|
+
);
|
|
44
54
|
var observabilityConfigValueSchema = zod.z.object({
|
|
45
55
|
serviceName: zod.z.string().min(1, "Service name is required"),
|
|
46
56
|
sampling: samplingStrategySchema.optional(),
|
|
47
57
|
exporters: zod.z.array(zod.z.any()).optional(),
|
|
58
|
+
bridge: zod.z.any().optional(),
|
|
48
59
|
spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
|
|
49
60
|
includeInternalSpans: zod.z.boolean().optional(),
|
|
50
61
|
requestContextKeys: zod.z.array(zod.z.string()).optional()
|
|
51
|
-
})
|
|
62
|
+
}).refine(
|
|
63
|
+
(data) => {
|
|
64
|
+
const hasExporters = data.exporters && data.exporters.length > 0;
|
|
65
|
+
const hasBridge = !!data.bridge;
|
|
66
|
+
return hasExporters || hasBridge;
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
message: "At least one exporter or a bridge is required"
|
|
70
|
+
}
|
|
71
|
+
);
|
|
52
72
|
var observabilityRegistryConfigSchema = zod.z.object({
|
|
53
73
|
default: zod.z.object({
|
|
54
74
|
enabled: zod.z.boolean().optional()
|
|
@@ -75,6 +95,18 @@ var observabilityRegistryConfigSchema = zod.z.object({
|
|
|
75
95
|
{
|
|
76
96
|
message: 'A "configSelector" function is required when multiple configs are specified to determine which config to use.'
|
|
77
97
|
}
|
|
98
|
+
).refine(
|
|
99
|
+
(data) => {
|
|
100
|
+
if (data.configSelector) {
|
|
101
|
+
const isDefaultEnabled = data.default?.enabled === true;
|
|
102
|
+
const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
|
|
103
|
+
return isDefaultEnabled || hasConfigs;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
message: 'A "configSelector" requires at least one config or default observability to be configured.'
|
|
109
|
+
}
|
|
78
110
|
);
|
|
79
111
|
var BaseExporter = class {
|
|
80
112
|
/** Mastra logger instance */
|
|
@@ -895,6 +927,79 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
895
927
|
this.logger.info("DefaultExporter shutdown complete");
|
|
896
928
|
}
|
|
897
929
|
};
|
|
930
|
+
|
|
931
|
+
// src/exporters/test.ts
|
|
932
|
+
var TestExporter = class extends BaseExporter {
|
|
933
|
+
name = "tracing-test-exporter";
|
|
934
|
+
#events = [];
|
|
935
|
+
constructor(config = {}) {
|
|
936
|
+
super(config);
|
|
937
|
+
}
|
|
938
|
+
async _exportTracingEvent(event) {
|
|
939
|
+
this.#events.push(event);
|
|
940
|
+
}
|
|
941
|
+
clearEvents() {
|
|
942
|
+
this.#events = [];
|
|
943
|
+
}
|
|
944
|
+
get events() {
|
|
945
|
+
return this.#events;
|
|
946
|
+
}
|
|
947
|
+
async shutdown() {
|
|
948
|
+
this.logger.info("TestExporter shutdown");
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
// src/usage.ts
|
|
953
|
+
function extractUsageMetrics(usage, providerMetadata) {
|
|
954
|
+
if (!usage) {
|
|
955
|
+
return {};
|
|
956
|
+
}
|
|
957
|
+
const inputDetails = {};
|
|
958
|
+
const outputDetails = {};
|
|
959
|
+
let inputTokens = usage.inputTokens;
|
|
960
|
+
const outputTokens = usage.outputTokens;
|
|
961
|
+
if (usage.cachedInputTokens) {
|
|
962
|
+
inputDetails.cacheRead = usage.cachedInputTokens;
|
|
963
|
+
}
|
|
964
|
+
if (usage.reasoningTokens) {
|
|
965
|
+
outputDetails.reasoning = usage.reasoningTokens;
|
|
966
|
+
}
|
|
967
|
+
const anthropic = providerMetadata?.anthropic;
|
|
968
|
+
if (anthropic) {
|
|
969
|
+
if (anthropic.cacheReadInputTokens) {
|
|
970
|
+
inputDetails.cacheRead = anthropic.cacheReadInputTokens;
|
|
971
|
+
}
|
|
972
|
+
if (anthropic.cacheCreationInputTokens) {
|
|
973
|
+
inputDetails.cacheWrite = anthropic.cacheCreationInputTokens;
|
|
974
|
+
}
|
|
975
|
+
if (anthropic.cacheReadInputTokens || anthropic.cacheCreationInputTokens) {
|
|
976
|
+
inputDetails.text = usage.inputTokens;
|
|
977
|
+
inputTokens = (usage.inputTokens ?? 0) + (anthropic.cacheReadInputTokens ?? 0) + (anthropic.cacheCreationInputTokens ?? 0);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
const google = providerMetadata?.google;
|
|
981
|
+
if (google?.usageMetadata) {
|
|
982
|
+
if (google.usageMetadata.cachedContentTokenCount) {
|
|
983
|
+
inputDetails.cacheRead = google.usageMetadata.cachedContentTokenCount;
|
|
984
|
+
}
|
|
985
|
+
if (google.usageMetadata.thoughtsTokenCount) {
|
|
986
|
+
outputDetails.reasoning = google.usageMetadata.thoughtsTokenCount;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const result = {
|
|
990
|
+
inputTokens,
|
|
991
|
+
outputTokens
|
|
992
|
+
};
|
|
993
|
+
if (Object.keys(inputDetails).length > 0) {
|
|
994
|
+
result.inputDetails = inputDetails;
|
|
995
|
+
}
|
|
996
|
+
if (Object.keys(outputDetails).length > 0) {
|
|
997
|
+
result.outputDetails = outputDetails;
|
|
998
|
+
}
|
|
999
|
+
return result;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/model-tracing.ts
|
|
898
1003
|
var ModelSpanTracker = class {
|
|
899
1004
|
#modelSpan;
|
|
900
1005
|
#currentStepSpan;
|
|
@@ -902,9 +1007,23 @@ var ModelSpanTracker = class {
|
|
|
902
1007
|
#accumulator = {};
|
|
903
1008
|
#stepIndex = 0;
|
|
904
1009
|
#chunkSequence = 0;
|
|
1010
|
+
#completionStartTime;
|
|
1011
|
+
/** Tracks tool output accumulators by toolCallId for consolidating sub-agent streams */
|
|
1012
|
+
#toolOutputAccumulators = /* @__PURE__ */ new Map();
|
|
1013
|
+
/** Tracks toolCallIds that had streaming output (to skip redundant tool-result spans) */
|
|
1014
|
+
#streamedToolCallIds = /* @__PURE__ */ new Set();
|
|
905
1015
|
constructor(modelSpan) {
|
|
906
1016
|
this.#modelSpan = modelSpan;
|
|
907
1017
|
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Capture the completion start time (time to first token) when the first content chunk arrives.
|
|
1020
|
+
*/
|
|
1021
|
+
#captureCompletionStartTime() {
|
|
1022
|
+
if (this.#completionStartTime) {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
this.#completionStartTime = /* @__PURE__ */ new Date();
|
|
1026
|
+
}
|
|
908
1027
|
/**
|
|
909
1028
|
* Get the tracing context for creating child spans.
|
|
910
1029
|
* Returns the current step span if active, otherwise the model span.
|
|
@@ -921,10 +1040,16 @@ var ModelSpanTracker = class {
|
|
|
921
1040
|
this.#modelSpan?.error(options);
|
|
922
1041
|
}
|
|
923
1042
|
/**
|
|
924
|
-
* End the generation span
|
|
1043
|
+
* End the generation span with optional raw usage data.
|
|
1044
|
+
* If usage is provided, it will be converted to UsageStats with cache token details.
|
|
925
1045
|
*/
|
|
926
1046
|
endGeneration(options) {
|
|
927
|
-
|
|
1047
|
+
const { usage, providerMetadata, ...spanOptions } = options ?? {};
|
|
1048
|
+
if (spanOptions.attributes) {
|
|
1049
|
+
spanOptions.attributes.completionStartTime = this.#completionStartTime;
|
|
1050
|
+
spanOptions.attributes.usage = extractUsageMetrics(usage, providerMetadata);
|
|
1051
|
+
}
|
|
1052
|
+
this.#modelSpan?.end(spanOptions);
|
|
928
1053
|
}
|
|
929
1054
|
/**
|
|
930
1055
|
* Update the generation span
|
|
@@ -954,9 +1079,10 @@ var ModelSpanTracker = class {
|
|
|
954
1079
|
#endStepSpan(payload) {
|
|
955
1080
|
if (!this.#currentStepSpan) return;
|
|
956
1081
|
const output = payload.output;
|
|
957
|
-
const { usage, ...otherOutput } = output;
|
|
1082
|
+
const { usage: rawUsage, ...otherOutput } = output;
|
|
958
1083
|
const stepResult = payload.stepResult;
|
|
959
1084
|
const metadata = payload.metadata;
|
|
1085
|
+
const usage = extractUsageMetrics(rawUsage, metadata?.providerMetadata);
|
|
960
1086
|
const cleanMetadata = metadata ? { ...metadata } : void 0;
|
|
961
1087
|
if (cleanMetadata?.request) {
|
|
962
1088
|
delete cleanMetadata.request;
|
|
@@ -1129,6 +1255,77 @@ var ModelSpanTracker = class {
|
|
|
1129
1255
|
break;
|
|
1130
1256
|
}
|
|
1131
1257
|
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Handle tool-output chunks from sub-agents.
|
|
1260
|
+
* Consolidates streaming text/reasoning deltas into a single span per tool call.
|
|
1261
|
+
*/
|
|
1262
|
+
#handleToolOutputChunk(chunk) {
|
|
1263
|
+
if (chunk.type !== "tool-output") return;
|
|
1264
|
+
const payload = chunk.payload;
|
|
1265
|
+
const { output, toolCallId, toolName } = payload;
|
|
1266
|
+
let acc = this.#toolOutputAccumulators.get(toolCallId);
|
|
1267
|
+
if (!acc) {
|
|
1268
|
+
if (!this.#currentStepSpan) {
|
|
1269
|
+
this.#startStepSpan();
|
|
1270
|
+
}
|
|
1271
|
+
acc = {
|
|
1272
|
+
toolName: toolName || "unknown",
|
|
1273
|
+
toolCallId,
|
|
1274
|
+
text: "",
|
|
1275
|
+
reasoning: "",
|
|
1276
|
+
sequenceNumber: this.#chunkSequence++,
|
|
1277
|
+
// Name the span 'tool-result' for consistency (tool-call → tool-result)
|
|
1278
|
+
span: this.#currentStepSpan?.createChildSpan({
|
|
1279
|
+
name: `chunk: 'tool-result'`,
|
|
1280
|
+
type: observability.SpanType.MODEL_CHUNK,
|
|
1281
|
+
attributes: {
|
|
1282
|
+
chunkType: "tool-result",
|
|
1283
|
+
sequenceNumber: this.#chunkSequence - 1
|
|
1284
|
+
}
|
|
1285
|
+
})
|
|
1286
|
+
};
|
|
1287
|
+
this.#toolOutputAccumulators.set(toolCallId, acc);
|
|
1288
|
+
}
|
|
1289
|
+
if (output && typeof output === "object" && "type" in output) {
|
|
1290
|
+
const innerType = output.type;
|
|
1291
|
+
switch (innerType) {
|
|
1292
|
+
case "text-delta":
|
|
1293
|
+
if (output.payload?.text) {
|
|
1294
|
+
acc.text += output.payload.text;
|
|
1295
|
+
}
|
|
1296
|
+
break;
|
|
1297
|
+
case "reasoning-delta":
|
|
1298
|
+
if (output.payload?.text) {
|
|
1299
|
+
acc.reasoning += output.payload.text;
|
|
1300
|
+
}
|
|
1301
|
+
break;
|
|
1302
|
+
case "finish":
|
|
1303
|
+
case "workflow-finish":
|
|
1304
|
+
this.#endToolOutputSpan(toolCallId);
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* End a tool output span and clean up the accumulator
|
|
1311
|
+
*/
|
|
1312
|
+
#endToolOutputSpan(toolCallId) {
|
|
1313
|
+
const acc = this.#toolOutputAccumulators.get(toolCallId);
|
|
1314
|
+
if (!acc) return;
|
|
1315
|
+
const output = {
|
|
1316
|
+
toolCallId: acc.toolCallId,
|
|
1317
|
+
toolName: acc.toolName
|
|
1318
|
+
};
|
|
1319
|
+
if (acc.text) {
|
|
1320
|
+
output.text = acc.text;
|
|
1321
|
+
}
|
|
1322
|
+
if (acc.reasoning) {
|
|
1323
|
+
output.reasoning = acc.reasoning;
|
|
1324
|
+
}
|
|
1325
|
+
acc.span?.end({ output });
|
|
1326
|
+
this.#toolOutputAccumulators.delete(toolCallId);
|
|
1327
|
+
this.#streamedToolCallIds.add(toolCallId);
|
|
1328
|
+
}
|
|
1132
1329
|
/**
|
|
1133
1330
|
* Wraps a stream with model tracing transform to track MODEL_STEP and MODEL_CHUNK spans.
|
|
1134
1331
|
*
|
|
@@ -1139,6 +1336,13 @@ var ModelSpanTracker = class {
|
|
|
1139
1336
|
return stream.pipeThrough(
|
|
1140
1337
|
new web.TransformStream({
|
|
1141
1338
|
transform: (chunk, controller) => {
|
|
1339
|
+
switch (chunk.type) {
|
|
1340
|
+
case "text-delta":
|
|
1341
|
+
case "tool-call-delta":
|
|
1342
|
+
case "reasoning-delta":
|
|
1343
|
+
this.#captureCompletionStartTime();
|
|
1344
|
+
break;
|
|
1345
|
+
}
|
|
1142
1346
|
controller.enqueue(chunk);
|
|
1143
1347
|
switch (chunk.type) {
|
|
1144
1348
|
case "text-start":
|
|
@@ -1172,6 +1376,19 @@ var ModelSpanTracker = class {
|
|
|
1172
1376
|
case "start":
|
|
1173
1377
|
case "finish":
|
|
1174
1378
|
break;
|
|
1379
|
+
case "tool-output":
|
|
1380
|
+
this.#handleToolOutputChunk(chunk);
|
|
1381
|
+
break;
|
|
1382
|
+
case "tool-result": {
|
|
1383
|
+
const toolCallId = chunk.payload?.toolCallId;
|
|
1384
|
+
if (toolCallId && this.#streamedToolCallIds.has(toolCallId)) {
|
|
1385
|
+
this.#streamedToolCallIds.delete(toolCallId);
|
|
1386
|
+
break;
|
|
1387
|
+
}
|
|
1388
|
+
const { args, ...cleanPayload } = chunk.payload || {};
|
|
1389
|
+
this.#createEventSpan(chunk.type, cleanPayload);
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1175
1392
|
// Default: auto-create event span for all other chunk types
|
|
1176
1393
|
default: {
|
|
1177
1394
|
let outputPayload = chunk.payload;
|
|
@@ -1226,6 +1443,16 @@ function isSpanInternal(spanType, flags) {
|
|
|
1226
1443
|
return false;
|
|
1227
1444
|
}
|
|
1228
1445
|
}
|
|
1446
|
+
function getExternalParentId(options) {
|
|
1447
|
+
if (!options.parent) {
|
|
1448
|
+
return void 0;
|
|
1449
|
+
}
|
|
1450
|
+
if (options.parent.isInternal) {
|
|
1451
|
+
return options.parent.getParentSpanId(false);
|
|
1452
|
+
} else {
|
|
1453
|
+
return options.parent.id;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1229
1456
|
var BaseSpan = class {
|
|
1230
1457
|
name;
|
|
1231
1458
|
type;
|
|
@@ -1240,6 +1467,7 @@ var BaseSpan = class {
|
|
|
1240
1467
|
output;
|
|
1241
1468
|
errorInfo;
|
|
1242
1469
|
metadata;
|
|
1470
|
+
tags;
|
|
1243
1471
|
traceState;
|
|
1244
1472
|
/** Parent span ID (for root spans that are children of external spans) */
|
|
1245
1473
|
parentSpanId;
|
|
@@ -1254,6 +1482,7 @@ var BaseSpan = class {
|
|
|
1254
1482
|
this.isEvent = options.isEvent ?? false;
|
|
1255
1483
|
this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
|
|
1256
1484
|
this.traceState = options.traceState;
|
|
1485
|
+
this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
|
|
1257
1486
|
if (this.isEvent) {
|
|
1258
1487
|
this.output = deepClean(options.output);
|
|
1259
1488
|
} else {
|
|
@@ -1316,12 +1545,36 @@ var BaseSpan = class {
|
|
|
1316
1545
|
errorInfo: this.errorInfo,
|
|
1317
1546
|
isEvent: this.isEvent,
|
|
1318
1547
|
isRootSpan: this.isRootSpan,
|
|
1319
|
-
parentSpanId: this.getParentSpanId(includeInternalSpans)
|
|
1548
|
+
parentSpanId: this.getParentSpanId(includeInternalSpans),
|
|
1549
|
+
// Tags are only included for root spans
|
|
1550
|
+
...this.isRootSpan && this.tags?.length ? { tags: this.tags } : {}
|
|
1320
1551
|
};
|
|
1321
1552
|
}
|
|
1322
1553
|
get externalTraceId() {
|
|
1323
1554
|
return this.isValid ? this.traceId : void 0;
|
|
1324
1555
|
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Execute an async function within this span's tracing context.
|
|
1558
|
+
* Delegates to the bridge if available.
|
|
1559
|
+
*/
|
|
1560
|
+
async executeInContext(fn) {
|
|
1561
|
+
const bridge = this.observabilityInstance.getBridge();
|
|
1562
|
+
if (bridge?.executeInContext) {
|
|
1563
|
+
return bridge.executeInContext(this.id, fn);
|
|
1564
|
+
}
|
|
1565
|
+
return fn();
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Execute a synchronous function within this span's tracing context.
|
|
1569
|
+
* Delegates to the bridge if available.
|
|
1570
|
+
*/
|
|
1571
|
+
executeInContextSync(fn) {
|
|
1572
|
+
const bridge = this.observabilityInstance.getBridge();
|
|
1573
|
+
if (bridge?.executeInContextSync) {
|
|
1574
|
+
return bridge.executeInContextSync(this.id, fn);
|
|
1575
|
+
}
|
|
1576
|
+
return fn();
|
|
1577
|
+
}
|
|
1325
1578
|
};
|
|
1326
1579
|
var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
|
|
1327
1580
|
"logger",
|
|
@@ -1347,6 +1600,9 @@ function deepClean(value, options = {}, _seen = /* @__PURE__ */ new WeakSet(), _
|
|
|
1347
1600
|
return "[Circular]";
|
|
1348
1601
|
}
|
|
1349
1602
|
_seen.add(value);
|
|
1603
|
+
if (value instanceof Date) {
|
|
1604
|
+
return value;
|
|
1605
|
+
}
|
|
1350
1606
|
if (Array.isArray(value)) {
|
|
1351
1607
|
return value.map((item) => deepClean(item, options, _seen, _depth + 1));
|
|
1352
1608
|
}
|
|
@@ -1368,27 +1624,30 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
1368
1624
|
traceId;
|
|
1369
1625
|
constructor(options, observabilityInstance) {
|
|
1370
1626
|
super(options, observabilityInstance);
|
|
1371
|
-
|
|
1627
|
+
const bridge = observabilityInstance.getBridge();
|
|
1628
|
+
if (bridge && !this.isInternal) {
|
|
1629
|
+
const bridgeIds = bridge.createSpan(options);
|
|
1630
|
+
if (bridgeIds) {
|
|
1631
|
+
this.id = bridgeIds.spanId;
|
|
1632
|
+
this.traceId = bridgeIds.traceId;
|
|
1633
|
+
this.parentSpanId = bridgeIds.parentSpanId;
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1372
1637
|
if (options.parent) {
|
|
1373
1638
|
this.traceId = options.parent.traceId;
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
} else {
|
|
1378
|
-
console.error(
|
|
1379
|
-
`[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
|
|
1380
|
-
);
|
|
1381
|
-
this.traceId = generateTraceId();
|
|
1382
|
-
}
|
|
1383
|
-
} else {
|
|
1384
|
-
this.traceId = generateTraceId();
|
|
1639
|
+
this.parentSpanId = options.parent.id;
|
|
1640
|
+
this.id = generateSpanId();
|
|
1641
|
+
return;
|
|
1385
1642
|
}
|
|
1386
|
-
|
|
1643
|
+
this.traceId = getOrCreateTraceId(options);
|
|
1644
|
+
this.id = generateSpanId();
|
|
1645
|
+
if (options.parentSpanId) {
|
|
1387
1646
|
if (isValidSpanId(options.parentSpanId)) {
|
|
1388
1647
|
this.parentSpanId = options.parentSpanId;
|
|
1389
1648
|
} else {
|
|
1390
1649
|
console.error(
|
|
1391
|
-
`[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring
|
|
1650
|
+
`[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring.`
|
|
1392
1651
|
);
|
|
1393
1652
|
}
|
|
1394
1653
|
}
|
|
@@ -1493,6 +1752,18 @@ function isValidTraceId(traceId) {
|
|
|
1493
1752
|
function isValidSpanId(spanId) {
|
|
1494
1753
|
return /^[0-9a-f]{1,16}$/i.test(spanId);
|
|
1495
1754
|
}
|
|
1755
|
+
function getOrCreateTraceId(options) {
|
|
1756
|
+
if (options.traceId) {
|
|
1757
|
+
if (isValidTraceId(options.traceId)) {
|
|
1758
|
+
return options.traceId;
|
|
1759
|
+
} else {
|
|
1760
|
+
console.error(
|
|
1761
|
+
`[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
|
|
1762
|
+
);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return generateTraceId();
|
|
1766
|
+
}
|
|
1496
1767
|
|
|
1497
1768
|
// src/spans/no-op.ts
|
|
1498
1769
|
var NoOpSpan = class extends BaseSpan {
|
|
@@ -1525,13 +1796,17 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1525
1796
|
sampling: config.sampling ?? { type: "always" /* ALWAYS */ },
|
|
1526
1797
|
exporters: config.exporters ?? [],
|
|
1527
1798
|
spanOutputProcessors: config.spanOutputProcessors ?? [],
|
|
1799
|
+
bridge: config.bridge ?? void 0,
|
|
1528
1800
|
includeInternalSpans: config.includeInternalSpans ?? false,
|
|
1529
1801
|
requestContextKeys: config.requestContextKeys ?? []
|
|
1530
1802
|
};
|
|
1803
|
+
if (this.config.bridge?.init) {
|
|
1804
|
+
this.config.bridge.init({ config: this.config });
|
|
1805
|
+
}
|
|
1531
1806
|
}
|
|
1532
1807
|
/**
|
|
1533
1808
|
* Override setLogger to add Observability specific initialization log
|
|
1534
|
-
* and propagate logger to exporters
|
|
1809
|
+
* and propagate logger to exporters and bridge
|
|
1535
1810
|
*/
|
|
1536
1811
|
__setLogger(logger) {
|
|
1537
1812
|
super.__setLogger(logger);
|
|
@@ -1540,8 +1815,11 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1540
1815
|
exporter.__setLogger(logger);
|
|
1541
1816
|
}
|
|
1542
1817
|
});
|
|
1818
|
+
if (this.config.bridge?.__setLogger) {
|
|
1819
|
+
this.config.bridge.__setLogger(logger);
|
|
1820
|
+
}
|
|
1543
1821
|
this.logger.debug(
|
|
1544
|
-
`[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling
|
|
1822
|
+
`[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling?.type}] [bridge=${!!this.config.bridge}]`
|
|
1545
1823
|
);
|
|
1546
1824
|
}
|
|
1547
1825
|
// ============================================================================
|
|
@@ -1570,11 +1848,15 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1570
1848
|
} else {
|
|
1571
1849
|
traceState = this.computeTraceState(tracingOptions);
|
|
1572
1850
|
}
|
|
1573
|
-
const
|
|
1851
|
+
const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
|
|
1852
|
+
const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
|
|
1853
|
+
const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
|
|
1854
|
+
const tags = !options.parent ? tracingOptions?.tags : void 0;
|
|
1574
1855
|
const span = this.createSpan({
|
|
1575
1856
|
...rest,
|
|
1576
1857
|
metadata: enrichedMetadata,
|
|
1577
|
-
traceState
|
|
1858
|
+
traceState,
|
|
1859
|
+
tags
|
|
1578
1860
|
});
|
|
1579
1861
|
if (span.isEvent) {
|
|
1580
1862
|
this.emitSpanEnded(span);
|
|
@@ -1608,6 +1890,12 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1608
1890
|
getSpanOutputProcessors() {
|
|
1609
1891
|
return [...this.spanOutputProcessors];
|
|
1610
1892
|
}
|
|
1893
|
+
/**
|
|
1894
|
+
* Get the bridge instance if configured
|
|
1895
|
+
*/
|
|
1896
|
+
getBridge() {
|
|
1897
|
+
return this.config.bridge;
|
|
1898
|
+
}
|
|
1611
1899
|
/**
|
|
1612
1900
|
* Get the logger instance (for exporters and other components)
|
|
1613
1901
|
*/
|
|
@@ -1652,7 +1940,9 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1652
1940
|
*/
|
|
1653
1941
|
shouldSample(options) {
|
|
1654
1942
|
const { sampling } = this.config;
|
|
1655
|
-
switch (sampling
|
|
1943
|
+
switch (sampling?.type) {
|
|
1944
|
+
case void 0:
|
|
1945
|
+
return true;
|
|
1656
1946
|
case "always" /* ALWAYS */:
|
|
1657
1947
|
return true;
|
|
1658
1948
|
case "never" /* NEVER */:
|
|
@@ -1784,17 +2074,21 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1784
2074
|
}
|
|
1785
2075
|
}
|
|
1786
2076
|
/**
|
|
1787
|
-
* Export tracing event through all exporters (realtime mode)
|
|
2077
|
+
* Export tracing event through all exporters and bridge (realtime mode)
|
|
1788
2078
|
*/
|
|
1789
2079
|
async exportTracingEvent(event) {
|
|
1790
|
-
const
|
|
2080
|
+
const targets = [
|
|
2081
|
+
...this.exporters
|
|
2082
|
+
];
|
|
2083
|
+
if (this.config.bridge) {
|
|
2084
|
+
targets.push(this.config.bridge);
|
|
2085
|
+
}
|
|
2086
|
+
const exportPromises = targets.map(async (target) => {
|
|
1791
2087
|
try {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
this.logger.debug(`[Observability] Event exported [exporter=${exporter.name}] [type=${event.type}]`);
|
|
1795
|
-
}
|
|
2088
|
+
await target.exportTracingEvent(event);
|
|
2089
|
+
this.logger.debug(`[Observability] Event exported [target=${target.name}] [type=${event.type}]`);
|
|
1796
2090
|
} catch (error) {
|
|
1797
|
-
this.logger.error(`[Observability] Export error [
|
|
2091
|
+
this.logger.error(`[Observability] Export error [target=${target.name}]`, error);
|
|
1798
2092
|
}
|
|
1799
2093
|
});
|
|
1800
2094
|
await Promise.allSettled(exportPromises);
|
|
@@ -1818,6 +2112,9 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
1818
2112
|
...this.exporters.map((e) => e.shutdown()),
|
|
1819
2113
|
...this.spanOutputProcessors.map((p) => p.shutdown())
|
|
1820
2114
|
];
|
|
2115
|
+
if (this.config.bridge) {
|
|
2116
|
+
shutdownPromises.push(this.config.bridge.shutdown());
|
|
2117
|
+
}
|
|
1821
2118
|
await Promise.allSettled(shutdownPromises);
|
|
1822
2119
|
this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
|
|
1823
2120
|
}
|
|
@@ -1964,9 +2261,16 @@ var SensitiveDataFilter = class {
|
|
|
1964
2261
|
/**
|
|
1965
2262
|
* Recursively filter objects/arrays for sensitive keys.
|
|
1966
2263
|
* Handles circular references by replacing with a marker.
|
|
2264
|
+
* Also attempts to parse and redact JSON strings.
|
|
1967
2265
|
*/
|
|
1968
2266
|
deepFilter(obj, seen = /* @__PURE__ */ new WeakSet()) {
|
|
1969
2267
|
if (obj === null || typeof obj !== "object") {
|
|
2268
|
+
if (typeof obj === "string") {
|
|
2269
|
+
const trimmed = obj.trim();
|
|
2270
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
2271
|
+
return this.redactJsonString(obj);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
1970
2274
|
return obj;
|
|
1971
2275
|
}
|
|
1972
2276
|
if (seen.has(obj)) {
|
|
@@ -2019,6 +2323,22 @@ var SensitiveDataFilter = class {
|
|
|
2019
2323
|
return normalizedKey === sensitiveField;
|
|
2020
2324
|
});
|
|
2021
2325
|
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Attempt to parse a string as JSON and redact sensitive fields within it.
|
|
2328
|
+
* If parsing fails or no sensitive data is found, returns the original string.
|
|
2329
|
+
*/
|
|
2330
|
+
redactJsonString(str) {
|
|
2331
|
+
try {
|
|
2332
|
+
const parsed = JSON.parse(str);
|
|
2333
|
+
if (parsed && typeof parsed === "object") {
|
|
2334
|
+
const filtered = this.deepFilter(parsed, /* @__PURE__ */ new WeakSet());
|
|
2335
|
+
return JSON.stringify(filtered);
|
|
2336
|
+
}
|
|
2337
|
+
return str;
|
|
2338
|
+
} catch {
|
|
2339
|
+
return str;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2022
2342
|
/**
|
|
2023
2343
|
* Redact a sensitive value.
|
|
2024
2344
|
* - Full style: replaces with a fixed token.
|
|
@@ -2171,6 +2491,11 @@ var Observability = class extends base.MastraBase {
|
|
|
2171
2491
|
}
|
|
2172
2492
|
};
|
|
2173
2493
|
|
|
2494
|
+
// src/tracing-options.ts
|
|
2495
|
+
function buildTracingOptions(...updaters) {
|
|
2496
|
+
return updaters.reduce((opts, updater) => updater(opts), {});
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2174
2499
|
exports.BaseExporter = BaseExporter;
|
|
2175
2500
|
exports.BaseObservabilityInstance = BaseObservabilityInstance;
|
|
2176
2501
|
exports.BaseSpan = BaseSpan;
|
|
@@ -2184,7 +2509,10 @@ exports.NoOpSpan = NoOpSpan;
|
|
|
2184
2509
|
exports.Observability = Observability;
|
|
2185
2510
|
exports.SamplingStrategyType = SamplingStrategyType;
|
|
2186
2511
|
exports.SensitiveDataFilter = SensitiveDataFilter;
|
|
2512
|
+
exports.TestExporter = TestExporter;
|
|
2513
|
+
exports.buildTracingOptions = buildTracingOptions;
|
|
2187
2514
|
exports.deepClean = deepClean;
|
|
2515
|
+
exports.getExternalParentId = getExternalParentId;
|
|
2188
2516
|
exports.observabilityConfigValueSchema = observabilityConfigValueSchema;
|
|
2189
2517
|
exports.observabilityInstanceConfigSchema = observabilityInstanceConfigSchema;
|
|
2190
2518
|
exports.observabilityRegistryConfigSchema = observabilityRegistryConfigSchema;
|