@mastra/observability 1.18.4 → 1.19.0-alpha.1
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/context/metrics.d.ts.map +1 -1
- package/dist/exporters/tracking.d.ts +7 -0
- package/dist/exporters/tracking.d.ts.map +1 -1
- package/dist/index.cjs +90 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +90 -24
- package/dist/index.js.map +1 -1
- package/dist/instances/base.d.ts.map +1 -1
- package/dist/metrics/auto-extract.d.ts.map +1 -1
- package/dist/spans/base.d.ts +1 -1
- package/dist/spans/base.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1041,6 +1041,7 @@ var MetricsContextImpl = class {
|
|
|
1041
1041
|
labels: filteredLabels,
|
|
1042
1042
|
correlationContext: this.correlationContext,
|
|
1043
1043
|
costContext,
|
|
1044
|
+
...options?.usageId ? { usageId: options.usageId } : {},
|
|
1044
1045
|
metadata: this.metadata
|
|
1045
1046
|
}
|
|
1046
1047
|
};
|
|
@@ -1742,13 +1743,13 @@ function emitUsageMetrics(attrs, usage, metrics) {
|
|
|
1742
1743
|
metricCosts = /* @__PURE__ */ new Map();
|
|
1743
1744
|
}
|
|
1744
1745
|
const labels = attrs.usageIncomplete ? { usageIncomplete: "true" } : void 0;
|
|
1746
|
+
const usageId = generateSignalId();
|
|
1745
1747
|
const emit = (name, value) => {
|
|
1746
1748
|
const costContext = metricCosts.get(name);
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
}
|
|
1751
|
-
metrics.emit(name, value, labels, { costContext });
|
|
1749
|
+
metrics.emit(name, value, labels, costContext ? {
|
|
1750
|
+
costContext,
|
|
1751
|
+
usageId
|
|
1752
|
+
} : { usageId });
|
|
1752
1753
|
};
|
|
1753
1754
|
for (const sample of getTokenMetricSamples(usage)) emit(sample.name, sample.value);
|
|
1754
1755
|
}
|
|
@@ -3179,6 +3180,7 @@ var BaseSpan = class BaseSpan {
|
|
|
3179
3180
|
exportSpan(includeInternalSpans) {
|
|
3180
3181
|
const hideInput = this.traceState?.hideInput ?? false;
|
|
3181
3182
|
const hideOutput = this.traceState?.hideOutput ?? false;
|
|
3183
|
+
const nestedUnderParent = this.traceState?.nestedUnderParent ?? false;
|
|
3182
3184
|
return {
|
|
3183
3185
|
id: this.id,
|
|
3184
3186
|
traceId: this.traceId,
|
|
@@ -3200,7 +3202,8 @@ var BaseSpan = class BaseSpan {
|
|
|
3200
3202
|
...this.isInternal ? { isInternal: true } : {},
|
|
3201
3203
|
parentSpanId: this.getParentSpanId(includeInternalSpans),
|
|
3202
3204
|
externalParentSpanId: this.getExportedExternalParentSpanId(includeInternalSpans),
|
|
3203
|
-
...
|
|
3205
|
+
...nestedUnderParent ? { nestedUnderParent: true } : {},
|
|
3206
|
+
...this.isRootSpan && !nestedUnderParent && this.tags?.length ? { tags: this.tags } : {}
|
|
3204
3207
|
};
|
|
3205
3208
|
}
|
|
3206
3209
|
get externalTraceId() {
|
|
@@ -3213,7 +3216,7 @@ var BaseSpan = class BaseSpan {
|
|
|
3213
3216
|
async executeInContext(fn) {
|
|
3214
3217
|
const bridge = this.observabilityInstance.getBridge();
|
|
3215
3218
|
if (bridge?.executeInContext) {
|
|
3216
|
-
const bridgeContextSpan = this.isInternal ? this.getParentSpan(false) : this;
|
|
3219
|
+
const bridgeContextSpan = this.isInternal || this.isExcluded ? this.getParentSpan(false) : this;
|
|
3217
3220
|
return bridge.executeInContext(bridgeContextSpan?.id ?? this.id, fn);
|
|
3218
3221
|
}
|
|
3219
3222
|
return fn();
|
|
@@ -3225,7 +3228,7 @@ var BaseSpan = class BaseSpan {
|
|
|
3225
3228
|
executeInContextSync(fn) {
|
|
3226
3229
|
const bridge = this.observabilityInstance.getBridge();
|
|
3227
3230
|
if (bridge?.executeInContextSync) {
|
|
3228
|
-
const bridgeContextSpan = this.isInternal ? this.getParentSpan(false) : this;
|
|
3231
|
+
const bridgeContextSpan = this.isInternal || this.isExcluded ? this.getParentSpan(false) : this;
|
|
3229
3232
|
return bridge.executeInContextSync(bridgeContextSpan?.id ?? this.id, fn);
|
|
3230
3233
|
}
|
|
3231
3234
|
return fn();
|
|
@@ -3270,7 +3273,7 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
3270
3273
|
return;
|
|
3271
3274
|
}
|
|
3272
3275
|
const bridge = observabilityInstance.getBridge();
|
|
3273
|
-
if (bridge && !this.isInternal) {
|
|
3276
|
+
if (bridge && !this.isInternal && !this.isExcluded) {
|
|
3274
3277
|
const bridgeIds = bridge.createSpan(options);
|
|
3275
3278
|
if (bridgeIds) {
|
|
3276
3279
|
this.id = bridgeIds.spanId;
|
|
@@ -4747,6 +4750,11 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
4747
4750
|
tags,
|
|
4748
4751
|
requestContext
|
|
4749
4752
|
});
|
|
4753
|
+
if (!options.parent && traceState?.nestedUnderParent && (!tracingOptions?.traceId || span.traceId !== tracingOptions.traceId || !tracingOptions.parentSpanId || !(span instanceof BaseSpan) || span.externalParentSpanId !== tracingOptions.parentSpanId)) {
|
|
4754
|
+
const { nestedUnderParent: _ignored, ...ownTraceState } = traceState;
|
|
4755
|
+
span.traceState = ownTraceState;
|
|
4756
|
+
this.logger.debug("[Observability] Ignoring tracingOptions.nestUnderParent: the run did not join the requested trace under the requested parent");
|
|
4757
|
+
}
|
|
4750
4758
|
if (rest.type === SpanType.MODEL_GENERATION && this.config.excludeSpanTypes?.includes(SpanType.MODEL_GENERATION)) {
|
|
4751
4759
|
const attrs = rest.attributes;
|
|
4752
4760
|
const model = resolveModelId(attrs?.responseModel, attrs?.model);
|
|
@@ -4782,6 +4790,7 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
4782
4790
|
traceId: cached.traceId,
|
|
4783
4791
|
spanId: cached.id,
|
|
4784
4792
|
parentSpanId: cached.parentSpanId,
|
|
4793
|
+
externalParentSpanId: cached.externalParentSpanId,
|
|
4785
4794
|
startTime: cached.startTime instanceof Date ? cached.startTime : new Date(cached.startTime),
|
|
4786
4795
|
input: cached.input,
|
|
4787
4796
|
attributes: cached.attributes,
|
|
@@ -4789,7 +4798,11 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
4789
4798
|
entityType: cached.entityType,
|
|
4790
4799
|
entityId: cached.entityId,
|
|
4791
4800
|
entityName: cached.entityName,
|
|
4792
|
-
tracingPolicy: cached.isInternal ? { internal: InternalSpans.ALL } : void 0
|
|
4801
|
+
tracingPolicy: cached.isInternal ? { internal: InternalSpans.ALL } : void 0,
|
|
4802
|
+
traceState: cached.nestedUnderParent ? {
|
|
4803
|
+
requestContextKeys: [],
|
|
4804
|
+
nestedUnderParent: true
|
|
4805
|
+
} : void 0
|
|
4793
4806
|
});
|
|
4794
4807
|
this.wireSpanLifecycle(span);
|
|
4795
4808
|
return span;
|
|
@@ -4993,11 +5006,13 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
4993
5006
|
const allKeys = [...configuredKeys, ...additionalKeys];
|
|
4994
5007
|
const hideInput = tracingOptions?.hideInput;
|
|
4995
5008
|
const hideOutput = tracingOptions?.hideOutput;
|
|
4996
|
-
|
|
5009
|
+
const nestedUnderParent = tracingOptions?.nestUnderParent === true;
|
|
5010
|
+
if (allKeys.length === 0 && !hideInput && !hideOutput && !nestedUnderParent) return;
|
|
4997
5011
|
return {
|
|
4998
5012
|
requestContextKeys: allKeys,
|
|
4999
5013
|
...hideInput !== void 0 && { hideInput },
|
|
5000
|
-
...hideOutput !== void 0 && { hideOutput }
|
|
5014
|
+
...hideOutput !== void 0 && { hideOutput },
|
|
5015
|
+
...nestedUnderParent && { nestedUnderParent }
|
|
5001
5016
|
};
|
|
5002
5017
|
}
|
|
5003
5018
|
/**
|
|
@@ -5869,6 +5884,12 @@ var TraceData = class {
|
|
|
5869
5884
|
this.#waitingForParent.delete(args.spanId);
|
|
5870
5885
|
}
|
|
5871
5886
|
/**
|
|
5887
|
+
* Get the span IDs that have events waiting on them.
|
|
5888
|
+
*/
|
|
5889
|
+
getWaitingForSpanIds() {
|
|
5890
|
+
return [...this.#waitingForParent.keys()];
|
|
5891
|
+
}
|
|
5892
|
+
/**
|
|
5872
5893
|
* Get total count of events in all waiting queues.
|
|
5873
5894
|
*/
|
|
5874
5895
|
waitingQueueSize() {
|
|
@@ -6052,6 +6073,8 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6052
6073
|
#hardCapEnforcementInProgress = false;
|
|
6053
6074
|
/** Map of traceId to scheduled cleanup timeout */
|
|
6054
6075
|
#pendingCleanups = /* @__PURE__ */ new Map();
|
|
6076
|
+
/** Waiting-queue replays started by setImmediate that haven't finished yet */
|
|
6077
|
+
#activeReplays = /* @__PURE__ */ new Set();
|
|
6055
6078
|
/** Subclass configuration with resolved values */
|
|
6056
6079
|
config;
|
|
6057
6080
|
/** Maximum attempts to process a queued event before dropping */
|
|
@@ -6079,12 +6102,13 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6079
6102
|
*/
|
|
6080
6103
|
#scheduleProcessWaitingForRoot(traceId) {
|
|
6081
6104
|
setImmediate(() => {
|
|
6082
|
-
this.#
|
|
6105
|
+
if (this.#shutdownStarted) return;
|
|
6106
|
+
this.#trackReplay(this.#processWaitingForRoot(traceId).then(() => {}, (error) => {
|
|
6083
6107
|
this.logger.error(`${this.name}: Error processing waiting-for-root queue`, {
|
|
6084
6108
|
error,
|
|
6085
6109
|
traceId
|
|
6086
6110
|
});
|
|
6087
|
-
});
|
|
6111
|
+
}));
|
|
6088
6112
|
});
|
|
6089
6113
|
}
|
|
6090
6114
|
/**
|
|
@@ -6093,24 +6117,59 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6093
6117
|
*/
|
|
6094
6118
|
#scheduleProcessWaitingFor(traceId, spanId) {
|
|
6095
6119
|
setImmediate(() => {
|
|
6096
|
-
this.#
|
|
6120
|
+
if (this.#shutdownStarted) return;
|
|
6121
|
+
this.#trackReplay(this.#processWaitingFor(traceId, spanId).then(() => {}, (error) => {
|
|
6097
6122
|
this.logger.error(`${this.name}: Error processing waiting queue`, {
|
|
6098
6123
|
error,
|
|
6099
6124
|
traceId,
|
|
6100
6125
|
spanId
|
|
6101
6126
|
});
|
|
6102
|
-
});
|
|
6127
|
+
}));
|
|
6103
6128
|
});
|
|
6104
6129
|
}
|
|
6105
6130
|
/**
|
|
6131
|
+
* Track a running replay so flush() and shutdown() can wait for it.
|
|
6132
|
+
*/
|
|
6133
|
+
#trackReplay(replay) {
|
|
6134
|
+
this.#activeReplays.add(replay);
|
|
6135
|
+
replay.finally(() => this.#activeReplays.delete(replay));
|
|
6136
|
+
}
|
|
6137
|
+
/**
|
|
6138
|
+
* Process every queued event whose dependency has been built, without waiting for the
|
|
6139
|
+
* setImmediate-scheduled replays (which never fire under fake timers). Waits for replays
|
|
6140
|
+
* that are already running, then repeats until a pass makes no progress, so cascades
|
|
6141
|
+
* (root -> parent -> child -> end) are fully drained.
|
|
6142
|
+
*/
|
|
6143
|
+
async #drainWaitingQueues() {
|
|
6144
|
+
while (true) {
|
|
6145
|
+
while (this.#activeReplays.size > 0) await Promise.all(this.#activeReplays);
|
|
6146
|
+
let progressed = false;
|
|
6147
|
+
for (const [traceId, traceData] of this.#traceMap) try {
|
|
6148
|
+
if (traceData.isRootProcessed() && await this.#processWaitingForRoot(traceId) > 0) progressed = true;
|
|
6149
|
+
for (const spanId of traceData.getWaitingForSpanIds()) {
|
|
6150
|
+
if (!traceData.hasSpan({ spanId })) continue;
|
|
6151
|
+
if (await this.#processWaitingFor(traceId, spanId) > 0) progressed = true;
|
|
6152
|
+
}
|
|
6153
|
+
} catch (error) {
|
|
6154
|
+
this.logger.error(`${this.name}: Error draining waiting queues`, {
|
|
6155
|
+
error,
|
|
6156
|
+
traceId
|
|
6157
|
+
});
|
|
6158
|
+
}
|
|
6159
|
+
if (!progressed && this.#activeReplays.size === 0) return;
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
/**
|
|
6106
6163
|
* Process all events waiting for root span.
|
|
6164
|
+
* Takes the queue before processing, so a concurrent call can't process the same events.
|
|
6165
|
+
* @returns The number of events that left the queue (processed, moved or dropped)
|
|
6107
6166
|
*/
|
|
6108
6167
|
async #processWaitingForRoot(traceId) {
|
|
6109
|
-
if (this.#shutdownStarted) return;
|
|
6110
6168
|
const traceData = this.#traceMap.get(traceId);
|
|
6111
|
-
if (!traceData) return;
|
|
6169
|
+
if (!traceData) return 0;
|
|
6112
6170
|
const queue = traceData.getEventsWaitingForRoot();
|
|
6113
|
-
if (queue.length === 0) return;
|
|
6171
|
+
if (queue.length === 0) return 0;
|
|
6172
|
+
traceData.clearWaitingForRoot();
|
|
6114
6173
|
this.logger.debug(`${this.name}: Processing ${queue.length} events waiting for root`, { traceId });
|
|
6115
6174
|
const toKeep = [];
|
|
6116
6175
|
const now = Date.now();
|
|
@@ -6146,23 +6205,25 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6146
6205
|
else toKeep.push(queuedEvent);
|
|
6147
6206
|
}
|
|
6148
6207
|
}
|
|
6149
|
-
traceData.clearWaitingForRoot();
|
|
6150
6208
|
for (const event of toKeep) traceData.addToWaitingQueue({
|
|
6151
6209
|
event: event.event,
|
|
6152
6210
|
waitingFor: "root",
|
|
6153
6211
|
attempts: event.attempts,
|
|
6154
6212
|
queuedAt: event.queuedAt
|
|
6155
6213
|
});
|
|
6214
|
+
return queue.length - toKeep.length;
|
|
6156
6215
|
}
|
|
6157
6216
|
/**
|
|
6158
6217
|
* Process events waiting for a specific parent span.
|
|
6218
|
+
* Takes the queue before processing, so a concurrent call can't process the same events.
|
|
6219
|
+
* @returns The number of events that left the queue (processed, moved or dropped)
|
|
6159
6220
|
*/
|
|
6160
6221
|
async #processWaitingFor(traceId, spanId) {
|
|
6161
|
-
if (this.#shutdownStarted) return;
|
|
6162
6222
|
const traceData = this.#traceMap.get(traceId);
|
|
6163
|
-
if (!traceData) return;
|
|
6223
|
+
if (!traceData) return 0;
|
|
6164
6224
|
const queue = traceData.getEventsWaitingFor({ spanId });
|
|
6165
|
-
if (queue.length === 0) return;
|
|
6225
|
+
if (queue.length === 0) return 0;
|
|
6226
|
+
traceData.clearWaitingFor({ spanId });
|
|
6166
6227
|
this.logger.debug(`${this.name}: Processing ${queue.length} events waiting for span`, {
|
|
6167
6228
|
traceId,
|
|
6168
6229
|
spanId
|
|
@@ -6192,13 +6253,13 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6192
6253
|
queuedEvent.attempts++;
|
|
6193
6254
|
if (!await this.#tryProcessQueuedEvent(queuedEvent, traceData)) toKeep.push(queuedEvent);
|
|
6194
6255
|
}
|
|
6195
|
-
traceData.clearWaitingFor({ spanId });
|
|
6196
6256
|
for (const event of toKeep) traceData.addToWaitingQueue({
|
|
6197
6257
|
event: event.event,
|
|
6198
6258
|
waitingFor: spanId,
|
|
6199
6259
|
attempts: event.attempts,
|
|
6200
6260
|
queuedAt: event.queuedAt
|
|
6201
6261
|
});
|
|
6262
|
+
return queue.length - toKeep.length;
|
|
6202
6263
|
}
|
|
6203
6264
|
/**
|
|
6204
6265
|
* Try to process a queued event.
|
|
@@ -6658,11 +6719,15 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6658
6719
|
* This is useful in serverless environments where you need to ensure spans
|
|
6659
6720
|
* are exported before the runtime instance is terminated.
|
|
6660
6721
|
*
|
|
6722
|
+
* Queued events whose dependencies have been built are processed first, so
|
|
6723
|
+
* a shutdown() right after flush() only aborts spans that are still open.
|
|
6724
|
+
*
|
|
6661
6725
|
* Subclasses should override _flush() to implement vendor-specific flush logic.
|
|
6662
6726
|
*/
|
|
6663
6727
|
async flush() {
|
|
6664
6728
|
if (this.isDisabled) return;
|
|
6665
6729
|
this.logger.debug(`${this.name}: Flushing`);
|
|
6730
|
+
if (!this.#shutdownStarted) await this.#drainWaitingQueues();
|
|
6666
6731
|
await this._flush();
|
|
6667
6732
|
}
|
|
6668
6733
|
/**
|
|
@@ -6682,6 +6747,7 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
6682
6747
|
async shutdown() {
|
|
6683
6748
|
if (this.isDisabled) return;
|
|
6684
6749
|
this.#shutdownStarted = true;
|
|
6750
|
+
await this.#drainWaitingQueues();
|
|
6685
6751
|
await this._preShutdown();
|
|
6686
6752
|
for (const [traceId, timeout] of this.#pendingCleanups) {
|
|
6687
6753
|
clearTimeout(timeout);
|