@mastra/observability 1.0.0-beta.11 → 1.0.0-beta.13

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.js CHANGED
@@ -4161,6 +4161,8 @@ var observabilityRegistryConfigSchema = external_exports.object({
4161
4161
  var BaseExporter = class {
4162
4162
  /** Mastra logger instance */
4163
4163
  logger;
4164
+ /** Base configuration (accessible by subclasses) */
4165
+ baseConfig;
4164
4166
  /** Whether this exporter is disabled */
4165
4167
  #disabled = false;
4166
4168
  /** Public getter for disabled state */
@@ -4171,6 +4173,7 @@ var BaseExporter = class {
4171
4173
  * Initialize the base exporter with logger
4172
4174
  */
4173
4175
  constructor(config = {}) {
4176
+ this.baseConfig = config;
4174
4177
  const logLevel = this.resolveLogLevel(config.logLevel);
4175
4178
  this.logger = config.logger ?? new ConsoleLogger({ level: logLevel, name: this.constructor.name });
4176
4179
  }
@@ -4208,17 +4211,59 @@ var BaseExporter = class {
4208
4211
  this.#disabled = true;
4209
4212
  this.logger.warn(`${this.name} disabled: ${reason}`);
4210
4213
  }
4214
+ /**
4215
+ * Apply the customSpanFormatter if configured.
4216
+ * This is called automatically by exportTracingEvent before _exportTracingEvent.
4217
+ *
4218
+ * Supports both synchronous and asynchronous formatters. If the formatter
4219
+ * returns a Promise, it will be awaited.
4220
+ *
4221
+ * @param event - The incoming tracing event
4222
+ * @returns The (possibly modified) event to process
4223
+ */
4224
+ async applySpanFormatter(event) {
4225
+ if (this.baseConfig.customSpanFormatter) {
4226
+ try {
4227
+ const formattedSpan = await this.baseConfig.customSpanFormatter(event.exportedSpan);
4228
+ return {
4229
+ ...event,
4230
+ exportedSpan: formattedSpan
4231
+ };
4232
+ } catch (error) {
4233
+ this.logger.error(`${this.name}: Error in customSpanFormatter`, {
4234
+ error,
4235
+ spanId: event.exportedSpan.id,
4236
+ traceId: event.exportedSpan.traceId
4237
+ });
4238
+ }
4239
+ }
4240
+ return event;
4241
+ }
4211
4242
  /**
4212
4243
  * Export a tracing event
4213
4244
  *
4214
- * This method checks if the exporter is disabled before calling _exportEvent.
4215
- * Subclasses should implement _exportEvent instead of overriding this method.
4245
+ * This method checks if the exporter is disabled, applies the customSpanFormatter,
4246
+ * then calls _exportTracingEvent.
4247
+ * Subclasses should implement _exportTracingEvent instead of overriding this method.
4216
4248
  */
4217
4249
  async exportTracingEvent(event) {
4218
4250
  if (this.isDisabled) {
4219
4251
  return;
4220
4252
  }
4221
- await this._exportTracingEvent(event);
4253
+ const processedEvent = await this.applySpanFormatter(event);
4254
+ await this._exportTracingEvent(processedEvent);
4255
+ }
4256
+ /**
4257
+ * Force flush any buffered/queued spans without shutting down the exporter.
4258
+ *
4259
+ * This is useful in serverless environments where you need to ensure spans
4260
+ * are exported before the runtime instance is terminated, while keeping
4261
+ * the exporter active for future requests.
4262
+ *
4263
+ * Default implementation is a no-op. Override to add flush logic.
4264
+ */
4265
+ async flush() {
4266
+ this.logger.debug(`${this.name} flush called (no-op in base class)`);
4222
4267
  }
4223
4268
  /**
4224
4269
  * Shutdown the exporter and clean up resources
@@ -4890,6 +4935,10 @@ var TrackingExporter = class extends BaseExporter {
4890
4935
  /**
4891
4936
  * Hook called before processing each tracing event.
4892
4937
  * Override to transform or enrich the event before processing.
4938
+ *
4939
+ * Note: The customSpanFormatter is applied at the BaseExporter level before this hook.
4940
+ * Subclasses can override this to add additional pre-processing logic.
4941
+ *
4893
4942
  * @param event - The incoming tracing event
4894
4943
  * @returns The (possibly modified) event to process
4895
4944
  */
@@ -5103,8 +5152,31 @@ var TrackingExporter = class extends BaseExporter {
5103
5152
  return this.#traceMap.size;
5104
5153
  }
5105
5154
  // ============================================================================
5106
- // Shutdown Hooks (Override in subclass as needed)
5155
+ // Flush and Shutdown Hooks (Override in subclass as needed)
5107
5156
  // ============================================================================
5157
+ /**
5158
+ * Hook called by flush() to perform vendor-specific flush logic.
5159
+ * Override to send buffered data to the vendor's API.
5160
+ *
5161
+ * Unlike _postShutdown(), this method should NOT release resources,
5162
+ * as the exporter will continue to be used after flushing.
5163
+ */
5164
+ async _flush() {
5165
+ }
5166
+ /**
5167
+ * Force flush any buffered data without shutting down the exporter.
5168
+ * This is useful in serverless environments where you need to ensure spans
5169
+ * are exported before the runtime instance is terminated.
5170
+ *
5171
+ * Subclasses should override _flush() to implement vendor-specific flush logic.
5172
+ */
5173
+ async flush() {
5174
+ if (this.isDisabled) {
5175
+ return;
5176
+ }
5177
+ this.logger.debug(`${this.name}: Flushing`);
5178
+ await this._flush();
5179
+ }
5108
5180
  /**
5109
5181
  * Hook called at the start of shutdown, before cancelling timers and aborting spans.
5110
5182
  * Override to perform vendor-specific pre-shutdown tasks.
@@ -5162,9 +5234,20 @@ var TrackingExporter = class extends BaseExporter {
5162
5234
  await super.shutdown();
5163
5235
  }
5164
5236
  };
5237
+
5238
+ // src/exporters/span-formatters.ts
5239
+ function chainFormatters(formatters) {
5240
+ return async (span) => {
5241
+ let currentSpan = span;
5242
+ for (const formatter of formatters) {
5243
+ currentSpan = await formatter(currentSpan);
5244
+ }
5245
+ return currentSpan;
5246
+ };
5247
+ }
5165
5248
  var CloudExporter = class extends BaseExporter {
5166
5249
  name = "mastra-cloud-observability-exporter";
5167
- config;
5250
+ cloudConfig;
5168
5251
  buffer;
5169
5252
  flushTimer = null;
5170
5253
  constructor(config = {}) {
@@ -5174,7 +5257,7 @@ var CloudExporter = class extends BaseExporter {
5174
5257
  this.setDisabled("MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.");
5175
5258
  }
5176
5259
  const endpoint = config.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
5177
- this.config = {
5260
+ this.cloudConfig = {
5178
5261
  logger: this.logger,
5179
5262
  logLevel: config.logLevel ?? LogLevel.INFO,
5180
5263
  maxBatchSize: config.maxBatchSize ?? 1e3,
@@ -5232,12 +5315,12 @@ var CloudExporter = class extends BaseExporter {
5232
5315
  return spanRecord;
5233
5316
  }
5234
5317
  shouldFlush() {
5235
- if (this.buffer.totalSize >= this.config.maxBatchSize) {
5318
+ if (this.buffer.totalSize >= this.cloudConfig.maxBatchSize) {
5236
5319
  return true;
5237
5320
  }
5238
5321
  if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
5239
5322
  const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
5240
- if (elapsed >= this.config.maxBatchWaitMs) {
5323
+ if (elapsed >= this.cloudConfig.maxBatchWaitMs) {
5241
5324
  return true;
5242
5325
  }
5243
5326
  }
@@ -5260,9 +5343,9 @@ var CloudExporter = class extends BaseExporter {
5260
5343
  this.logger.trackException(mastraError);
5261
5344
  this.logger.error("Scheduled flush failed", mastraError);
5262
5345
  });
5263
- }, this.config.maxBatchWaitMs);
5346
+ }, this.cloudConfig.maxBatchWaitMs);
5264
5347
  }
5265
- async flush() {
5348
+ async flushBuffer() {
5266
5349
  if (this.flushTimer) {
5267
5350
  clearTimeout(this.flushTimer);
5268
5351
  this.flushTimer = null;
@@ -5272,7 +5355,7 @@ var CloudExporter = class extends BaseExporter {
5272
5355
  }
5273
5356
  const startTime = Date.now();
5274
5357
  const spansCopy = [...this.buffer.spans];
5275
- const flushReason = this.buffer.totalSize >= this.config.maxBatchSize ? "size" : "time";
5358
+ const flushReason = this.buffer.totalSize >= this.cloudConfig.maxBatchSize ? "size" : "time";
5276
5359
  this.resetBuffer();
5277
5360
  try {
5278
5361
  await this.batchUpload(spansCopy);
@@ -5303,7 +5386,7 @@ var CloudExporter = class extends BaseExporter {
5303
5386
  */
5304
5387
  async batchUpload(spans) {
5305
5388
  const headers = {
5306
- Authorization: `Bearer ${this.config.accessToken}`,
5389
+ Authorization: `Bearer ${this.cloudConfig.accessToken}`,
5307
5390
  "Content-Type": "application/json"
5308
5391
  };
5309
5392
  const options = {
@@ -5311,13 +5394,29 @@ var CloudExporter = class extends BaseExporter {
5311
5394
  headers,
5312
5395
  body: JSON.stringify({ spans })
5313
5396
  };
5314
- await fetchWithRetry(this.config.endpoint, options, this.config.maxRetries);
5397
+ await fetchWithRetry(this.cloudConfig.endpoint, options, this.cloudConfig.maxRetries);
5315
5398
  }
5316
5399
  resetBuffer() {
5317
5400
  this.buffer.spans = [];
5318
5401
  this.buffer.firstEventTime = void 0;
5319
5402
  this.buffer.totalSize = 0;
5320
5403
  }
5404
+ /**
5405
+ * Force flush any buffered spans without shutting down the exporter.
5406
+ * This is useful in serverless environments where you need to ensure spans
5407
+ * are exported before the runtime instance is terminated.
5408
+ */
5409
+ async flush() {
5410
+ if (this.isDisabled) {
5411
+ return;
5412
+ }
5413
+ if (this.buffer.totalSize > 0) {
5414
+ this.logger.debug("Flushing buffered events", {
5415
+ bufferedEvents: this.buffer.totalSize
5416
+ });
5417
+ await this.flushBuffer();
5418
+ }
5419
+ }
5321
5420
  async shutdown() {
5322
5421
  if (this.isDisabled) {
5323
5422
  return;
@@ -5326,27 +5425,22 @@ var CloudExporter = class extends BaseExporter {
5326
5425
  clearTimeout(this.flushTimer);
5327
5426
  this.flushTimer = null;
5328
5427
  }
5329
- if (this.buffer.totalSize > 0) {
5330
- this.logger.info("Flushing remaining events on shutdown", {
5331
- remainingEvents: this.buffer.totalSize
5332
- });
5333
- try {
5334
- await this.flush();
5335
- } catch (error) {
5336
- const mastraError = new MastraError(
5337
- {
5338
- id: `CLOUD_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
5339
- domain: ErrorDomain.MASTRA_OBSERVABILITY,
5340
- category: ErrorCategory.USER,
5341
- details: {
5342
- remainingEvents: this.buffer.totalSize
5343
- }
5344
- },
5345
- error
5346
- );
5347
- this.logger.trackException(mastraError);
5348
- this.logger.error("Failed to flush remaining events during shutdown", mastraError);
5349
- }
5428
+ try {
5429
+ await this.flush();
5430
+ } catch (error) {
5431
+ const mastraError = new MastraError(
5432
+ {
5433
+ id: `CLOUD_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
5434
+ domain: ErrorDomain.MASTRA_OBSERVABILITY,
5435
+ category: ErrorCategory.USER,
5436
+ details: {
5437
+ remainingEvents: this.buffer.totalSize
5438
+ }
5439
+ },
5440
+ error
5441
+ );
5442
+ this.logger.trackException(mastraError);
5443
+ this.logger.error("Failed to flush remaining events during shutdown", mastraError);
5350
5444
  }
5351
5445
  this.logger.info("CloudExporter shutdown complete");
5352
5446
  }
@@ -5650,7 +5744,7 @@ var DefaultExporter = class extends BaseExporter {
5650
5744
  clearTimeout(this.#flushTimer);
5651
5745
  }
5652
5746
  this.#flushTimer = setTimeout(() => {
5653
- this.flush().catch((error) => {
5747
+ this.flushBuffer().catch((error) => {
5654
5748
  this.logger.error("Scheduled flush failed", {
5655
5749
  error: error instanceof Error ? error.message : String(error)
5656
5750
  });
@@ -5784,7 +5878,7 @@ var DefaultExporter = class extends BaseExporter {
5784
5878
  handleBatchWithUpdatesEvent(event) {
5785
5879
  this.addToBuffer(event);
5786
5880
  if (this.shouldFlush()) {
5787
- this.flush().catch((error) => {
5881
+ this.flushBuffer().catch((error) => {
5788
5882
  this.logger.error("Batch flush failed", {
5789
5883
  error: error instanceof Error ? error.message : String(error)
5790
5884
  });
@@ -5800,7 +5894,7 @@ var DefaultExporter = class extends BaseExporter {
5800
5894
  if (event.type === TracingEventType.SPAN_ENDED) {
5801
5895
  this.addToBuffer(event);
5802
5896
  if (this.shouldFlush()) {
5803
- this.flush().catch((error) => {
5897
+ this.flushBuffer().catch((error) => {
5804
5898
  this.logger.error("Batch flush failed", {
5805
5899
  error: error instanceof Error ? error.message : String(error)
5806
5900
  });
@@ -5817,9 +5911,9 @@ var DefaultExporter = class extends BaseExporter {
5817
5911
  return this.#config.retryDelayMs * Math.pow(2, attempt);
5818
5912
  }
5819
5913
  /**
5820
- * Flushes the current buffer to storage with retry logic
5914
+ * Flushes the current buffer to storage with retry logic (internal implementation)
5821
5915
  */
5822
- async flush() {
5916
+ async flushBuffer() {
5823
5917
  if (!this.#observability) {
5824
5918
  this.logger.debug("Cannot flush traces. Observability storage is not initialized");
5825
5919
  return;
@@ -5926,23 +6020,25 @@ var DefaultExporter = class extends BaseExporter {
5926
6020
  break;
5927
6021
  }
5928
6022
  }
6023
+ /**
6024
+ * Force flush any buffered spans without shutting down the exporter.
6025
+ * This is useful in serverless environments where you need to ensure spans
6026
+ * are exported before the runtime instance is terminated.
6027
+ */
6028
+ async flush() {
6029
+ if (this.buffer.totalSize > 0) {
6030
+ this.logger.debug("Flushing buffered events", {
6031
+ bufferedEvents: this.buffer.totalSize
6032
+ });
6033
+ await this.flushBuffer();
6034
+ }
6035
+ }
5929
6036
  async shutdown() {
5930
6037
  if (this.#flushTimer) {
5931
6038
  clearTimeout(this.#flushTimer);
5932
6039
  this.#flushTimer = null;
5933
6040
  }
5934
- if (this.buffer.totalSize > 0) {
5935
- this.logger.info("Flushing remaining events on shutdown", {
5936
- remainingEvents: this.buffer.totalSize
5937
- });
5938
- try {
5939
- await this.flush();
5940
- } catch (error) {
5941
- this.logger.error("Failed to flush remaining events during shutdown", {
5942
- error: error instanceof Error ? error.message : String(error)
5943
- });
5944
- }
5945
- }
6041
+ await this.flush();
5946
6042
  this.logger.info("DefaultExporter shutdown complete");
5947
6043
  }
5948
6044
  };
@@ -7316,6 +7412,29 @@ var BaseObservabilityInstance = class extends MastraBase {
7316
7412
  this.logger.debug(`[Observability] Initialization started [name=${this.name}]`);
7317
7413
  this.logger.info(`[Observability] Initialized successfully [name=${this.name}]`);
7318
7414
  }
7415
+ /**
7416
+ * Force flush any buffered/queued spans from all exporters and the bridge
7417
+ * without shutting down the observability instance.
7418
+ *
7419
+ * This is useful in serverless environments (like Vercel's fluid compute) where
7420
+ * you need to ensure all spans are exported before the runtime instance is
7421
+ * terminated, while keeping the observability system active for future requests.
7422
+ */
7423
+ async flush() {
7424
+ this.logger.debug(`[Observability] Flush started [name=${this.name}]`);
7425
+ const flushPromises = [...this.exporters.map((e) => e.flush())];
7426
+ if (this.config.bridge) {
7427
+ flushPromises.push(this.config.bridge.flush());
7428
+ }
7429
+ const results = await Promise.allSettled(flushPromises);
7430
+ results.forEach((result, index) => {
7431
+ if (result.status === "rejected") {
7432
+ const targetName = index < this.exporters.length ? this.exporters[index]?.name : "bridge";
7433
+ this.logger.error(`[Observability] Flush error [target=${targetName}]`, result.reason);
7434
+ }
7435
+ });
7436
+ this.logger.debug(`[Observability] Flush completed [name=${this.name}]`);
7437
+ }
7319
7438
  /**
7320
7439
  * Shutdown Observability and clean up resources
7321
7440
  */
@@ -7715,6 +7834,6 @@ function buildTracingOptions(...updaters) {
7715
7834
  return updaters.reduce((opts, updater) => updater(opts), {});
7716
7835
  }
7717
7836
 
7718
- export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildTracingOptions, deepClean, getExternalParentId, mergeSerializationOptions, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema, serializationOptionsSchema, truncateString };
7837
+ export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildTracingOptions, chainFormatters, deepClean, getExternalParentId, mergeSerializationOptions, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema, serializationOptionsSchema, truncateString };
7719
7838
  //# sourceMappingURL=index.js.map
7720
7839
  //# sourceMappingURL=index.js.map