@mastra/observability 1.17.0 → 1.17.1-alpha.0

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
@@ -185,21 +185,15 @@ var BaseObservabilityEventBus = class BaseObservabilityEventBus extends MastraBa
185
185
  * }
186
186
  * ```
187
187
  */
188
- /**
189
- * Default keys to strip from objects during deep cleaning.
190
- * These are typically internal/sensitive fields that shouldn't be traced.
191
- */
192
- const DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
193
- "logger",
194
- "experimental_providerMetadata",
195
- "providerMetadata",
196
- "steps",
197
- "tracingContext",
198
- "execute",
199
- "validate"
200
- ]);
188
+ const FUNCTION_KEYS_TO_STRIP = /* @__PURE__ */ new Set(["execute", "validate"]);
189
+ const LOGGER_METHODS = [
190
+ "debug",
191
+ "info",
192
+ "warn",
193
+ "error"
194
+ ];
201
195
  const DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
202
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
196
+ keysToStrip: [],
203
197
  maxDepth: 8,
204
198
  maxStringLength: 128 * 1024,
205
199
  maxArrayLength: 50,
@@ -212,7 +206,7 @@ const DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
212
206
  function mergeSerializationOptions(userOptions) {
213
207
  if (!userOptions) return DEFAULT_DEEP_CLEAN_OPTIONS;
214
208
  return {
215
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
209
+ keysToStrip: DEFAULT_DEEP_CLEAN_OPTIONS.keysToStrip,
216
210
  maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
217
211
  maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
218
212
  maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
@@ -238,6 +232,47 @@ function getMapKeyType(key) {
238
232
  if (key instanceof Error) return "error";
239
233
  return typeof key;
240
234
  }
235
+ function hasOnlyKnownKeys(value, keys) {
236
+ try {
237
+ return Object.keys(value).every((key) => keys.includes(key));
238
+ } catch {
239
+ return false;
240
+ }
241
+ }
242
+ function isSpanLike(value) {
243
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
244
+ const span = value;
245
+ try {
246
+ return typeof span.id === "string" && typeof span.traceId === "string" && typeof span.type === "string" && typeof span.name === "string";
247
+ } catch {
248
+ return false;
249
+ }
250
+ }
251
+ function isTracingContextLike(value) {
252
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
253
+ const context = value;
254
+ if (!hasOnlyKnownKeys(context, ["currentSpan"])) return false;
255
+ try {
256
+ return context.currentSpan === void 0 || isSpanLike(context.currentSpan);
257
+ } catch {
258
+ return false;
259
+ }
260
+ }
261
+ function isLoggerLike(value) {
262
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
263
+ const logger = value;
264
+ try {
265
+ return LOGGER_METHODS.some((method) => typeof logger[method] === "function");
266
+ } catch {
267
+ return false;
268
+ }
269
+ }
270
+ function shouldStripEntry(key, value, stripSet) {
271
+ if (stripSet.has(key)) return true;
272
+ if (key === "tracingContext" && isTracingContextLike(value)) return true;
273
+ if (key === "logger") return typeof value === "function" || isLoggerLike(value);
274
+ return FUNCTION_KEYS_TO_STRIP.has(key) && typeof value === "function";
275
+ }
241
276
  function restoreSerializedMapKey(keyType, key) {
242
277
  switch (keyType) {
243
278
  case "undefined": return;
@@ -340,7 +375,7 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
340
375
  let mapKeyCount = 0;
341
376
  let omittedMapEntries = 0;
342
377
  for (const [mapKey, mapVal] of val) {
343
- if (typeof mapKey === "string" && stripSet.has(mapKey)) continue;
378
+ if (typeof mapKey === "string" && shouldStripEntry(mapKey, mapVal, stripSet)) continue;
344
379
  if (mapKeyCount >= maxObjectKeys) {
345
380
  omittedMapEntries++;
346
381
  continue;
@@ -416,15 +451,34 @@ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
416
451
  }
417
452
  if (looksLikeJsonSchema) return val;
418
453
  const cleaned = {};
419
- const keys = Object.keys(val).filter((key) => !stripSet.has(key));
454
+ let keys;
455
+ try {
456
+ keys = Object.keys(val);
457
+ } catch (error) {
458
+ return formatSerializationError(error);
459
+ }
420
460
  let keyCount = 0;
421
461
  for (const key of keys) {
462
+ if (stripSet.has(key)) continue;
463
+ let rawValue;
464
+ try {
465
+ rawValue = val[key];
466
+ } catch (error) {
467
+ if (keyCount >= maxObjectKeys) {
468
+ cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
469
+ break;
470
+ }
471
+ cleaned[key] = formatSerializationError(error);
472
+ keyCount++;
473
+ continue;
474
+ }
475
+ if (shouldStripEntry(key, rawValue, stripSet)) continue;
422
476
  if (keyCount >= maxObjectKeys) {
423
477
  cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
424
478
  break;
425
479
  }
426
480
  try {
427
- cleaned[key] = helper(val[key], depth + 1);
481
+ cleaned[key] = helper(rawValue, depth + 1);
428
482
  keyCount++;
429
483
  } catch (error) {
430
484
  cleaned[key] = formatSerializationError(error);
@@ -2437,6 +2491,49 @@ var ModelSpanTracker = class {
2437
2491
  }
2438
2492
  };
2439
2493
  //#endregion
2494
+ //#region src/spans/metadata.ts
2495
+ /**
2496
+ * Shared span-metadata helpers.
2497
+ *
2498
+ * Used by both the observability instance (`instances/base.ts`) and the span
2499
+ * base class (`spans/base.ts`) so the plain-record check and the
2500
+ * descriptor-preserving merge stay in a single place. Both feed the same span
2501
+ * metadata pipeline, so keeping one implementation avoids divergence.
2502
+ */
2503
+ /**
2504
+ * Returns true only for plain object records (prototype is `Object.prototype`
2505
+ * or `null`). Maps, Dates, class instances, and arrays return false so callers
2506
+ * can preserve their original shape instead of shallow-copying them into `{}`.
2507
+ */
2508
+ function isPlainRecord(value) {
2509
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2510
+ try {
2511
+ const prototype = Object.getPrototypeOf(value);
2512
+ return prototype === Object.prototype || prototype === null;
2513
+ } catch {
2514
+ return false;
2515
+ }
2516
+ }
2517
+ /**
2518
+ * Merges two metadata values while preserving property descriptors (so getters
2519
+ * are copied as accessors rather than eagerly invoked). Only plain records are
2520
+ * merged; if either side is non-plain the second argument is returned as-is,
2521
+ * matching the previous per-module behavior.
2522
+ */
2523
+ function mergeMetadata$1(parentMetadata, metadata) {
2524
+ if (!parentMetadata) return metadata;
2525
+ if (!metadata) return parentMetadata;
2526
+ if (!isPlainRecord(parentMetadata) || !isPlainRecord(metadata)) return metadata;
2527
+ try {
2528
+ const merged = {};
2529
+ Object.defineProperties(merged, Object.getOwnPropertyDescriptors(parentMetadata));
2530
+ Object.defineProperties(merged, Object.getOwnPropertyDescriptors(metadata));
2531
+ return merged;
2532
+ } catch {
2533
+ return metadata;
2534
+ }
2535
+ }
2536
+ //#endregion
2440
2537
  //#region src/spans/base.ts
2441
2538
  /**
2442
2539
  * Determines if a span type should be considered internal based on flags.
@@ -2561,10 +2658,7 @@ var BaseSpan = class {
2561
2658
  this.type = options.type;
2562
2659
  this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
2563
2660
  this.isExcluded = this.alwaysExcluded || observabilityConfig.excludeSpanTypes?.includes(this.type) === true || this.isInternal && !observabilityConfig.includeInternalSpans;
2564
- this.metadata = deepClean(options.parent?.metadata || options.metadata ? {
2565
- ...options.parent?.metadata,
2566
- ...options.metadata
2567
- } : void 0, this.deepCleanOptions);
2661
+ this.metadata = deepClean(this.prepareSpanMetadata(mergeMetadata$1(options.parent?.metadata, options.metadata)), this.deepCleanOptions);
2568
2662
  this.parent = options.parent;
2569
2663
  this.startTime = options.startTime ?? /* @__PURE__ */ new Date();
2570
2664
  this.observabilityInstance = observabilityInstance;
@@ -2582,9 +2676,32 @@ var BaseSpan = class {
2582
2676
  }
2583
2677
  this.attributes = deepClean(options.attributes, this.deepCleanOptions) || {};
2584
2678
  if (options.requestContext && options.requestContext.size() > 0) this.requestContext = deepClean(options.requestContext, this.deepCleanOptions);
2585
- if (this.isEvent) this.output = deepClean(options.output, this.deepCleanOptions);
2679
+ if (this.isEvent) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2586
2680
  else this.input = deepClean(options.input, this.deepCleanOptions);
2587
2681
  }
2682
+ prepareSpanOutput(value) {
2683
+ if (!isPlainRecord(value)) return value;
2684
+ if (this.type !== SpanType.MODEL_STEP && this.type !== SpanType.MODEL_INFERENCE) return value;
2685
+ try {
2686
+ const prepared = { ...value };
2687
+ delete prepared.steps;
2688
+ return prepared;
2689
+ } catch {
2690
+ return value;
2691
+ }
2692
+ }
2693
+ prepareSpanMetadata(value) {
2694
+ if (!isPlainRecord(value)) return value;
2695
+ if (this.type !== SpanType.MODEL_STEP) return value;
2696
+ try {
2697
+ const prepared = { ...value };
2698
+ delete prepared.providerMetadata;
2699
+ delete prepared.experimental_providerMetadata;
2700
+ return prepared;
2701
+ } catch {
2702
+ return value;
2703
+ }
2704
+ }
2588
2705
  createChildSpan(options) {
2589
2706
  return this.observabilityInstance.startSpan({
2590
2707
  ...options,
@@ -2791,10 +2908,10 @@ var DefaultSpan = class extends BaseSpan {
2791
2908
  this.endTime = /* @__PURE__ */ new Date();
2792
2909
  if (options?.metadata) this.metadata = {
2793
2910
  ...this.metadata,
2794
- ...deepClean(options.metadata, this.deepCleanOptions)
2911
+ ...deepClean(this.prepareSpanMetadata(options.metadata), this.deepCleanOptions)
2795
2912
  };
2796
2913
  if (this.isExcluded) return;
2797
- if (options?.output !== void 0) this.output = deepClean(options.output, this.deepCleanOptions);
2914
+ if (options?.output !== void 0) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2798
2915
  if (options?.attributes) this.attributes = {
2799
2916
  ...this.attributes,
2800
2917
  ...deepClean(options.attributes, this.deepCleanOptions)
@@ -2805,7 +2922,7 @@ var DefaultSpan = class extends BaseSpan {
2805
2922
  const { error, endSpan = true, attributes, metadata } = options;
2806
2923
  if (metadata) this.metadata = {
2807
2924
  ...this.metadata,
2808
- ...deepClean(metadata, this.deepCleanOptions)
2925
+ ...deepClean(this.prepareSpanMetadata(metadata), this.deepCleanOptions)
2809
2926
  };
2810
2927
  if (!this.isExcluded) {
2811
2928
  this.errorInfo = deepClean(error instanceof MastraError ? {
@@ -2834,11 +2951,11 @@ var DefaultSpan = class extends BaseSpan {
2834
2951
  if (options.name !== void 0) this.name = options.name;
2835
2952
  if (options.metadata) this.metadata = {
2836
2953
  ...this.metadata,
2837
- ...deepClean(options.metadata, this.deepCleanOptions)
2954
+ ...deepClean(this.prepareSpanMetadata(options.metadata), this.deepCleanOptions)
2838
2955
  };
2839
2956
  if (this.isExcluded) return;
2840
2957
  if (options.input !== void 0) this.input = deepClean(options.input, this.deepCleanOptions);
2841
- if (options.output !== void 0) this.output = deepClean(options.output, this.deepCleanOptions);
2958
+ if (options.output !== void 0) this.output = deepClean(this.prepareSpanOutput(options.output), this.deepCleanOptions);
2842
2959
  if (options.attributes) this.attributes = {
2843
2960
  ...this.attributes,
2844
2961
  ...deepClean(options.attributes, this.deepCleanOptions)
@@ -2926,6 +3043,19 @@ var NoOpSpan = class extends BaseSpan {
2926
3043
  /**
2927
3044
  * BaseObservability - Abstract base class for Observability implementations
2928
3045
  */
3046
+ function hasMetadataKey(metadata, key) {
3047
+ if (!metadata || typeof metadata !== "object") return false;
3048
+ try {
3049
+ return Object.prototype.hasOwnProperty.call(Object.getOwnPropertyDescriptors(metadata), key);
3050
+ } catch {
3051
+ return true;
3052
+ }
3053
+ }
3054
+ function injectEnvironmentMetadata(metadata, environment) {
3055
+ if (environment === void 0 || hasMetadataKey(metadata, "environment")) return metadata;
3056
+ if (metadata && !isPlainRecord(metadata)) return metadata;
3057
+ return mergeMetadata$1(metadata, { environment });
3058
+ }
2929
3059
  /**
2930
3060
  * Abstract base class for all Observability implementations in Mastra.
2931
3061
  */
@@ -3021,16 +3151,9 @@ var BaseObservabilityInstance = class extends MastraBase {
3021
3151
  let traceState;
3022
3152
  if (options.parent) traceState = options.parent.traceState;
3023
3153
  else traceState = this.computeTraceState(tracingOptions);
3024
- const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
3025
- const mergedMetadata = metadata || tracingMetadata ? {
3026
- ...metadata,
3027
- ...tracingMetadata
3028
- } : void 0;
3154
+ const mergedMetadata = mergeMetadata$1(metadata, !options.parent ? tracingOptions?.metadata : void 0);
3029
3155
  const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
3030
- const finalMetadata = !options.parent && this.#mastraEnvironment !== void 0 && (enrichedMetadata === void 0 || enrichedMetadata.environment === void 0) ? {
3031
- ...enrichedMetadata ?? {},
3032
- environment: this.#mastraEnvironment
3033
- } : enrichedMetadata;
3156
+ const finalMetadata = !options.parent ? injectEnvironmentMetadata(enrichedMetadata, this.#mastraEnvironment) : enrichedMetadata;
3034
3157
  const tags = !options.parent ? tracingOptions?.tags : void 0;
3035
3158
  const traceId = !options.parent ? options.traceId ?? tracingOptions?.traceId : options.traceId;
3036
3159
  const parentSpanId = options.parentSpanId;
@@ -3295,10 +3418,7 @@ var BaseObservabilityInstance = class extends MastraBase {
3295
3418
  if (!requestContext || !traceState || traceState.requestContextKeys.length === 0) return explicitMetadata;
3296
3419
  const extracted = this.extractKeys(requestContext, traceState.requestContextKeys);
3297
3420
  if (Object.keys(extracted).length === 0 && !explicitMetadata) return;
3298
- return {
3299
- ...extracted,
3300
- ...explicitMetadata
3301
- };
3421
+ return mergeMetadata$1(extracted, explicitMetadata);
3302
3422
  }
3303
3423
  /**
3304
3424
  * Extract specific keys from RequestContext
@@ -6345,6 +6465,8 @@ const QUOTA_EXCEEDED_STATUS = 402;
6345
6465
  const OBSERVABILITY_STATUS_HEADER = "x-mastra-observability";
6346
6466
  const OBSERVABILITY_DISABLED_VALUE = "disabled";
6347
6467
  const OBSERVABILITY_RETRY_AFTER_HEADER = "x-mastra-observability-retry-after";
6468
+ const OBSERVABILITY_CAPABILITIES_HEADER = "x-mastra-observability-capabilities";
6469
+ const QUOTA_PAUSE_CAPABILITY = "quota-pause-v1";
6348
6470
  const DEFAULT_QUOTA_PROBE_INTERVAL_SECONDS = 300;
6349
6471
  const MAX_QUOTA_PROBE_INTERVAL_SECONDS = Math.floor(2147483647 / 1e3);
6350
6472
  function isObservabilityDisabled(response) {
@@ -6671,7 +6793,8 @@ var MastraPlatformExporter = class extends BaseExporter {
6671
6793
  buildPublishHeaders() {
6672
6794
  return {
6673
6795
  Authorization: `Bearer ${this.platformConfig.accessToken}`,
6674
- "Content-Type": "application/json"
6796
+ "Content-Type": "application/json",
6797
+ [OBSERVABILITY_CAPABILITIES_HEADER]: QUOTA_PAUSE_CAPABILITY
6675
6798
  };
6676
6799
  }
6677
6800
  buildPublishBody(signal, records) {
@@ -6724,7 +6847,7 @@ var MastraPlatformExporter = class extends BaseExporter {
6724
6847
  this.flushTimer = null;
6725
6848
  }
6726
6849
  this.resetBuffer();
6727
- this.logger.warn(`Mastra observability paused: quota exhausted, dropping telemetry and probing every ${retryAfterSeconds}s`);
6850
+ this.logger.warn(`Mastra observability export paused: platform quota exhausted (OBSERVABILITY_QUOTA_EXCEEDED). Dropping telemetry and retrying in ${retryAfterSeconds} seconds. Check Platform billing/usage to restore telemetry.`);
6728
6851
  this.scheduleQuotaProbe();
6729
6852
  }
6730
6853
  scheduleQuotaProbe() {
@@ -9299,6 +9422,6 @@ function buildTracingOptions(...updaters) {
9299
9422
  return updaters.reduce((opts, updater) => updater(opts), {});
9300
9423
  }
9301
9424
  //#endregion
9302
- export { BaseExporter, BaseObservabilityEventBus, BaseObservabilityInstance, BaseSpan, CardinalityFilter, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DEFAULT_LIMITS, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, JsonExporter, LoggerContextImpl, MastraPlatformExporter, MastraStorageExporter, MetricsContextImpl, ModelSpanTracker, NoOpSpan, Observability, ObservabilityBus, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildExportedLog, buildExportedSpan, buildTracingOptions, chainFormatters, createClientObservabilityProxy, decodeResourceLogs, decodeResourceSpans, deepClean, formatBaggage, formatTraceparent, getExternalParentId, isSerializedMap, mergeSerializationOptions, observabilityConfigValueSchema, observabilityFeatures, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, otlpSeverityToLogLevel, parseBaggage, parseTraceparent, reconstructSerializedMap, routeToHandler, samplingStrategySchema, serializationOptionsSchema, truncateString };
9425
+ export { BaseExporter, BaseObservabilityEventBus, BaseObservabilityInstance, BaseSpan, CardinalityFilter, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_LIMITS, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, JsonExporter, LoggerContextImpl, MastraPlatformExporter, MastraStorageExporter, MetricsContextImpl, ModelSpanTracker, NoOpSpan, OBSERVABILITY_CAPABILITIES_HEADER, Observability, ObservabilityBus, QUOTA_PAUSE_CAPABILITY, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildExportedLog, buildExportedSpan, buildTracingOptions, chainFormatters, createClientObservabilityProxy, decodeResourceLogs, decodeResourceSpans, deepClean, formatBaggage, formatTraceparent, getExternalParentId, isSerializedMap, mergeSerializationOptions, observabilityConfigValueSchema, observabilityFeatures, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, otlpSeverityToLogLevel, parseBaggage, parseTraceparent, reconstructSerializedMap, routeToHandler, samplingStrategySchema, serializationOptionsSchema, truncateString };
9303
9426
 
9304
9427
  //# sourceMappingURL=index.js.map