@mastra/observability 1.16.6-alpha.3 → 1.16.6
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 +46 -0
- package/dist/exporters/auth-failure-cooldown.d.ts +1 -1
- package/dist/exporters/auth-failure-cooldown.d.ts.map +1 -1
- package/dist/exporters/mastra-platform.d.ts +21 -0
- package/dist/exporters/mastra-platform.d.ts.map +1 -1
- package/dist/index.cjs +125 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +125 -13
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -5178,15 +5178,15 @@ function isAuthFailureError(error) {
|
|
|
5178
5178
|
function isAuthFailureStatus(status) {
|
|
5179
5179
|
return AUTH_FAILURE_STATUSES.has(status);
|
|
5180
5180
|
}
|
|
5181
|
-
async function fetchWithAuthFailureHandling(url, options, maxRetries) {
|
|
5181
|
+
async function fetchWithAuthFailureHandling(url, options, maxRetries, callerShouldRetry) {
|
|
5182
5182
|
let authFailureStatus;
|
|
5183
5183
|
try {
|
|
5184
|
-
await fetchWithRetry(url, options, maxRetries, { shouldRetryResponse: (response) => {
|
|
5184
|
+
return await fetchWithRetry(url, options, maxRetries, { shouldRetryResponse: (response) => {
|
|
5185
5185
|
if (isAuthFailureStatus(response.status)) {
|
|
5186
5186
|
authFailureStatus = response.status;
|
|
5187
5187
|
return false;
|
|
5188
5188
|
}
|
|
5189
|
-
return true;
|
|
5189
|
+
return callerShouldRetry?.(response) ?? true;
|
|
5190
5190
|
} });
|
|
5191
5191
|
} catch (error) {
|
|
5192
5192
|
if (authFailureStatus !== void 0) throw new AuthFailureError(authFailureStatus, error);
|
|
@@ -6316,6 +6316,25 @@ const SIGNAL_PUBLISH_SUFFIXES = {
|
|
|
6316
6316
|
feedback: "/feedback/publish"
|
|
6317
6317
|
};
|
|
6318
6318
|
const DEFAULT_PLATFORM_SPAN_FILTER = (span) => span.type !== SpanType.MODEL_CHUNK;
|
|
6319
|
+
const QUOTA_EXCEEDED_STATUS = 402;
|
|
6320
|
+
const OBSERVABILITY_STATUS_HEADER = "x-mastra-observability";
|
|
6321
|
+
const OBSERVABILITY_DISABLED_VALUE = "disabled";
|
|
6322
|
+
const OBSERVABILITY_RETRY_AFTER_HEADER = "x-mastra-observability-retry-after";
|
|
6323
|
+
const DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS = 300;
|
|
6324
|
+
const MAX_QUOTA_PROBE_INTERVAL_SECONDS = Math.floor(2147483647 / 1e3);
|
|
6325
|
+
function isObservabilityDisabled(response) {
|
|
6326
|
+
return response.headers.get(OBSERVABILITY_STATUS_HEADER) === OBSERVABILITY_DISABLED_VALUE;
|
|
6327
|
+
}
|
|
6328
|
+
function isQuotaExceededResponse(response) {
|
|
6329
|
+
return response.status === QUOTA_EXCEEDED_STATUS || isObservabilityDisabled(response);
|
|
6330
|
+
}
|
|
6331
|
+
function parseQuotaRetryAfterSeconds(response) {
|
|
6332
|
+
const raw = response.headers.get(OBSERVABILITY_RETRY_AFTER_HEADER);
|
|
6333
|
+
if (!raw || !/^\d+$/.test(raw.trim())) return DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6334
|
+
const parsed = Number.parseInt(raw, 10);
|
|
6335
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6336
|
+
return Math.min(parsed, MAX_QUOTA_PROBE_INTERVAL_SECONDS);
|
|
6337
|
+
}
|
|
6319
6338
|
const SIGNAL_PUBLISH_SEGMENTS = {
|
|
6320
6339
|
traces: "spans",
|
|
6321
6340
|
logs: "logs",
|
|
@@ -6405,6 +6424,10 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6405
6424
|
buffer;
|
|
6406
6425
|
flushTimer = null;
|
|
6407
6426
|
inFlightFlushes = /* @__PURE__ */ new Set();
|
|
6427
|
+
quotaPaused = false;
|
|
6428
|
+
quotaProbeTimer = null;
|
|
6429
|
+
quotaProbeIntervalSeconds = DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6430
|
+
shuttingDown = false;
|
|
6408
6431
|
constructor(config = {}) {
|
|
6409
6432
|
super(config);
|
|
6410
6433
|
if (config.projectId !== void 0 && !VALID_PROJECT_ID.test(config.projectId)) throw createInvalidProjectIdError(config.projectId);
|
|
@@ -6453,31 +6476,31 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6453
6476
|
async _exportTracingEvent(event) {
|
|
6454
6477
|
if (event.type !== TracingEventType.SPAN_ENDED) return;
|
|
6455
6478
|
if (!DEFAULT_PLATFORM_SPAN_FILTER(event.exportedSpan)) return;
|
|
6456
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6479
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6457
6480
|
this.addToBuffer(event);
|
|
6458
6481
|
await this.handleBufferedEvent();
|
|
6459
6482
|
}
|
|
6460
6483
|
async onLogEvent(event) {
|
|
6461
6484
|
if (this.isDisabled) return;
|
|
6462
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6485
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6463
6486
|
this.addLogToBuffer(event);
|
|
6464
6487
|
await this.handleBufferedEvent();
|
|
6465
6488
|
}
|
|
6466
6489
|
async onMetricEvent(event) {
|
|
6467
6490
|
if (this.isDisabled) return;
|
|
6468
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6491
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6469
6492
|
this.addMetricToBuffer(event);
|
|
6470
6493
|
await this.handleBufferedEvent();
|
|
6471
6494
|
}
|
|
6472
6495
|
async onScoreEvent(event) {
|
|
6473
6496
|
if (this.isDisabled) return;
|
|
6474
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6497
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6475
6498
|
this.addScoreToBuffer(event);
|
|
6476
6499
|
await this.handleBufferedEvent();
|
|
6477
6500
|
}
|
|
6478
6501
|
async onFeedbackEvent(event) {
|
|
6479
6502
|
if (this.isDisabled) return;
|
|
6480
|
-
if (this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6503
|
+
if (this.quotaPaused || this.authFailureCooldown.dropEventIfCoolingDown()) return;
|
|
6481
6504
|
this.addFeedbackToBuffer(event);
|
|
6482
6505
|
await this.handleBufferedEvent();
|
|
6483
6506
|
}
|
|
@@ -6567,6 +6590,10 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6567
6590
|
this.flushTimer = null;
|
|
6568
6591
|
}
|
|
6569
6592
|
if (this.buffer.totalSize === 0) return;
|
|
6593
|
+
if (this.quotaPaused) {
|
|
6594
|
+
this.resetBuffer();
|
|
6595
|
+
return;
|
|
6596
|
+
}
|
|
6570
6597
|
if (this.authFailureCooldown.dropEventsIfCoolingDown(this.buffer.totalSize)) {
|
|
6571
6598
|
this.resetBuffer();
|
|
6572
6599
|
return;
|
|
@@ -6616,11 +6643,16 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6616
6643
|
/**
|
|
6617
6644
|
* Uploads a signal batch to the configured Mastra Observability API using fetchWithRetry.
|
|
6618
6645
|
*/
|
|
6619
|
-
|
|
6620
|
-
|
|
6646
|
+
buildPublishHeaders() {
|
|
6647
|
+
return {
|
|
6621
6648
|
Authorization: `Bearer ${this.platformConfig.accessToken}`,
|
|
6622
6649
|
"Content-Type": "application/json"
|
|
6623
6650
|
};
|
|
6651
|
+
}
|
|
6652
|
+
buildPublishBody(signal, records) {
|
|
6653
|
+
return JSON.stringify({ [SIGNAL_PUBLISH_SEGMENTS[signal]]: records });
|
|
6654
|
+
}
|
|
6655
|
+
async batchUpload(signal, records) {
|
|
6624
6656
|
const endpointMap = {
|
|
6625
6657
|
traces: this.platformConfig.tracesEndpoint,
|
|
6626
6658
|
logs: this.platformConfig.logsEndpoint,
|
|
@@ -6630,10 +6662,85 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6630
6662
|
};
|
|
6631
6663
|
const options = {
|
|
6632
6664
|
method: "POST",
|
|
6633
|
-
headers,
|
|
6634
|
-
body:
|
|
6665
|
+
headers: this.buildPublishHeaders(),
|
|
6666
|
+
body: this.buildPublishBody(signal, records)
|
|
6635
6667
|
};
|
|
6636
|
-
|
|
6668
|
+
let quotaResponse;
|
|
6669
|
+
let response;
|
|
6670
|
+
try {
|
|
6671
|
+
response = await fetchWithAuthFailureHandling(endpointMap[signal], options, this.platformConfig.maxRetries, (res) => {
|
|
6672
|
+
if (isQuotaExceededResponse(res)) {
|
|
6673
|
+
quotaResponse = res;
|
|
6674
|
+
return false;
|
|
6675
|
+
}
|
|
6676
|
+
return true;
|
|
6677
|
+
});
|
|
6678
|
+
} catch (error) {
|
|
6679
|
+
if (quotaResponse) {
|
|
6680
|
+
this.enterQuotaPause(parseQuotaRetryAfterSeconds(quotaResponse));
|
|
6681
|
+
return;
|
|
6682
|
+
}
|
|
6683
|
+
throw error;
|
|
6684
|
+
}
|
|
6685
|
+
if (isObservabilityDisabled(response)) this.enterQuotaPause(parseQuotaRetryAfterSeconds(response));
|
|
6686
|
+
}
|
|
6687
|
+
/**
|
|
6688
|
+
* Enter the quota-exhausted paused state: drop buffered events, stop the
|
|
6689
|
+
* flush timer, and start probing until the collector stops rejecting with
|
|
6690
|
+
* 402. The gate is per-org, so a signal on any publish route pauses all
|
|
6691
|
+
* five signal types.
|
|
6692
|
+
*/
|
|
6693
|
+
enterQuotaPause(retryAfterSeconds) {
|
|
6694
|
+
this.quotaProbeIntervalSeconds = retryAfterSeconds;
|
|
6695
|
+
if (this.quotaPaused) return;
|
|
6696
|
+
this.quotaPaused = true;
|
|
6697
|
+
if (this.flushTimer) {
|
|
6698
|
+
clearTimeout(this.flushTimer);
|
|
6699
|
+
this.flushTimer = null;
|
|
6700
|
+
}
|
|
6701
|
+
this.resetBuffer();
|
|
6702
|
+
this.logger.warn(`Mastra observability paused: quota exhausted, dropping telemetry and probing every ${retryAfterSeconds}s`);
|
|
6703
|
+
this.scheduleQuotaProbe();
|
|
6704
|
+
}
|
|
6705
|
+
scheduleQuotaProbe() {
|
|
6706
|
+
if (this.shuttingDown) return;
|
|
6707
|
+
if (this.quotaProbeTimer) clearTimeout(this.quotaProbeTimer);
|
|
6708
|
+
this.quotaProbeTimer = setTimeout(() => {
|
|
6709
|
+
this.quotaProbeTimer = null;
|
|
6710
|
+
this.probeQuotaStatus();
|
|
6711
|
+
}, this.quotaProbeIntervalSeconds * 1e3);
|
|
6712
|
+
this.quotaProbeTimer.unref?.();
|
|
6713
|
+
}
|
|
6714
|
+
/**
|
|
6715
|
+
* Send an empty spans batch as a free probe. The collector treats an empty
|
|
6716
|
+
* batch as a no-op, and an exhausted org still gets the 402 on it. A 402
|
|
6717
|
+
* throws out of fetchWithRetry without the Response, so the retry predicate
|
|
6718
|
+
* captures it to keep the retry-after hint.
|
|
6719
|
+
*/
|
|
6720
|
+
async probeQuotaStatus() {
|
|
6721
|
+
if (this.shuttingDown || !this.quotaPaused) return;
|
|
6722
|
+
let quotaResponse;
|
|
6723
|
+
try {
|
|
6724
|
+
const response = await fetchWithRetry(this.platformConfig.tracesEndpoint, {
|
|
6725
|
+
method: "POST",
|
|
6726
|
+
headers: this.buildPublishHeaders(),
|
|
6727
|
+
body: this.buildPublishBody("traces", [])
|
|
6728
|
+
}, 1, { shouldRetryResponse: (res) => {
|
|
6729
|
+
if (isQuotaExceededResponse(res)) quotaResponse = res;
|
|
6730
|
+
return false;
|
|
6731
|
+
} });
|
|
6732
|
+
if (this.shuttingDown) return;
|
|
6733
|
+
if (!isObservabilityDisabled(response)) {
|
|
6734
|
+
this.quotaPaused = false;
|
|
6735
|
+
this.quotaProbeIntervalSeconds = DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS;
|
|
6736
|
+
this.logger.warn("Mastra observability resumed: quota restored, exports re-enabled");
|
|
6737
|
+
return;
|
|
6738
|
+
}
|
|
6739
|
+
this.quotaProbeIntervalSeconds = parseQuotaRetryAfterSeconds(response);
|
|
6740
|
+
} catch {
|
|
6741
|
+
if (quotaResponse) this.quotaProbeIntervalSeconds = parseQuotaRetryAfterSeconds(quotaResponse);
|
|
6742
|
+
}
|
|
6743
|
+
this.scheduleQuotaProbe();
|
|
6637
6744
|
}
|
|
6638
6745
|
async flushSignalBatch(signal, records) {
|
|
6639
6746
|
if (records.length === 0) return {
|
|
@@ -6701,11 +6808,16 @@ var MastraPlatformExporter = class extends BaseExporter {
|
|
|
6701
6808
|
}
|
|
6702
6809
|
}
|
|
6703
6810
|
async shutdown() {
|
|
6811
|
+
this.shuttingDown = true;
|
|
6704
6812
|
if (this.isDisabled) return;
|
|
6705
6813
|
if (this.flushTimer) {
|
|
6706
6814
|
clearTimeout(this.flushTimer);
|
|
6707
6815
|
this.flushTimer = null;
|
|
6708
6816
|
}
|
|
6817
|
+
if (this.quotaProbeTimer) {
|
|
6818
|
+
clearTimeout(this.quotaProbeTimer);
|
|
6819
|
+
this.quotaProbeTimer = null;
|
|
6820
|
+
}
|
|
6709
6821
|
try {
|
|
6710
6822
|
await this.flush();
|
|
6711
6823
|
} catch (error) {
|