@mastra/observability 1.12.0-alpha.2 → 1.12.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -16217,6 +16217,852 @@ var DefaultExporter = class extends BaseExporter {
16217
16217
  this.logger.info("DefaultExporter shutdown complete");
16218
16218
  }
16219
16219
  };
16220
+ var SIGNAL_PUBLISH_SUFFIXES2 = {
16221
+ traces: "/spans/publish",
16222
+ logs: "/logs/publish",
16223
+ metrics: "/metrics/publish",
16224
+ scores: "/scores/publish",
16225
+ feedback: "/feedback/publish"
16226
+ };
16227
+ var DEFAULT_PLATFORM_SPAN_FILTER = (span) => span.type !== observability.SpanType.MODEL_CHUNK;
16228
+ var SIGNAL_PUBLISH_SEGMENTS2 = {
16229
+ traces: "spans",
16230
+ logs: "logs",
16231
+ metrics: "metrics",
16232
+ scores: "scores",
16233
+ feedback: "feedback"
16234
+ };
16235
+ function trimTrailingSlashes2(value) {
16236
+ let end = value.length;
16237
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
16238
+ end--;
16239
+ }
16240
+ return end === value.length ? value : value.slice(0, end);
16241
+ }
16242
+ function createInvalidEndpointError2(endpoint, text, cause) {
16243
+ return new error$1.MastraError(
16244
+ {
16245
+ id: `MASTRA_PLATFORM_EXPORTER_INVALID_ENDPOINT`,
16246
+ text,
16247
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
16248
+ category: error$1.ErrorCategory.USER,
16249
+ details: {
16250
+ endpoint
16251
+ }
16252
+ },
16253
+ cause
16254
+ );
16255
+ }
16256
+ var VALID_PROJECT_ID2 = /^[a-zA-Z0-9_-]+$/;
16257
+ function createInvalidProjectIdError2(projectId) {
16258
+ return new error$1.MastraError({
16259
+ id: `MASTRA_PLATFORM_EXPORTER_INVALID_PROJECT_ID`,
16260
+ text: "MastraPlatformExporter projectId must only contain letters, numbers, hyphens, and underscores.",
16261
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
16262
+ category: error$1.ErrorCategory.USER,
16263
+ details: {
16264
+ projectId
16265
+ }
16266
+ });
16267
+ }
16268
+ function resolveBaseEndpoint2(baseEndpoint) {
16269
+ const normalizedEndpoint = trimTrailingSlashes2(baseEndpoint);
16270
+ const invalidText = 'MastraPlatformExporter endpoint must be a base origin like "https://collector.example.com" with no path, search, or hash.';
16271
+ try {
16272
+ const parsedEndpoint = new URL(normalizedEndpoint);
16273
+ if (parsedEndpoint.pathname !== "/" || parsedEndpoint.search || parsedEndpoint.hash) {
16274
+ throw createInvalidEndpointError2(baseEndpoint, invalidText);
16275
+ }
16276
+ return trimTrailingSlashes2(parsedEndpoint.origin);
16277
+ } catch (error48) {
16278
+ if (error48 instanceof error$1.MastraError) {
16279
+ throw error48;
16280
+ }
16281
+ throw createInvalidEndpointError2(baseEndpoint, invalidText, error48);
16282
+ }
16283
+ }
16284
+ function buildSignalPath2(signal, projectId) {
16285
+ const signalSegment = SIGNAL_PUBLISH_SEGMENTS2[signal];
16286
+ if (!projectId) {
16287
+ return `/ai/${signalSegment}/publish`;
16288
+ }
16289
+ return `/projects/${projectId}/ai/${signalSegment}/publish`;
16290
+ }
16291
+ function buildSignalEndpoint2(baseEndpoint, signal, projectId) {
16292
+ return `${baseEndpoint}${buildSignalPath2(signal, projectId)}`;
16293
+ }
16294
+ function resolveExplicitSignalEndpoint2(signal, endpoint, projectId) {
16295
+ const normalizedEndpoint = trimTrailingSlashes2(endpoint);
16296
+ const invalidText = `MastraPlatformExporter ${signal}Endpoint must be a base origin like "https://collector.example.com" or a full ${signal} publish URL ending in "${SIGNAL_PUBLISH_SUFFIXES2[signal]}".`;
16297
+ try {
16298
+ const parsedEndpoint = new URL(normalizedEndpoint);
16299
+ if (parsedEndpoint.search || parsedEndpoint.hash) {
16300
+ throw createInvalidEndpointError2(endpoint, invalidText);
16301
+ }
16302
+ const normalizedOrigin = trimTrailingSlashes2(parsedEndpoint.origin);
16303
+ const normalizedPathname = trimTrailingSlashes2(parsedEndpoint.pathname);
16304
+ if (!normalizedPathname || normalizedPathname === "/") {
16305
+ return buildSignalEndpoint2(normalizedOrigin, signal, projectId);
16306
+ }
16307
+ if (normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES2[signal])) {
16308
+ return `${normalizedOrigin}${normalizedPathname}`;
16309
+ }
16310
+ throw createInvalidEndpointError2(endpoint, invalidText);
16311
+ } catch (error48) {
16312
+ if (error48 instanceof error$1.MastraError) {
16313
+ throw error48;
16314
+ }
16315
+ throw createInvalidEndpointError2(endpoint, invalidText, error48);
16316
+ }
16317
+ }
16318
+ function deriveSignalEndpointFromTracesEndpoint2(signal, tracesEndpoint) {
16319
+ if (signal === "traces") {
16320
+ return tracesEndpoint;
16321
+ }
16322
+ const normalizedTracesEndpoint = trimTrailingSlashes2(tracesEndpoint);
16323
+ const invalidText = 'MastraPlatformExporter tracesEndpoint must be a base origin like "https://collector.example.com" or a full traces publish URL ending in "/spans/publish".';
16324
+ try {
16325
+ const parsedEndpoint = new URL(normalizedTracesEndpoint);
16326
+ const normalizedOrigin = trimTrailingSlashes2(parsedEndpoint.origin);
16327
+ const normalizedPathname = trimTrailingSlashes2(parsedEndpoint.pathname);
16328
+ if (!normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES2.traces)) {
16329
+ throw createInvalidEndpointError2(tracesEndpoint, invalidText);
16330
+ }
16331
+ const basePath = normalizedPathname.slice(0, -SIGNAL_PUBLISH_SUFFIXES2.traces.length);
16332
+ return `${normalizedOrigin}${basePath}${SIGNAL_PUBLISH_SUFFIXES2[signal]}`;
16333
+ } catch (error48) {
16334
+ if (error48 instanceof error$1.MastraError) {
16335
+ throw error48;
16336
+ }
16337
+ throw createInvalidEndpointError2(tracesEndpoint, invalidText, error48);
16338
+ }
16339
+ }
16340
+ var MastraPlatformExporter = class extends BaseExporter {
16341
+ name = "mastra-platform-exporter";
16342
+ platformConfig;
16343
+ buffer;
16344
+ flushTimer = null;
16345
+ inFlightFlushes = /* @__PURE__ */ new Set();
16346
+ constructor(config2 = {}) {
16347
+ super(config2);
16348
+ if (config2.projectId !== void 0 && !VALID_PROJECT_ID2.test(config2.projectId)) {
16349
+ throw createInvalidProjectIdError2(config2.projectId);
16350
+ }
16351
+ const accessToken = config2.accessToken || process.env.MASTRA_PLATFORM_ACCESS_TOKEN || process.env.MASTRA_CLOUD_ACCESS_TOKEN;
16352
+ const envProjectId = process.env.MASTRA_PROJECT_ID === "" ? void 0 : process.env.MASTRA_PROJECT_ID;
16353
+ const rawProjectId = config2.projectId ?? envProjectId;
16354
+ if (rawProjectId !== void 0 && !VALID_PROJECT_ID2.test(rawProjectId)) {
16355
+ throw createInvalidProjectIdError2(rawProjectId);
16356
+ }
16357
+ const projectId = rawProjectId;
16358
+ if (!accessToken) {
16359
+ this.setDisabled("MASTRA_PLATFORM_ACCESS_TOKEN environment variable not set.", "debug");
16360
+ }
16361
+ const tracesEndpointOverride = config2.tracesEndpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT;
16362
+ let baseEndpoint;
16363
+ let tracesEndpoint;
16364
+ if (tracesEndpointOverride) {
16365
+ tracesEndpoint = resolveExplicitSignalEndpoint2("traces", tracesEndpointOverride, projectId);
16366
+ } else {
16367
+ baseEndpoint = resolveBaseEndpoint2(config2.endpoint ?? "https://observability.mastra.ai");
16368
+ tracesEndpoint = buildSignalEndpoint2(baseEndpoint, "traces", projectId);
16369
+ }
16370
+ const resolveConfiguredSignalEndpoint = (signal, explicitEndpoint) => {
16371
+ if (explicitEndpoint) {
16372
+ return resolveExplicitSignalEndpoint2(signal, explicitEndpoint, projectId);
16373
+ }
16374
+ if (tracesEndpointOverride) {
16375
+ return deriveSignalEndpointFromTracesEndpoint2(signal, tracesEndpoint);
16376
+ }
16377
+ return buildSignalEndpoint2(baseEndpoint, signal, projectId);
16378
+ };
16379
+ this.platformConfig = {
16380
+ logger: this.logger,
16381
+ logLevel: config2.logLevel ?? logger.LogLevel.INFO,
16382
+ maxBatchSize: config2.maxBatchSize ?? 1e3,
16383
+ maxBatchWaitMs: config2.maxBatchWaitMs ?? 5e3,
16384
+ maxRetries: config2.maxRetries ?? 3,
16385
+ accessToken: accessToken || "",
16386
+ tracesEndpoint,
16387
+ logsEndpoint: resolveConfiguredSignalEndpoint("logs", config2.logsEndpoint),
16388
+ metricsEndpoint: resolveConfiguredSignalEndpoint("metrics", config2.metricsEndpoint),
16389
+ scoresEndpoint: resolveConfiguredSignalEndpoint("scores", config2.scoresEndpoint),
16390
+ feedbackEndpoint: resolveConfiguredSignalEndpoint("feedback", config2.feedbackEndpoint)
16391
+ };
16392
+ this.buffer = {
16393
+ spans: [],
16394
+ logs: [],
16395
+ metrics: [],
16396
+ scores: [],
16397
+ feedback: [],
16398
+ totalSize: 0
16399
+ };
16400
+ }
16401
+ async _exportTracingEvent(event) {
16402
+ if (event.type !== observability.TracingEventType.SPAN_ENDED) {
16403
+ return;
16404
+ }
16405
+ if (!DEFAULT_PLATFORM_SPAN_FILTER(event.exportedSpan)) {
16406
+ return;
16407
+ }
16408
+ this.addToBuffer(event);
16409
+ await this.handleBufferedEvent();
16410
+ }
16411
+ async onLogEvent(event) {
16412
+ if (this.isDisabled) {
16413
+ return;
16414
+ }
16415
+ this.addLogToBuffer(event);
16416
+ await this.handleBufferedEvent();
16417
+ }
16418
+ async onMetricEvent(event) {
16419
+ if (this.isDisabled) {
16420
+ return;
16421
+ }
16422
+ this.addMetricToBuffer(event);
16423
+ await this.handleBufferedEvent();
16424
+ }
16425
+ async onScoreEvent(event) {
16426
+ if (this.isDisabled) {
16427
+ return;
16428
+ }
16429
+ this.addScoreToBuffer(event);
16430
+ await this.handleBufferedEvent();
16431
+ }
16432
+ async onFeedbackEvent(event) {
16433
+ if (this.isDisabled) {
16434
+ return;
16435
+ }
16436
+ this.addFeedbackToBuffer(event);
16437
+ await this.handleBufferedEvent();
16438
+ }
16439
+ addToBuffer(event) {
16440
+ this.markBufferStart();
16441
+ const spanRecord = this.formatSpan(event.exportedSpan);
16442
+ this.buffer.spans.push(spanRecord);
16443
+ this.buffer.totalSize++;
16444
+ }
16445
+ addLogToBuffer(event) {
16446
+ this.markBufferStart();
16447
+ this.buffer.logs.push(this.formatLog(event.log));
16448
+ this.buffer.totalSize++;
16449
+ }
16450
+ addMetricToBuffer(event) {
16451
+ this.markBufferStart();
16452
+ this.buffer.metrics.push(this.formatMetric(event.metric));
16453
+ this.buffer.totalSize++;
16454
+ }
16455
+ addScoreToBuffer(event) {
16456
+ this.markBufferStart();
16457
+ this.buffer.scores.push(this.formatScore(event.score));
16458
+ this.buffer.totalSize++;
16459
+ }
16460
+ addFeedbackToBuffer(event) {
16461
+ this.markBufferStart();
16462
+ this.buffer.feedback.push(this.formatFeedback(event.feedback));
16463
+ this.buffer.totalSize++;
16464
+ }
16465
+ markBufferStart() {
16466
+ if (this.buffer.totalSize === 0) {
16467
+ this.buffer.firstEventTime = /* @__PURE__ */ new Date();
16468
+ }
16469
+ }
16470
+ formatSpan(span) {
16471
+ const spanRecord = {
16472
+ ...span,
16473
+ spanId: span.id,
16474
+ spanType: span.type,
16475
+ startedAt: span.startTime,
16476
+ endedAt: span.endTime ?? null,
16477
+ error: span.errorInfo ?? null,
16478
+ createdAt: /* @__PURE__ */ new Date(),
16479
+ updatedAt: null
16480
+ };
16481
+ return spanRecord;
16482
+ }
16483
+ formatLog(log) {
16484
+ return {
16485
+ ...log
16486
+ };
16487
+ }
16488
+ formatMetric(metric) {
16489
+ return {
16490
+ ...metric
16491
+ };
16492
+ }
16493
+ formatScore(score) {
16494
+ return {
16495
+ ...score
16496
+ };
16497
+ }
16498
+ formatFeedback(feedback) {
16499
+ return {
16500
+ ...feedback
16501
+ };
16502
+ }
16503
+ async handleBufferedEvent() {
16504
+ if (this.shouldFlush()) {
16505
+ void this.flush().catch((error48) => {
16506
+ this.logger.error("Batch flush failed", {
16507
+ error: error48 instanceof Error ? error48.message : String(error48)
16508
+ });
16509
+ });
16510
+ } else if (this.buffer.totalSize === 1) {
16511
+ this.scheduleFlush();
16512
+ }
16513
+ }
16514
+ shouldFlush() {
16515
+ if (this.buffer.totalSize >= this.platformConfig.maxBatchSize) {
16516
+ return true;
16517
+ }
16518
+ if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
16519
+ const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
16520
+ if (elapsed >= this.platformConfig.maxBatchWaitMs) {
16521
+ return true;
16522
+ }
16523
+ }
16524
+ return false;
16525
+ }
16526
+ scheduleFlush() {
16527
+ if (this.flushTimer) {
16528
+ clearTimeout(this.flushTimer);
16529
+ }
16530
+ this.flushTimer = setTimeout(() => {
16531
+ void this.flush().catch((error48) => {
16532
+ const mastraError = new error$1.MastraError(
16533
+ {
16534
+ id: `MASTRA_PLATFORM_EXPORTER_FAILED_TO_SCHEDULE_FLUSH`,
16535
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
16536
+ category: error$1.ErrorCategory.USER
16537
+ },
16538
+ error48
16539
+ );
16540
+ this.logger.trackException(mastraError);
16541
+ this.logger.error("Scheduled flush failed", mastraError);
16542
+ });
16543
+ }, this.platformConfig.maxBatchWaitMs);
16544
+ }
16545
+ async flushBuffer() {
16546
+ if (this.flushTimer) {
16547
+ clearTimeout(this.flushTimer);
16548
+ this.flushTimer = null;
16549
+ }
16550
+ if (this.buffer.totalSize === 0) {
16551
+ return;
16552
+ }
16553
+ const startTime = Date.now();
16554
+ const spansCopy = [...this.buffer.spans];
16555
+ const logsCopy = [...this.buffer.logs];
16556
+ const metricsCopy = [...this.buffer.metrics];
16557
+ const scoresCopy = [...this.buffer.scores];
16558
+ const feedbackCopy = [...this.buffer.feedback];
16559
+ const batchSize = this.buffer.totalSize;
16560
+ const flushReason = this.buffer.totalSize >= this.platformConfig.maxBatchSize ? "size" : "time";
16561
+ this.resetBuffer();
16562
+ const results = await Promise.all([
16563
+ this.flushSignalBatch("traces", spansCopy),
16564
+ this.flushSignalBatch("logs", logsCopy),
16565
+ this.flushSignalBatch("metrics", metricsCopy),
16566
+ this.flushSignalBatch("scores", scoresCopy),
16567
+ this.flushSignalBatch("feedback", feedbackCopy)
16568
+ ]);
16569
+ const failedSignals = results.filter((result) => !result.succeeded).map((result) => result.signal);
16570
+ const elapsed = Date.now() - startTime;
16571
+ if (failedSignals.length === 0) {
16572
+ this.logger.debug("Batch flushed successfully", {
16573
+ batchSize,
16574
+ flushReason,
16575
+ durationMs: elapsed
16576
+ });
16577
+ return;
16578
+ }
16579
+ this.logger.warn("Batch flush completed with dropped signal batches", {
16580
+ batchSize,
16581
+ flushReason,
16582
+ durationMs: elapsed,
16583
+ failedSignals
16584
+ });
16585
+ }
16586
+ /**
16587
+ * Uploads a signal batch to the configured Mastra Observability API using fetchWithRetry.
16588
+ */
16589
+ async batchUpload(signal, records) {
16590
+ const headers = {
16591
+ Authorization: `Bearer ${this.platformConfig.accessToken}`,
16592
+ "Content-Type": "application/json"
16593
+ };
16594
+ const endpointMap = {
16595
+ traces: this.platformConfig.tracesEndpoint,
16596
+ logs: this.platformConfig.logsEndpoint,
16597
+ metrics: this.platformConfig.metricsEndpoint,
16598
+ scores: this.platformConfig.scoresEndpoint,
16599
+ feedback: this.platformConfig.feedbackEndpoint
16600
+ };
16601
+ const options = {
16602
+ method: "POST",
16603
+ headers,
16604
+ body: JSON.stringify({ [SIGNAL_PUBLISH_SEGMENTS2[signal]]: records })
16605
+ };
16606
+ await utils.fetchWithRetry(endpointMap[signal], options, this.platformConfig.maxRetries);
16607
+ }
16608
+ async flushSignalBatch(signal, records) {
16609
+ if (records.length === 0) {
16610
+ return { signal, succeeded: true };
16611
+ }
16612
+ try {
16613
+ await this.batchUpload(signal, records);
16614
+ return { signal, succeeded: true };
16615
+ } catch (error48) {
16616
+ const errorId = `MASTRA_PLATFORM_EXPORTER_FAILED_TO_BATCH_UPLOAD_${signal.toUpperCase()}`;
16617
+ const mastraError = new error$1.MastraError(
16618
+ {
16619
+ id: errorId,
16620
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
16621
+ category: error$1.ErrorCategory.USER,
16622
+ details: {
16623
+ signal,
16624
+ droppedBatchSize: records.length
16625
+ }
16626
+ },
16627
+ error48
16628
+ );
16629
+ this.logger.trackException(mastraError);
16630
+ this.logger.error("Batch upload failed after all retries, dropping batch", mastraError);
16631
+ return { signal, succeeded: false };
16632
+ }
16633
+ }
16634
+ resetBuffer() {
16635
+ this.buffer.spans = [];
16636
+ this.buffer.logs = [];
16637
+ this.buffer.metrics = [];
16638
+ this.buffer.scores = [];
16639
+ this.buffer.feedback = [];
16640
+ this.buffer.firstEventTime = void 0;
16641
+ this.buffer.totalSize = 0;
16642
+ }
16643
+ /**
16644
+ * Force flush any buffered events without shutting down the exporter.
16645
+ * This is useful in serverless environments where you need to ensure events
16646
+ * are exported before the runtime instance is terminated.
16647
+ */
16648
+ async flush() {
16649
+ if (this.isDisabled) {
16650
+ return;
16651
+ }
16652
+ while (this.buffer.totalSize > 0 || this.inFlightFlushes.size > 0) {
16653
+ if (this.buffer.totalSize > 0) {
16654
+ this.logger.debug("Flushing buffered events", {
16655
+ bufferedEvents: this.buffer.totalSize
16656
+ });
16657
+ const flushPromise = this.flushBuffer();
16658
+ this.inFlightFlushes.add(flushPromise);
16659
+ try {
16660
+ await flushPromise;
16661
+ } finally {
16662
+ this.inFlightFlushes.delete(flushPromise);
16663
+ }
16664
+ continue;
16665
+ }
16666
+ await Promise.allSettled([...this.inFlightFlushes]);
16667
+ }
16668
+ }
16669
+ async shutdown() {
16670
+ if (this.isDisabled) {
16671
+ return;
16672
+ }
16673
+ if (this.flushTimer) {
16674
+ clearTimeout(this.flushTimer);
16675
+ this.flushTimer = null;
16676
+ }
16677
+ try {
16678
+ await this.flush();
16679
+ } catch (error48) {
16680
+ const mastraError = new error$1.MastraError(
16681
+ {
16682
+ id: `MASTRA_PLATFORM_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
16683
+ domain: error$1.ErrorDomain.MASTRA_OBSERVABILITY,
16684
+ category: error$1.ErrorCategory.USER,
16685
+ details: {
16686
+ remainingEvents: this.buffer.totalSize
16687
+ }
16688
+ },
16689
+ error48
16690
+ );
16691
+ this.logger.trackException(mastraError);
16692
+ this.logger.error("Failed to flush remaining events during shutdown", mastraError);
16693
+ }
16694
+ this.logger.info("MastraPlatformExporter shutdown complete");
16695
+ }
16696
+ };
16697
+ function resolveTracingStorageStrategy2(config2, observabilityStorage, storageName, logger) {
16698
+ const observabilityStrategy = observabilityStorage.observabilityStrategy;
16699
+ if (config2.strategy && config2.strategy !== "auto") {
16700
+ if (observabilityStrategy.supported.includes(config2.strategy)) {
16701
+ return config2.strategy;
16702
+ }
16703
+ logger.warn("User-specified tracing strategy not supported by storage adapter, falling back to auto-selection", {
16704
+ userStrategy: config2.strategy,
16705
+ storageAdapter: storageName,
16706
+ supportedStrategies: observabilityStrategy.supported,
16707
+ fallbackStrategy: observabilityStrategy.preferred
16708
+ });
16709
+ }
16710
+ return observabilityStrategy.preferred;
16711
+ }
16712
+ var MastraStorageExporter = class extends BaseExporter {
16713
+ name = "mastra-storage-exporter";
16714
+ #config;
16715
+ #isInitializing = false;
16716
+ #initPromises = /* @__PURE__ */ new Set();
16717
+ #eventBuffer;
16718
+ #storage;
16719
+ #observabilityStorage;
16720
+ #resolvedStrategy;
16721
+ #flushTimer;
16722
+ // Signals whose storage methods threw "not implemented" — skip on future flushes
16723
+ #unsupportedSignals = /* @__PURE__ */ new Set();
16724
+ constructor(config2 = {}) {
16725
+ super(config2);
16726
+ this.#config = {
16727
+ ...config2,
16728
+ maxBatchSize: config2.maxBatchSize ?? 1e3,
16729
+ maxBufferSize: config2.maxBufferSize ?? 1e4,
16730
+ maxBatchWaitMs: config2.maxBatchWaitMs ?? 5e3,
16731
+ maxRetries: config2.maxRetries ?? 4,
16732
+ retryDelayMs: config2.retryDelayMs ?? 500,
16733
+ strategy: config2.strategy ?? "auto"
16734
+ };
16735
+ this.#eventBuffer = new EventBuffer({ maxRetries: this.#config.maxRetries ?? 4 });
16736
+ }
16737
+ /**
16738
+ * Initialize the exporter (called after all dependencies are ready)
16739
+ */
16740
+ async init(options) {
16741
+ try {
16742
+ this.#isInitializing = true;
16743
+ this.#storage = options.mastra?.getStorage();
16744
+ if (!this.#storage) {
16745
+ this.logger.warn("MastraStorageExporter disabled: Storage not available. Traces will not be persisted.");
16746
+ return;
16747
+ }
16748
+ this.#observabilityStorage = await this.#storage.getStore("observability");
16749
+ if (!this.#observabilityStorage) {
16750
+ this.logger.warn(
16751
+ "MastraStorageExporter disabled: Observability storage not available. Traces will not be persisted."
16752
+ );
16753
+ return;
16754
+ }
16755
+ if (!this.#resolvedStrategy) {
16756
+ this.#resolvedStrategy = resolveTracingStorageStrategy2(
16757
+ this.#config,
16758
+ this.#observabilityStorage,
16759
+ this.#storage.constructor.name,
16760
+ this.logger
16761
+ );
16762
+ this.logger.debug("tracing storage exporter initialized", {
16763
+ strategy: this.#resolvedStrategy,
16764
+ source: this.#config.strategy !== "auto" ? "user" : "auto",
16765
+ storageAdapter: this.#storage.constructor.name,
16766
+ maxBatchSize: this.#config.maxBatchSize,
16767
+ maxBatchWaitMs: this.#config.maxBatchWaitMs
16768
+ });
16769
+ }
16770
+ if (this.#resolvedStrategy) {
16771
+ this.#eventBuffer.init({ strategy: this.#resolvedStrategy });
16772
+ }
16773
+ } finally {
16774
+ this.#isInitializing = false;
16775
+ this.#initPromises.forEach((resolve) => {
16776
+ resolve();
16777
+ });
16778
+ this.#initPromises.clear();
16779
+ }
16780
+ }
16781
+ /**
16782
+ * Checks if buffer should be flushed based on size or time triggers
16783
+ */
16784
+ shouldFlush() {
16785
+ if (this.#resolvedStrategy === "realtime") {
16786
+ return true;
16787
+ }
16788
+ if (this.#eventBuffer.totalSize >= this.#config.maxBufferSize) {
16789
+ return true;
16790
+ }
16791
+ if (this.#eventBuffer.totalSize >= this.#config.maxBatchSize) {
16792
+ return true;
16793
+ }
16794
+ if (this.#eventBuffer.totalSize > 0) {
16795
+ if (this.#eventBuffer.elapsed >= this.#config.maxBatchWaitMs) {
16796
+ return true;
16797
+ }
16798
+ }
16799
+ return false;
16800
+ }
16801
+ /**
16802
+ * Schedules a flush using setTimeout
16803
+ */
16804
+ scheduleFlush() {
16805
+ if (this.#flushTimer) {
16806
+ clearTimeout(this.#flushTimer);
16807
+ }
16808
+ this.#flushTimer = setTimeout(() => {
16809
+ this.flushBuffer().catch((error48) => {
16810
+ this.logger.error("Scheduled flush failed", {
16811
+ error: error48 instanceof Error ? error48.message : String(error48)
16812
+ });
16813
+ });
16814
+ }, this.#config.maxBatchWaitMs);
16815
+ }
16816
+ /**
16817
+ * Checks flush triggers and schedules/triggers flush as needed.
16818
+ * Called after adding any event to the buffer.
16819
+ * Returns the flush promise when flushing so callers can await it.
16820
+ */
16821
+ async handleBatchedFlush() {
16822
+ if (this.shouldFlush()) {
16823
+ await this.flushBuffer();
16824
+ } else if (this.#eventBuffer.totalSize === 1) {
16825
+ this.scheduleFlush();
16826
+ }
16827
+ }
16828
+ /**
16829
+ * Flush a batch of create events for a single signal type.
16830
+ * On "not implemented" errors, disables the signal for future flushes.
16831
+ * On other errors, re-adds events to the buffer for retry.
16832
+ */
16833
+ async flushCreates(signal, events, storageCall) {
16834
+ if (this.#unsupportedSignals.has(signal) || events.length === 0) return;
16835
+ try {
16836
+ await storageCall(events);
16837
+ } catch (error48) {
16838
+ if (error48 instanceof error$1.MastraError && error48.domain === error$1.ErrorDomain.MASTRA_OBSERVABILITY && error48.id.endsWith("_NOT_IMPLEMENTED")) {
16839
+ this.logger.warn(error48.message);
16840
+ this.#unsupportedSignals.add(signal);
16841
+ } else {
16842
+ this.#eventBuffer.reAddCreates(events);
16843
+ }
16844
+ }
16845
+ }
16846
+ /**
16847
+ * Flush span update/end events, deferring any whose span hasn't been created yet.
16848
+ * When `isEnd` is true, successfully flushed spans are removed from tracking.
16849
+ */
16850
+ async flushSpanUpdates(events, deferredUpdates, isEnd) {
16851
+ if (this.#unsupportedSignals.has("tracing") || events.length === 0) return;
16852
+ const partials = [];
16853
+ for (const event of events) {
16854
+ const span = event.exportedSpan;
16855
+ if (this.#eventBuffer.spanExists(span)) {
16856
+ partials.push({
16857
+ traceId: span.traceId,
16858
+ spanId: span.id,
16859
+ updates: storage.buildUpdateSpanRecord(span)
16860
+ });
16861
+ } else {
16862
+ deferredUpdates.push(event);
16863
+ }
16864
+ }
16865
+ if (partials.length === 0) return;
16866
+ try {
16867
+ await this.#observabilityStorage.batchUpdateSpans({ records: partials });
16868
+ if (isEnd) {
16869
+ this.#eventBuffer.endFinishedSpans({ records: partials });
16870
+ }
16871
+ } catch (error48) {
16872
+ if (error48 instanceof error$1.MastraError && error48.domain === error$1.ErrorDomain.MASTRA_OBSERVABILITY && error48.id.endsWith("_NOT_IMPLEMENTED")) {
16873
+ this.logger.warn(error48.message);
16874
+ this.#unsupportedSignals.add("tracing");
16875
+ } else {
16876
+ deferredUpdates.length = 0;
16877
+ this.#eventBuffer.reAddUpdates(events);
16878
+ }
16879
+ }
16880
+ }
16881
+ /**
16882
+ * Flushes the current buffer to storage.
16883
+ *
16884
+ * Creates are flushed first, then their span keys are added to allCreatedSpans.
16885
+ * Updates are checked against allCreatedSpans — those whose span hasn't been
16886
+ * created yet are re-inserted into the live buffer for the next flush.
16887
+ * Completed spans (SPAN_ENDED) are cleaned up from allCreatedSpans after success.
16888
+ */
16889
+ async flushBuffer() {
16890
+ if (!this.#observabilityStorage) {
16891
+ this.logger.debug("Cannot flush. Observability storage is not initialized");
16892
+ return;
16893
+ }
16894
+ if (!this.#resolvedStrategy) {
16895
+ this.logger.debug("Cannot flush. Observability strategy is not resolved");
16896
+ return;
16897
+ }
16898
+ if (this.#flushTimer) {
16899
+ clearTimeout(this.#flushTimer);
16900
+ this.#flushTimer = void 0;
16901
+ }
16902
+ if (this.#eventBuffer.totalSize === 0) {
16903
+ return;
16904
+ }
16905
+ const startTime = Date.now();
16906
+ const batchSize = this.#eventBuffer.totalSize;
16907
+ const creates = this.#eventBuffer.creates;
16908
+ const updates = this.#eventBuffer.updates;
16909
+ this.#eventBuffer.reset();
16910
+ const createFeedbackEvents = [];
16911
+ const createLogEvents = [];
16912
+ const createMetricEvents = [];
16913
+ const createScoreEvents = [];
16914
+ const createSpanEvents = [];
16915
+ const updateSpanEvents = [];
16916
+ const endSpanEvents = [];
16917
+ for (const createEvent of creates) {
16918
+ switch (createEvent.type) {
16919
+ case "feedback":
16920
+ createFeedbackEvents.push(createEvent);
16921
+ break;
16922
+ case "log":
16923
+ createLogEvents.push(createEvent);
16924
+ break;
16925
+ case "metric":
16926
+ createMetricEvents.push(createEvent);
16927
+ break;
16928
+ case "score":
16929
+ createScoreEvents.push(createEvent);
16930
+ break;
16931
+ default:
16932
+ createSpanEvents.push(createEvent);
16933
+ break;
16934
+ }
16935
+ }
16936
+ for (const updateEvent of updates) {
16937
+ switch (updateEvent.type) {
16938
+ case observability.TracingEventType.SPAN_UPDATED:
16939
+ updateSpanEvents.push(updateEvent);
16940
+ break;
16941
+ case observability.TracingEventType.SPAN_ENDED:
16942
+ endSpanEvents.push(updateEvent);
16943
+ break;
16944
+ }
16945
+ }
16946
+ await Promise.all([
16947
+ this.flushCreates(
16948
+ "feedback",
16949
+ createFeedbackEvents,
16950
+ (events) => this.#observabilityStorage.batchCreateFeedback({ feedbacks: events.map((f) => storage.buildFeedbackRecord(f)) })
16951
+ ),
16952
+ this.flushCreates(
16953
+ "logs",
16954
+ createLogEvents,
16955
+ (events) => this.#observabilityStorage.batchCreateLogs({ logs: events.map((l) => storage.buildLogRecord(l)) })
16956
+ ),
16957
+ this.flushCreates(
16958
+ "metrics",
16959
+ createMetricEvents,
16960
+ (events) => this.#observabilityStorage.batchCreateMetrics({ metrics: events.map((m) => storage.buildMetricRecord(m)) })
16961
+ ),
16962
+ this.flushCreates(
16963
+ "scores",
16964
+ createScoreEvents,
16965
+ (events) => this.#observabilityStorage.batchCreateScores({ scores: events.map((s) => storage.buildScoreRecord(s)) })
16966
+ ),
16967
+ this.flushCreates("tracing", createSpanEvents, async (events) => {
16968
+ const records = events.map((t) => storage.buildCreateSpanRecord(t.exportedSpan));
16969
+ await this.#observabilityStorage.batchCreateSpans({ records });
16970
+ this.#eventBuffer.addCreatedSpans({ records });
16971
+ })
16972
+ ]);
16973
+ const deferredUpdates = [];
16974
+ await this.flushSpanUpdates(updateSpanEvents, deferredUpdates, false);
16975
+ await this.flushSpanUpdates(endSpanEvents, deferredUpdates, true);
16976
+ if (deferredUpdates.length > 0) {
16977
+ this.#eventBuffer.reAddUpdates(deferredUpdates);
16978
+ }
16979
+ const elapsed = Date.now() - startTime;
16980
+ this.logger.debug("Batch flushed", {
16981
+ strategy: this.#resolvedStrategy,
16982
+ batchSize,
16983
+ durationMs: elapsed,
16984
+ deferredUpdates: deferredUpdates.length > 0 ? deferredUpdates.length : void 0
16985
+ });
16986
+ return;
16987
+ }
16988
+ async _exportTracingEvent(event) {
16989
+ await this.waitForInit();
16990
+ if (!this.#observabilityStorage) {
16991
+ this.logger.debug("Cannot store traces. Observability storage is not initialized");
16992
+ return;
16993
+ }
16994
+ this.#eventBuffer.addEvent(event);
16995
+ await this.handleBatchedFlush();
16996
+ }
16997
+ /**
16998
+ * Resolves when an ongoing init call is finished
16999
+ * Doesn't wait for the caller to call init
17000
+ * @returns
17001
+ */
17002
+ async waitForInit() {
17003
+ if (!this.#isInitializing) return;
17004
+ return new Promise((resolve) => {
17005
+ this.#initPromises.add(resolve);
17006
+ });
17007
+ }
17008
+ /**
17009
+ * Handle metric events — buffer for batch flush.
17010
+ */
17011
+ async onMetricEvent(event) {
17012
+ await this.waitForInit();
17013
+ if (!this.#observabilityStorage) return;
17014
+ this.#eventBuffer.addEvent(event);
17015
+ await this.handleBatchedFlush();
17016
+ }
17017
+ /**
17018
+ * Handle log events — buffer for batch flush.
17019
+ */
17020
+ async onLogEvent(event) {
17021
+ await this.waitForInit();
17022
+ if (!this.#observabilityStorage) return;
17023
+ this.#eventBuffer.addEvent(event);
17024
+ await this.handleBatchedFlush();
17025
+ }
17026
+ /**
17027
+ * Handle score events — buffer for batch flush.
17028
+ */
17029
+ async onScoreEvent(event) {
17030
+ await this.waitForInit();
17031
+ if (!this.#observabilityStorage) return;
17032
+ this.#eventBuffer.addEvent(event);
17033
+ await this.handleBatchedFlush();
17034
+ }
17035
+ /**
17036
+ * Handle feedback events — buffer for batch flush.
17037
+ */
17038
+ async onFeedbackEvent(event) {
17039
+ await this.waitForInit();
17040
+ if (!this.#observabilityStorage) return;
17041
+ this.#eventBuffer.addEvent(event);
17042
+ await this.handleBatchedFlush();
17043
+ }
17044
+ /**
17045
+ * Force flush any buffered spans without shutting down the exporter.
17046
+ * This is useful in serverless environments where you need to ensure spans
17047
+ * are exported before the runtime instance is terminated.
17048
+ */
17049
+ async flush() {
17050
+ if (this.#eventBuffer.totalSize > 0) {
17051
+ this.logger.debug("Flushing buffered events", {
17052
+ bufferedEvents: this.#eventBuffer.totalSize
17053
+ });
17054
+ await this.flushBuffer();
17055
+ }
17056
+ }
17057
+ async shutdown() {
17058
+ if (this.#flushTimer) {
17059
+ clearTimeout(this.#flushTimer);
17060
+ this.#flushTimer = void 0;
17061
+ }
17062
+ await this.flush();
17063
+ this.logger.info("MastraStorageExporter shutdown complete");
17064
+ }
17065
+ };
16220
17066
  var _snapshotsDir;
16221
17067
  async function getSnapshotsDir() {
16222
17068
  if (!_snapshotsDir) {
@@ -18183,7 +19029,6 @@ var PricingRegistry = class _PricingRegistry {
18183
19029
  constructor(pricingModels) {
18184
19030
  this.pricingModels = pricingModels;
18185
19031
  }
18186
- pricingModels;
18187
19032
  static globalRegistry = null;
18188
19033
  static fromText(pricingModelText) {
18189
19034
  return new _PricingRegistry(parsePricingModelText(pricingModelText));
@@ -19035,7 +19880,8 @@ var ModelSpanTracker = class {
19035
19880
  ...payload?.messageId ? { messageId: payload.messageId } : {},
19036
19881
  ...payload?.warnings?.length ? { warnings: payload.warnings } : {}
19037
19882
  },
19038
- input
19883
+ input,
19884
+ tracingPolicy: this.#modelSpan?.tracingPolicy
19039
19885
  });
19040
19886
  this.#currentStepInputIsFinal = Array.isArray(payload?.inputMessages);
19041
19887
  this.#chunkSequence = 0;
@@ -19102,7 +19948,8 @@ var ModelSpanTracker = class {
19102
19948
  ...ctx?.toolChoice !== void 0 ? { toolChoice: ctx.toolChoice } : {},
19103
19949
  ...ctx?.responseFormat !== void 0 ? { responseFormat: ctx.responseFormat } : {}
19104
19950
  },
19105
- input
19951
+ input,
19952
+ tracingPolicy: this.#modelSpan?.tracingPolicy
19106
19953
  });
19107
19954
  }
19108
19955
  /**
@@ -19195,7 +20042,8 @@ var ModelSpanTracker = class {
19195
20042
  attributes: {
19196
20043
  chunkType,
19197
20044
  sequenceNumber: this.#chunkSequence
19198
- }
20045
+ },
20046
+ tracingPolicy: this.#modelSpan?.tracingPolicy
19199
20047
  });
19200
20048
  this.#currentChunkType = chunkType;
19201
20049
  this.#accumulator = initialData || {};
@@ -19238,7 +20086,8 @@ var ModelSpanTracker = class {
19238
20086
  ...options?.attributes
19239
20087
  },
19240
20088
  metadata: options?.metadata,
19241
- output
20089
+ output,
20090
+ tracingPolicy: this.#modelSpan?.tracingPolicy
19242
20091
  });
19243
20092
  if (span) {
19244
20093
  this.#chunkSequence++;
@@ -19358,7 +20207,8 @@ var ModelSpanTracker = class {
19358
20207
  chunkType: "tool-call-approval",
19359
20208
  sequenceNumber: this.#chunkSequence
19360
20209
  },
19361
- output: payload
20210
+ output: payload,
20211
+ tracingPolicy: this.#modelSpan?.tracingPolicy
19362
20212
  });
19363
20213
  if (span) {
19364
20214
  this.#chunkSequence++;
@@ -21156,14 +22006,14 @@ var Observability = class extends base.MastraBase {
21156
22006
  };
21157
22007
  if (config2.default?.enabled) {
21158
22008
  console.warn(
21159
- '[Mastra Observability] The "default: { enabled: true }" configuration is deprecated and will be removed in a future version. Please use explicit configs with DefaultExporter and CloudExporter instead. Sensitive data filtering is applied by default and can be controlled via the top-level "sensitiveDataFilter" option. See https://mastra.ai/docs/observability/tracing/overview for the recommended configuration.'
22009
+ '[Mastra Observability] The "default: { enabled: true }" configuration is deprecated and will be removed in a future version. Please use explicit configs with MastraStorageExporter and MastraPlatformExporter instead. Sensitive data filtering is applied by default and can be controlled via the top-level "sensitiveDataFilter" option. See https://mastra.ai/docs/observability/tracing/overview for the recommended configuration.'
21160
22010
  );
21161
22011
  const autoFilter = buildAutoSensitiveFilter();
21162
22012
  const defaultInstance = new DefaultObservabilityInstance({
21163
22013
  serviceName: "mastra",
21164
22014
  name: "default",
21165
22015
  sampling: { type: "always" /* ALWAYS */ },
21166
- exporters: [new DefaultExporter(), new CloudExporter()],
22016
+ exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],
21167
22017
  spanOutputProcessors: autoFilter ? [autoFilter] : []
21168
22018
  });
21169
22019
  this.#registry.register("default", defaultInstance, true);
@@ -21427,6 +22277,8 @@ exports.DefaultObservabilityInstance = DefaultObservabilityInstance;
21427
22277
  exports.DefaultSpan = DefaultSpan;
21428
22278
  exports.JsonExporter = JsonExporter;
21429
22279
  exports.LoggerContextImpl = LoggerContextImpl;
22280
+ exports.MastraPlatformExporter = MastraPlatformExporter;
22281
+ exports.MastraStorageExporter = MastraStorageExporter;
21430
22282
  exports.MetricsContextImpl = MetricsContextImpl;
21431
22283
  exports.ModelSpanTracker = ModelSpanTracker;
21432
22284
  exports.NoOpSpan = NoOpSpan;