@mastra/observability 1.7.3 → 1.8.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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { MastraBase } from '@mastra/core/base';
2
2
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
3
3
  import { ConsoleLogger, LogLevel, RegisteredLogger } from '@mastra/core/logger';
4
- import { TracingEventType, DEFAULT_BLOCKED_LABELS, SpanType, noOpLoggerContext, InternalSpans } from '@mastra/core/observability';
4
+ import { SpanType, TracingEventType, DEFAULT_BLOCKED_LABELS, noOpLoggerContext, InternalSpans } from '@mastra/core/observability';
5
5
  import { fetchWithRetry, getNestedValue, setNestedValue } from '@mastra/core/utils';
6
6
  import { buildUpdateSpanRecord, buildFeedbackRecord, buildLogRecord, buildMetricRecord, buildScoreRecord, buildCreateSpanRecord } from '@mastra/core/storage';
7
7
  import fs from 'fs';
@@ -13847,17 +13847,36 @@ var serializationOptionsSchema = external_exports.object({
13847
13847
  maxArrayLength: external_exports.number().int().positive().optional(),
13848
13848
  maxObjectKeys: external_exports.number().int().positive().optional()
13849
13849
  }).optional();
13850
- var observabilityInstanceConfigSchema = external_exports.object({
13851
- name: external_exports.string().min(1, "Name is required"),
13850
+ var LOG_LEVELS = ["debug", "info", "warn", "error", "fatal"];
13851
+ var cardinalityConfigSchema = external_exports.object({
13852
+ blockedLabels: external_exports.array(external_exports.string()).optional(),
13853
+ blockUUIDs: external_exports.boolean().optional()
13854
+ }).optional();
13855
+ var loggingConfigSchema = external_exports.object({
13856
+ enabled: external_exports.boolean().optional(),
13857
+ level: external_exports.enum(LOG_LEVELS).optional()
13858
+ }).optional();
13859
+ var spanFilterSchema = external_exports.function({
13860
+ input: external_exports.tuple([external_exports.any()]),
13861
+ output: external_exports.boolean()
13862
+ }).optional();
13863
+ var observabilityInstanceConfigFields = {
13852
13864
  serviceName: external_exports.string().min(1, "Service name is required"),
13853
13865
  sampling: samplingStrategySchema.optional(),
13854
13866
  exporters: external_exports.array(external_exports.any()).optional(),
13855
13867
  bridge: external_exports.any().optional(),
13856
13868
  spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
13857
13869
  includeInternalSpans: external_exports.boolean().optional(),
13870
+ excludeSpanTypes: external_exports.array(external_exports.nativeEnum(SpanType)).optional(),
13871
+ spanFilter: spanFilterSchema,
13858
13872
  requestContextKeys: external_exports.array(external_exports.string()).optional(),
13859
13873
  serializationOptions: serializationOptionsSchema,
13860
- cardinality: external_exports.any().optional()
13874
+ cardinality: cardinalityConfigSchema,
13875
+ logging: loggingConfigSchema
13876
+ };
13877
+ var observabilityInstanceConfigSchema = external_exports.object({
13878
+ name: external_exports.string().min(1, "Name is required"),
13879
+ ...observabilityInstanceConfigFields
13861
13880
  }).refine(
13862
13881
  (data) => {
13863
13882
  const hasExporters = data.exporters && data.exporters.length > 0;
@@ -13868,16 +13887,7 @@ var observabilityInstanceConfigSchema = external_exports.object({
13868
13887
  message: "At least one exporter or a bridge is required"
13869
13888
  }
13870
13889
  );
13871
- var observabilityConfigValueSchema = external_exports.object({
13872
- serviceName: external_exports.string().min(1, "Service name is required"),
13873
- sampling: samplingStrategySchema.optional(),
13874
- exporters: external_exports.array(external_exports.any()).optional(),
13875
- bridge: external_exports.any().optional(),
13876
- spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
13877
- includeInternalSpans: external_exports.boolean().optional(),
13878
- requestContextKeys: external_exports.array(external_exports.string()).optional(),
13879
- serializationOptions: serializationOptionsSchema
13880
- }).refine(
13890
+ var observabilityConfigValueSchema = external_exports.object(observabilityInstanceConfigFields).refine(
13881
13891
  (data) => {
13882
13892
  const hasExporters = data.exporters && data.exporters.length > 0;
13883
13893
  const hasBridge = !!data.bridge;
@@ -15026,18 +15036,144 @@ function chainFormatters(formatters) {
15026
15036
  return currentSpan;
15027
15037
  };
15028
15038
  }
15039
+ var SIGNAL_PUBLISH_SUFFIXES = {
15040
+ traces: "/spans/publish",
15041
+ logs: "/logs/publish",
15042
+ metrics: "/metrics/publish",
15043
+ scores: "/scores/publish",
15044
+ feedback: "/feedback/publish"
15045
+ };
15046
+ var SIGNAL_PUBLISH_PATHS = {
15047
+ traces: "/ai/spans/publish",
15048
+ logs: "/ai/logs/publish",
15049
+ metrics: "/ai/metrics/publish",
15050
+ scores: "/ai/scores/publish",
15051
+ feedback: "/ai/feedback/publish"
15052
+ };
15053
+ var SIGNAL_PAYLOAD_KEYS = {
15054
+ traces: "spans",
15055
+ logs: "logs",
15056
+ metrics: "metrics",
15057
+ scores: "scores",
15058
+ feedback: "feedback"
15059
+ };
15060
+ var DEFAULT_CLOUD_ENDPOINT = "https://api.mastra.ai";
15061
+ function trimTrailingSlashes(value) {
15062
+ let end = value.length;
15063
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
15064
+ end--;
15065
+ }
15066
+ return end === value.length ? value : value.slice(0, end);
15067
+ }
15068
+ function createInvalidEndpointError(endpoint, text, cause) {
15069
+ return new MastraError(
15070
+ {
15071
+ id: `CLOUD_EXPORTER_INVALID_ENDPOINT`,
15072
+ text,
15073
+ domain: ErrorDomain.MASTRA_OBSERVABILITY,
15074
+ category: ErrorCategory.USER,
15075
+ details: {
15076
+ endpoint
15077
+ }
15078
+ },
15079
+ cause
15080
+ );
15081
+ }
15082
+ function resolveBaseEndpoint(baseEndpoint) {
15083
+ const normalizedEndpoint = trimTrailingSlashes(baseEndpoint);
15084
+ const invalidText = 'CloudExporter endpoint must be a base origin like "https://collector.example.com" with no path, search, or hash.';
15085
+ try {
15086
+ const parsedEndpoint = new URL(normalizedEndpoint);
15087
+ if (parsedEndpoint.pathname !== "/" || parsedEndpoint.search || parsedEndpoint.hash) {
15088
+ throw createInvalidEndpointError(baseEndpoint, invalidText);
15089
+ }
15090
+ return trimTrailingSlashes(parsedEndpoint.origin);
15091
+ } catch (error48) {
15092
+ if (error48 instanceof MastraError) {
15093
+ throw error48;
15094
+ }
15095
+ throw createInvalidEndpointError(baseEndpoint, invalidText, error48);
15096
+ }
15097
+ }
15098
+ function buildSignalEndpoint(baseEndpoint, signal) {
15099
+ return `${baseEndpoint}${SIGNAL_PUBLISH_PATHS[signal]}`;
15100
+ }
15101
+ function resolveExplicitSignalEndpoint(signal, endpoint) {
15102
+ const normalizedEndpoint = trimTrailingSlashes(endpoint);
15103
+ const invalidText = `CloudExporter ${signal}Endpoint must be a base origin like "https://collector.example.com" or a full ${signal} publish URL ending in "${SIGNAL_PUBLISH_SUFFIXES[signal]}".`;
15104
+ try {
15105
+ const parsedEndpoint = new URL(normalizedEndpoint);
15106
+ if (parsedEndpoint.search || parsedEndpoint.hash) {
15107
+ throw createInvalidEndpointError(endpoint, invalidText);
15108
+ }
15109
+ const normalizedOrigin = trimTrailingSlashes(parsedEndpoint.origin);
15110
+ const normalizedPathname = trimTrailingSlashes(parsedEndpoint.pathname);
15111
+ if (!normalizedPathname || normalizedPathname === "/") {
15112
+ return buildSignalEndpoint(normalizedOrigin, signal);
15113
+ }
15114
+ if (normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES[signal])) {
15115
+ return `${normalizedOrigin}${normalizedPathname}`;
15116
+ }
15117
+ throw createInvalidEndpointError(endpoint, invalidText);
15118
+ } catch (error48) {
15119
+ if (error48 instanceof MastraError) {
15120
+ throw error48;
15121
+ }
15122
+ throw createInvalidEndpointError(endpoint, invalidText, error48);
15123
+ }
15124
+ }
15125
+ function deriveSignalEndpointFromTracesEndpoint(signal, tracesEndpoint) {
15126
+ if (signal === "traces") {
15127
+ return tracesEndpoint;
15128
+ }
15129
+ const normalizedTracesEndpoint = trimTrailingSlashes(tracesEndpoint);
15130
+ const invalidText = 'CloudExporter tracesEndpoint must be a base origin like "https://collector.example.com" or a full traces publish URL ending in "/spans/publish".';
15131
+ try {
15132
+ const parsedEndpoint = new URL(normalizedTracesEndpoint);
15133
+ const normalizedOrigin = trimTrailingSlashes(parsedEndpoint.origin);
15134
+ const normalizedPathname = trimTrailingSlashes(parsedEndpoint.pathname);
15135
+ if (!normalizedPathname.endsWith(SIGNAL_PUBLISH_SUFFIXES.traces)) {
15136
+ throw createInvalidEndpointError(tracesEndpoint, invalidText);
15137
+ }
15138
+ const basePath = normalizedPathname.slice(0, -SIGNAL_PUBLISH_SUFFIXES.traces.length);
15139
+ return `${normalizedOrigin}${basePath}${SIGNAL_PUBLISH_SUFFIXES[signal]}`;
15140
+ } catch (error48) {
15141
+ if (error48 instanceof MastraError) {
15142
+ throw error48;
15143
+ }
15144
+ throw createInvalidEndpointError(tracesEndpoint, invalidText, error48);
15145
+ }
15146
+ }
15029
15147
  var CloudExporter = class extends BaseExporter {
15030
15148
  name = "mastra-cloud-observability-exporter";
15031
15149
  cloudConfig;
15032
15150
  buffer;
15033
15151
  flushTimer = null;
15152
+ inFlightFlushes = /* @__PURE__ */ new Set();
15034
15153
  constructor(config2 = {}) {
15035
15154
  super(config2);
15036
15155
  const accessToken = config2.accessToken ?? process.env.MASTRA_CLOUD_ACCESS_TOKEN;
15037
15156
  if (!accessToken) {
15038
15157
  this.setDisabled("MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.");
15039
15158
  }
15040
- const endpoint = config2.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
15159
+ const tracesEndpointOverride = config2.tracesEndpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT;
15160
+ let baseEndpoint;
15161
+ let tracesEndpoint;
15162
+ if (tracesEndpointOverride) {
15163
+ tracesEndpoint = resolveExplicitSignalEndpoint("traces", tracesEndpointOverride);
15164
+ } else {
15165
+ baseEndpoint = resolveBaseEndpoint(config2.endpoint ?? DEFAULT_CLOUD_ENDPOINT);
15166
+ tracesEndpoint = buildSignalEndpoint(baseEndpoint, "traces");
15167
+ }
15168
+ const resolveConfiguredSignalEndpoint = (signal, explicitEndpoint) => {
15169
+ if (explicitEndpoint) {
15170
+ return resolveExplicitSignalEndpoint(signal, explicitEndpoint);
15171
+ }
15172
+ if (tracesEndpointOverride) {
15173
+ return deriveSignalEndpointFromTracesEndpoint(signal, tracesEndpoint);
15174
+ }
15175
+ return buildSignalEndpoint(baseEndpoint, signal);
15176
+ };
15041
15177
  this.cloudConfig = {
15042
15178
  logger: this.logger,
15043
15179
  logLevel: config2.logLevel ?? LogLevel.INFO,
@@ -15045,10 +15181,18 @@ var CloudExporter = class extends BaseExporter {
15045
15181
  maxBatchWaitMs: config2.maxBatchWaitMs ?? 5e3,
15046
15182
  maxRetries: config2.maxRetries ?? 3,
15047
15183
  accessToken: accessToken || "",
15048
- endpoint
15184
+ tracesEndpoint,
15185
+ logsEndpoint: resolveConfiguredSignalEndpoint("logs", config2.logsEndpoint),
15186
+ metricsEndpoint: resolveConfiguredSignalEndpoint("metrics", config2.metricsEndpoint),
15187
+ scoresEndpoint: resolveConfiguredSignalEndpoint("scores", config2.scoresEndpoint),
15188
+ feedbackEndpoint: resolveConfiguredSignalEndpoint("feedback", config2.feedbackEndpoint)
15049
15189
  };
15050
15190
  this.buffer = {
15051
15191
  spans: [],
15192
+ logs: [],
15193
+ metrics: [],
15194
+ scores: [],
15195
+ feedback: [],
15052
15196
  totalSize: 0
15053
15197
  };
15054
15198
  }
@@ -15057,45 +15201,111 @@ var CloudExporter = class extends BaseExporter {
15057
15201
  return;
15058
15202
  }
15059
15203
  this.addToBuffer(event);
15060
- if (this.shouldFlush()) {
15061
- this.flush().catch((error48) => {
15062
- this.logger.error("Batch flush failed", {
15063
- error: error48 instanceof Error ? error48.message : String(error48)
15064
- });
15065
- });
15066
- } else if (this.buffer.totalSize === 1) {
15067
- this.scheduleFlush();
15204
+ await this.handleBufferedEvent();
15205
+ }
15206
+ async onLogEvent(event) {
15207
+ if (this.isDisabled) {
15208
+ return;
15068
15209
  }
15210
+ this.addLogToBuffer(event);
15211
+ await this.handleBufferedEvent();
15069
15212
  }
15070
- addToBuffer(event) {
15071
- if (this.buffer.totalSize === 0) {
15072
- this.buffer.firstEventTime = /* @__PURE__ */ new Date();
15213
+ async onMetricEvent(event) {
15214
+ if (this.isDisabled) {
15215
+ return;
15216
+ }
15217
+ this.addMetricToBuffer(event);
15218
+ await this.handleBufferedEvent();
15219
+ }
15220
+ async onScoreEvent(event) {
15221
+ if (this.isDisabled) {
15222
+ return;
15223
+ }
15224
+ this.addScoreToBuffer(event);
15225
+ await this.handleBufferedEvent();
15226
+ }
15227
+ async onFeedbackEvent(event) {
15228
+ if (this.isDisabled) {
15229
+ return;
15073
15230
  }
15231
+ this.addFeedbackToBuffer(event);
15232
+ await this.handleBufferedEvent();
15233
+ }
15234
+ addToBuffer(event) {
15235
+ this.markBufferStart();
15074
15236
  const spanRecord = this.formatSpan(event.exportedSpan);
15075
15237
  this.buffer.spans.push(spanRecord);
15076
15238
  this.buffer.totalSize++;
15077
15239
  }
15240
+ addLogToBuffer(event) {
15241
+ this.markBufferStart();
15242
+ this.buffer.logs.push(this.formatLog(event.log));
15243
+ this.buffer.totalSize++;
15244
+ }
15245
+ addMetricToBuffer(event) {
15246
+ this.markBufferStart();
15247
+ this.buffer.metrics.push(this.formatMetric(event.metric));
15248
+ this.buffer.totalSize++;
15249
+ }
15250
+ addScoreToBuffer(event) {
15251
+ this.markBufferStart();
15252
+ this.buffer.scores.push(this.formatScore(event.score));
15253
+ this.buffer.totalSize++;
15254
+ }
15255
+ addFeedbackToBuffer(event) {
15256
+ this.markBufferStart();
15257
+ this.buffer.feedback.push(this.formatFeedback(event.feedback));
15258
+ this.buffer.totalSize++;
15259
+ }
15260
+ markBufferStart() {
15261
+ if (this.buffer.totalSize === 0) {
15262
+ this.buffer.firstEventTime = /* @__PURE__ */ new Date();
15263
+ }
15264
+ }
15078
15265
  formatSpan(span) {
15079
15266
  const spanRecord = {
15080
- traceId: span.traceId,
15267
+ ...span,
15081
15268
  spanId: span.id,
15082
- parentSpanId: span.parentSpanId ?? null,
15083
- name: span.name,
15084
15269
  spanType: span.type,
15085
- attributes: span.attributes ?? null,
15086
- metadata: span.metadata ?? null,
15087
- requestContext: span.requestContext ?? null,
15088
15270
  startedAt: span.startTime,
15089
15271
  endedAt: span.endTime ?? null,
15090
- input: span.input ?? null,
15091
- output: span.output ?? null,
15092
- error: span.errorInfo,
15093
- isEvent: span.isEvent,
15272
+ error: span.errorInfo ?? null,
15094
15273
  createdAt: /* @__PURE__ */ new Date(),
15095
15274
  updatedAt: null
15096
15275
  };
15097
15276
  return spanRecord;
15098
15277
  }
15278
+ formatLog(log) {
15279
+ return {
15280
+ ...log
15281
+ };
15282
+ }
15283
+ formatMetric(metric) {
15284
+ return {
15285
+ ...metric
15286
+ };
15287
+ }
15288
+ formatScore(score) {
15289
+ return {
15290
+ ...score
15291
+ };
15292
+ }
15293
+ formatFeedback(feedback) {
15294
+ return {
15295
+ ...feedback
15296
+ };
15297
+ }
15298
+ async handleBufferedEvent() {
15299
+ if (this.shouldFlush()) {
15300
+ void this.flush().catch((error48) => {
15301
+ this.logger.error("Batch flush failed", {
15302
+ error: error48 instanceof Error ? error48.message : String(error48)
15303
+ });
15304
+ });
15305
+ } else if (this.buffer.totalSize === 1) {
15306
+ this.scheduleFlush();
15307
+ }
15308
+ }
15099
15309
  shouldFlush() {
15100
15310
  if (this.buffer.totalSize >= this.cloudConfig.maxBatchSize) {
15101
15311
  return true;
@@ -15113,7 +15323,7 @@ var CloudExporter = class extends BaseExporter {
15113
15323
  clearTimeout(this.flushTimer);
15114
15324
  }
15115
15325
  this.flushTimer = setTimeout(() => {
15116
- this.flush().catch((error48) => {
15326
+ void this.flush().catch((error48) => {
15117
15327
  const mastraError = new MastraError(
15118
15328
  {
15119
15329
  id: `CLOUD_EXPORTER_FAILED_TO_SCHEDULE_FLUSH`,
@@ -15137,49 +15347,91 @@ var CloudExporter = class extends BaseExporter {
15137
15347
  }
15138
15348
  const startTime = Date.now();
15139
15349
  const spansCopy = [...this.buffer.spans];
15350
+ const logsCopy = [...this.buffer.logs];
15351
+ const metricsCopy = [...this.buffer.metrics];
15352
+ const scoresCopy = [...this.buffer.scores];
15353
+ const feedbackCopy = [...this.buffer.feedback];
15354
+ const batchSize = this.buffer.totalSize;
15140
15355
  const flushReason = this.buffer.totalSize >= this.cloudConfig.maxBatchSize ? "size" : "time";
15141
15356
  this.resetBuffer();
15142
- try {
15143
- await this.batchUpload(spansCopy);
15144
- const elapsed = Date.now() - startTime;
15357
+ const results = await Promise.all([
15358
+ this.flushSignalBatch("traces", spansCopy),
15359
+ this.flushSignalBatch("logs", logsCopy),
15360
+ this.flushSignalBatch("metrics", metricsCopy),
15361
+ this.flushSignalBatch("scores", scoresCopy),
15362
+ this.flushSignalBatch("feedback", feedbackCopy)
15363
+ ]);
15364
+ const failedSignals = results.filter((result) => !result.succeeded).map((result) => result.signal);
15365
+ const elapsed = Date.now() - startTime;
15366
+ if (failedSignals.length === 0) {
15145
15367
  this.logger.debug("Batch flushed successfully", {
15146
- batchSize: spansCopy.length,
15368
+ batchSize,
15147
15369
  flushReason,
15148
15370
  durationMs: elapsed
15149
15371
  });
15372
+ return;
15373
+ }
15374
+ this.logger.warn("Batch flush completed with dropped signal batches", {
15375
+ batchSize,
15376
+ flushReason,
15377
+ durationMs: elapsed,
15378
+ failedSignals
15379
+ });
15380
+ }
15381
+ /**
15382
+ * Uploads a signal batch to the configured cloud API using fetchWithRetry.
15383
+ */
15384
+ async batchUpload(signal, records) {
15385
+ const headers = {
15386
+ Authorization: `Bearer ${this.cloudConfig.accessToken}`,
15387
+ "Content-Type": "application/json"
15388
+ };
15389
+ const endpointMap = {
15390
+ traces: this.cloudConfig.tracesEndpoint,
15391
+ logs: this.cloudConfig.logsEndpoint,
15392
+ metrics: this.cloudConfig.metricsEndpoint,
15393
+ scores: this.cloudConfig.scoresEndpoint,
15394
+ feedback: this.cloudConfig.feedbackEndpoint
15395
+ };
15396
+ const options = {
15397
+ method: "POST",
15398
+ headers,
15399
+ body: JSON.stringify({ [SIGNAL_PAYLOAD_KEYS[signal]]: records })
15400
+ };
15401
+ await fetchWithRetry(endpointMap[signal], options, this.cloudConfig.maxRetries);
15402
+ }
15403
+ async flushSignalBatch(signal, records) {
15404
+ if (records.length === 0) {
15405
+ return { signal, succeeded: true };
15406
+ }
15407
+ try {
15408
+ await this.batchUpload(signal, records);
15409
+ return { signal, succeeded: true };
15150
15410
  } catch (error48) {
15411
+ const errorId = `CLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD_${signal.toUpperCase()}`;
15151
15412
  const mastraError = new MastraError(
15152
15413
  {
15153
- id: `CLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD`,
15414
+ id: errorId,
15154
15415
  domain: ErrorDomain.MASTRA_OBSERVABILITY,
15155
15416
  category: ErrorCategory.USER,
15156
15417
  details: {
15157
- droppedBatchSize: spansCopy.length
15418
+ signal,
15419
+ droppedBatchSize: records.length
15158
15420
  }
15159
15421
  },
15160
15422
  error48
15161
15423
  );
15162
15424
  this.logger.trackException(mastraError);
15163
15425
  this.logger.error("Batch upload failed after all retries, dropping batch", mastraError);
15426
+ return { signal, succeeded: false };
15164
15427
  }
15165
15428
  }
15166
- /**
15167
- * Uploads spans to cloud API using fetchWithRetry for all retry logic
15168
- */
15169
- async batchUpload(spans) {
15170
- const headers = {
15171
- Authorization: `Bearer ${this.cloudConfig.accessToken}`,
15172
- "Content-Type": "application/json"
15173
- };
15174
- const options = {
15175
- method: "POST",
15176
- headers,
15177
- body: JSON.stringify({ spans })
15178
- };
15179
- await fetchWithRetry(this.cloudConfig.endpoint, options, this.cloudConfig.maxRetries);
15180
- }
15181
15429
  resetBuffer() {
15182
15430
  this.buffer.spans = [];
15431
+ this.buffer.logs = [];
15432
+ this.buffer.metrics = [];
15433
+ this.buffer.scores = [];
15434
+ this.buffer.feedback = [];
15183
15435
  this.buffer.firstEventTime = void 0;
15184
15436
  this.buffer.totalSize = 0;
15185
15437
  }
@@ -15192,11 +15444,21 @@ var CloudExporter = class extends BaseExporter {
15192
15444
  if (this.isDisabled) {
15193
15445
  return;
15194
15446
  }
15195
- if (this.buffer.totalSize > 0) {
15196
- this.logger.debug("Flushing buffered events", {
15197
- bufferedEvents: this.buffer.totalSize
15198
- });
15199
- await this.flushBuffer();
15447
+ while (this.buffer.totalSize > 0 || this.inFlightFlushes.size > 0) {
15448
+ if (this.buffer.totalSize > 0) {
15449
+ this.logger.debug("Flushing buffered events", {
15450
+ bufferedEvents: this.buffer.totalSize
15451
+ });
15452
+ const flushPromise = this.flushBuffer();
15453
+ this.inFlightFlushes.add(flushPromise);
15454
+ try {
15455
+ await flushPromise;
15456
+ } finally {
15457
+ this.inFlightFlushes.delete(flushPromise);
15458
+ }
15459
+ continue;
15460
+ }
15461
+ await Promise.allSettled([...this.inFlightFlushes]);
15200
15462
  }
15201
15463
  }
15202
15464
  async shutdown() {
@@ -16434,6 +16696,9 @@ var TestExporter = class extends BaseExporter {
16434
16696
  if (value instanceof Date) {
16435
16697
  return "<date>";
16436
16698
  }
16699
+ if (key === "createdAt" && typeof value === "number") {
16700
+ return "<date>";
16701
+ }
16437
16702
  if (typeof value === "string") {
16438
16703
  if (key === "traceId" && (uuidRegex.test(value) || hexId32Regex.test(value))) {
16439
16704
  if (!traceIdMap.has(value)) {
@@ -16462,7 +16727,23 @@ var TestExporter = class extends BaseExporter {
16462
16727
  if (value && typeof value === "object") {
16463
16728
  const normalized = {};
16464
16729
  for (const [k, v] of Object.entries(value)) {
16465
- normalized[k] = normalizeValue(v, k);
16730
+ if (key === "providerOptions" && k === "mastra" && v && typeof v === "object") {
16731
+ const mastraOptions = v;
16732
+ const remainingMastraOptions = Object.fromEntries(
16733
+ Object.entries(mastraOptions).filter(([mastraKey]) => mastraKey !== "createdAt")
16734
+ );
16735
+ if (Object.keys(remainingMastraOptions).length > 0) {
16736
+ normalized[k] = normalizeValue(remainingMastraOptions, k);
16737
+ }
16738
+ continue;
16739
+ }
16740
+ const normalizedValue = normalizeValue(v, k);
16741
+ if (normalizedValue !== void 0) {
16742
+ normalized[k] = normalizedValue;
16743
+ }
16744
+ }
16745
+ if (key === "providerOptions" && Object.keys(normalized).length === 0) {
16746
+ return void 0;
16466
16747
  }
16467
16748
  return normalized;
16468
16749
  }
@@ -17036,69 +17317,436 @@ var BaseObservabilityEventBus = class _BaseObservabilityEventBus extends MastraB
17036
17317
  }
17037
17318
  };
17038
17319
 
17039
- // src/bus/observability-bus.ts
17040
- var MAX_FLUSH_ITERATIONS = 3;
17041
- var ObservabilityBus = class extends BaseObservabilityEventBus {
17042
- exporters = [];
17043
- bridge;
17044
- /** In-flight handler promises from routeToHandler. Self-cleaning via .finally(). */
17045
- pendingHandlers = /* @__PURE__ */ new Set();
17046
- constructor() {
17047
- super({ name: "ObservabilityBus" });
17320
+ // src/spans/serialization.ts
17321
+ var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
17322
+ "logger",
17323
+ "experimental_providerMetadata",
17324
+ "providerMetadata",
17325
+ "steps",
17326
+ "tracingContext",
17327
+ "execute",
17328
+ // Tool execute functions
17329
+ "validate"
17330
+ // Schema validate functions
17331
+ ]);
17332
+ var DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
17333
+ keysToStrip: DEFAULT_KEYS_TO_STRIP,
17334
+ maxDepth: 8,
17335
+ maxStringLength: 128 * 1024,
17336
+ // 128KB - sufficient for large LLM prompts/responses
17337
+ maxArrayLength: 50,
17338
+ maxObjectKeys: 50
17339
+ });
17340
+ function mergeSerializationOptions(userOptions) {
17341
+ if (!userOptions) {
17342
+ return DEFAULT_DEEP_CLEAN_OPTIONS;
17048
17343
  }
17049
- /**
17050
- * Register an exporter to receive routed events.
17051
- * Duplicate registrations (same instance) are silently ignored.
17052
- *
17053
- * @param exporter - The exporter to register.
17054
- */
17055
- registerExporter(exporter) {
17056
- if (this.exporters.includes(exporter)) {
17057
- return;
17058
- }
17059
- this.exporters.push(exporter);
17344
+ return {
17345
+ keysToStrip: DEFAULT_KEYS_TO_STRIP,
17346
+ maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
17347
+ maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
17348
+ maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
17349
+ maxObjectKeys: userOptions.maxObjectKeys ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxObjectKeys
17350
+ };
17351
+ }
17352
+ function truncateString(s, maxChars) {
17353
+ if (s.length <= maxChars) {
17354
+ return s;
17060
17355
  }
17061
- /**
17062
- * Unregister an exporter.
17063
- *
17064
- * @param exporter - The exporter instance to remove.
17065
- * @returns `true` if the exporter was found and removed, `false` otherwise.
17066
- */
17067
- unregisterExporter(exporter) {
17068
- const index = this.exporters.indexOf(exporter);
17069
- if (index !== -1) {
17070
- this.exporters.splice(index, 1);
17071
- return true;
17072
- }
17073
- return false;
17356
+ return s.slice(0, maxChars) + "\u2026[truncated]";
17357
+ }
17358
+ function formatSerializationError(error48) {
17359
+ return `[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17360
+ }
17361
+ function getMapKeyType(key) {
17362
+ if (key === null) {
17363
+ return "null";
17074
17364
  }
17075
- /**
17076
- * Get registered exporters (read-only snapshot).
17077
- */
17078
- getExporters() {
17079
- return [...this.exporters];
17365
+ if (key instanceof Date) {
17366
+ return "date";
17080
17367
  }
17081
- /**
17082
- * Register a bridge to receive all routed events alongside exporters.
17083
- * Only one bridge can be registered at a time; replacing an existing bridge
17084
- * logs a warning.
17085
- *
17086
- * @param bridge - The bridge to register.
17087
- */
17088
- registerBridge(bridge) {
17089
- if (this.bridge) {
17090
- this.logger.warn(`[ObservabilityBus] Replacing existing bridge with new bridge`);
17091
- }
17092
- this.bridge = bridge;
17368
+ if (Array.isArray(key)) {
17369
+ return "array";
17093
17370
  }
17094
- /**
17095
- * Unregister the bridge.
17096
- *
17097
- * @returns `true` if a bridge was registered and removed, `false` otherwise.
17098
- */
17099
- unregisterBridge() {
17100
- if (this.bridge) {
17101
- this.bridge = void 0;
17371
+ if (key instanceof Map) {
17372
+ return "map";
17373
+ }
17374
+ if (key instanceof Set) {
17375
+ return "set";
17376
+ }
17377
+ if (key instanceof Error) {
17378
+ return "error";
17379
+ }
17380
+ return typeof key;
17381
+ }
17382
+ function restoreSerializedMapKey(keyType, key) {
17383
+ switch (keyType) {
17384
+ case "undefined":
17385
+ return void 0;
17386
+ case "null":
17387
+ return null;
17388
+ case "bigint":
17389
+ return typeof key === "string" && key.endsWith("n") ? BigInt(key.slice(0, -1)) : key;
17390
+ case "date":
17391
+ return typeof key === "string" ? new Date(key) : key;
17392
+ default:
17393
+ return key;
17394
+ }
17395
+ }
17396
+ function isSerializedMap(value) {
17397
+ return typeof value === "object" && value !== null && value.__type === "Map" && Array.isArray(value.__map_entries);
17398
+ }
17399
+ function reconstructSerializedMap(value) {
17400
+ return new Map(
17401
+ value.__map_entries.map(([keyType, key, mapValue]) => [restoreSerializedMapKey(keyType, key), mapValue])
17402
+ );
17403
+ }
17404
+ function isJsonSchema(val) {
17405
+ if (typeof val !== "object" || val === null) return false;
17406
+ if (val.$schema && typeof val.$schema === "string" && val.$schema.includes("json-schema")) {
17407
+ return true;
17408
+ }
17409
+ if (val.type === "object" && val.properties && typeof val.properties === "object") {
17410
+ return true;
17411
+ }
17412
+ return false;
17413
+ }
17414
+ function compressJsonSchema(schema, depth = 0) {
17415
+ if (depth > 3) {
17416
+ return schema.type || "object";
17417
+ }
17418
+ const compositionKeys = ["oneOf", "anyOf", "allOf"].filter((key) => Array.isArray(schema[key]));
17419
+ if (compositionKeys.length > 0) {
17420
+ const compressed2 = {};
17421
+ for (const key of compositionKeys) {
17422
+ compressed2[key] = schema[key].map((entry) => compressJsonSchema(entry, depth + 1));
17423
+ }
17424
+ if (typeof schema.type === "string") {
17425
+ compressed2.type = schema.type;
17426
+ }
17427
+ return compressed2;
17428
+ }
17429
+ if (schema.type !== "object" || !schema.properties) {
17430
+ return schema.type || schema;
17431
+ }
17432
+ const required2 = new Set(Array.isArray(schema.required) ? schema.required : []);
17433
+ const compressed = {};
17434
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
17435
+ const prop = propSchema;
17436
+ let value = prop.type || "unknown";
17437
+ if (prop.type === "object" && prop.properties) {
17438
+ value = compressJsonSchema(prop, depth + 1);
17439
+ if (required2.has(key)) {
17440
+ compressed[key + " (required)"] = value;
17441
+ continue;
17442
+ }
17443
+ } else if (prop.type === "array" && prop.items) {
17444
+ if (prop.items.type === "object" && prop.items.properties) {
17445
+ value = [compressJsonSchema(prop.items, depth + 1)];
17446
+ } else {
17447
+ value = `${prop.items.type || "any"}[]`;
17448
+ }
17449
+ } else if (prop.enum) {
17450
+ value = prop.enum.map((v) => JSON.stringify(v)).join(" | ");
17451
+ }
17452
+ if (required2.has(key) && typeof value === "string") {
17453
+ value += " (required)";
17454
+ }
17455
+ compressed[key] = value;
17456
+ }
17457
+ return compressed;
17458
+ }
17459
+ function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
17460
+ const { keysToStrip, maxDepth, maxStringLength, maxArrayLength, maxObjectKeys } = options;
17461
+ const stripSet = keysToStrip instanceof Set ? keysToStrip : new Set(Array.isArray(keysToStrip) ? keysToStrip : Object.keys(keysToStrip));
17462
+ const ancestors = /* @__PURE__ */ new WeakSet();
17463
+ function helper(val, depth) {
17464
+ if (depth > maxDepth) {
17465
+ return "[MaxDepth]";
17466
+ }
17467
+ if (val === null || val === void 0) {
17468
+ return val;
17469
+ }
17470
+ if (typeof val === "string") {
17471
+ return truncateString(val, maxStringLength);
17472
+ }
17473
+ if (typeof val === "number" || typeof val === "boolean") {
17474
+ return val;
17475
+ }
17476
+ if (typeof val === "bigint") {
17477
+ return `${val}n`;
17478
+ }
17479
+ if (typeof val === "function") {
17480
+ return "[Function]";
17481
+ }
17482
+ if (typeof val === "symbol") {
17483
+ return val.description ? `[Symbol(${val.description})]` : "[Symbol]";
17484
+ }
17485
+ if (val instanceof Date) {
17486
+ return val;
17487
+ }
17488
+ if (typeof val === "object") {
17489
+ if (ancestors.has(val)) {
17490
+ return "[Circular]";
17491
+ }
17492
+ ancestors.add(val);
17493
+ }
17494
+ try {
17495
+ if (val instanceof Error) {
17496
+ let errorName;
17497
+ let errorMessage;
17498
+ let errorStack;
17499
+ let rawCause;
17500
+ let causeReadFailed = false;
17501
+ try {
17502
+ errorName = val.name;
17503
+ } catch (error48) {
17504
+ errorName = formatSerializationError(error48);
17505
+ }
17506
+ try {
17507
+ errorMessage = val.message;
17508
+ } catch (error48) {
17509
+ errorMessage = formatSerializationError(error48);
17510
+ }
17511
+ try {
17512
+ errorStack = val.stack;
17513
+ } catch (error48) {
17514
+ errorStack = formatSerializationError(error48);
17515
+ }
17516
+ try {
17517
+ rawCause = val.cause;
17518
+ } catch (error48) {
17519
+ causeReadFailed = true;
17520
+ rawCause = formatSerializationError(error48);
17521
+ }
17522
+ const cleanedError = {
17523
+ name: typeof errorName === "string" ? truncateString(errorName, maxStringLength) : errorName,
17524
+ message: typeof errorMessage === "string" ? truncateString(errorMessage, maxStringLength) : errorMessage
17525
+ };
17526
+ if (typeof errorStack === "string") {
17527
+ cleanedError.stack = truncateString(errorStack, maxStringLength);
17528
+ } else if (errorStack !== void 0) {
17529
+ cleanedError.stack = errorStack;
17530
+ }
17531
+ if (causeReadFailed) {
17532
+ cleanedError.cause = rawCause;
17533
+ } else if (rawCause !== void 0) {
17534
+ try {
17535
+ cleanedError.cause = helper(rawCause, depth + 1);
17536
+ } catch (error48) {
17537
+ cleanedError.cause = formatSerializationError(error48);
17538
+ }
17539
+ }
17540
+ return cleanedError;
17541
+ }
17542
+ if (val instanceof Map) {
17543
+ const cleanedMap = { __type: "Map", __map_entries: [] };
17544
+ let mapKeyCount = 0;
17545
+ let omittedMapEntries = 0;
17546
+ for (const [mapKey, mapVal] of val) {
17547
+ if (typeof mapKey === "string" && stripSet.has(mapKey)) {
17548
+ continue;
17549
+ }
17550
+ if (mapKeyCount >= maxObjectKeys) {
17551
+ omittedMapEntries++;
17552
+ continue;
17553
+ }
17554
+ const mapKeyType = getMapKeyType(mapKey);
17555
+ let cleanedMapKey;
17556
+ let cleanedMapValue;
17557
+ try {
17558
+ cleanedMapKey = helper(mapKey, depth + 1);
17559
+ } catch (error48) {
17560
+ cleanedMapKey = formatSerializationError(error48);
17561
+ }
17562
+ try {
17563
+ cleanedMapValue = helper(mapVal, depth + 1);
17564
+ } catch (error48) {
17565
+ cleanedMapValue = formatSerializationError(error48);
17566
+ }
17567
+ cleanedMap.__map_entries.push([mapKeyType, cleanedMapKey, cleanedMapValue]);
17568
+ mapKeyCount++;
17569
+ }
17570
+ if (omittedMapEntries > 0) {
17571
+ cleanedMap.__truncated = `${omittedMapEntries} more keys omitted`;
17572
+ }
17573
+ return cleanedMap;
17574
+ }
17575
+ if (val instanceof Set) {
17576
+ const cleanedSet = [];
17577
+ let i = 0;
17578
+ const totalSetSize = val.size;
17579
+ for (const item of val) {
17580
+ if (i >= maxArrayLength) break;
17581
+ try {
17582
+ cleanedSet.push(helper(item, depth + 1));
17583
+ } catch (error48) {
17584
+ cleanedSet.push(formatSerializationError(error48));
17585
+ }
17586
+ i++;
17587
+ }
17588
+ if (totalSetSize > maxArrayLength) {
17589
+ cleanedSet.push(`[\u2026${totalSetSize - maxArrayLength} more items]`);
17590
+ }
17591
+ return cleanedSet;
17592
+ }
17593
+ if (Array.isArray(val)) {
17594
+ const cleaned2 = [];
17595
+ for (let i = 0; i < Math.min(val.length, maxArrayLength); i++) {
17596
+ try {
17597
+ cleaned2.push(helper(val[i], depth + 1));
17598
+ } catch (error48) {
17599
+ cleaned2.push(formatSerializationError(error48));
17600
+ }
17601
+ }
17602
+ if (val.length > maxArrayLength) {
17603
+ cleaned2.push(`[\u2026${val.length - maxArrayLength} more items]`);
17604
+ }
17605
+ return cleaned2;
17606
+ }
17607
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(val)) {
17608
+ return `[Buffer length=${val.length}]`;
17609
+ }
17610
+ if (ArrayBuffer.isView(val)) {
17611
+ const ctor = val.constructor?.name ?? "TypedArray";
17612
+ const byteLength = val.byteLength ?? "?";
17613
+ return `[${ctor} byteLength=${byteLength}]`;
17614
+ }
17615
+ if (val instanceof ArrayBuffer) {
17616
+ return `[ArrayBuffer byteLength=${val.byteLength}]`;
17617
+ }
17618
+ let serializeForSpan;
17619
+ try {
17620
+ serializeForSpan = val.serializeForSpan;
17621
+ } catch (error48) {
17622
+ return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17623
+ }
17624
+ if (typeof serializeForSpan === "function") {
17625
+ try {
17626
+ return helper(serializeForSpan.call(val), depth);
17627
+ } catch (error48) {
17628
+ return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
17629
+ }
17630
+ }
17631
+ let looksLikeJsonSchema = false;
17632
+ try {
17633
+ looksLikeJsonSchema = isJsonSchema(val);
17634
+ } catch {
17635
+ looksLikeJsonSchema = false;
17636
+ }
17637
+ if (looksLikeJsonSchema) {
17638
+ try {
17639
+ const compressed = compressJsonSchema(val);
17640
+ return compressed === val ? "[JSONSchema]" : helper(compressed, depth);
17641
+ } catch {
17642
+ }
17643
+ }
17644
+ const cleaned = {};
17645
+ const keys = Object.keys(val).filter((key) => !stripSet.has(key));
17646
+ let keyCount = 0;
17647
+ for (const key of keys) {
17648
+ if (keyCount >= maxObjectKeys) {
17649
+ cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
17650
+ break;
17651
+ }
17652
+ try {
17653
+ cleaned[key] = helper(val[key], depth + 1);
17654
+ keyCount++;
17655
+ } catch (error48) {
17656
+ cleaned[key] = formatSerializationError(error48);
17657
+ keyCount++;
17658
+ }
17659
+ }
17660
+ return cleaned;
17661
+ } finally {
17662
+ if (typeof val === "object" && val !== null) {
17663
+ ancestors.delete(val);
17664
+ }
17665
+ }
17666
+ }
17667
+ return helper(value, 0);
17668
+ }
17669
+
17670
+ // src/bus/observability-bus.ts
17671
+ function cleanEvent(event, options) {
17672
+ switch (event.type) {
17673
+ case "log":
17674
+ return { type: "log", log: deepClean(event.log, options) };
17675
+ case "metric":
17676
+ return { type: "metric", metric: deepClean(event.metric, options) };
17677
+ case "score":
17678
+ return { type: "score", score: deepClean(event.score, options) };
17679
+ case "feedback":
17680
+ return { type: "feedback", feedback: deepClean(event.feedback, options) };
17681
+ default:
17682
+ return event;
17683
+ }
17684
+ }
17685
+ var MAX_FLUSH_ITERATIONS = 3;
17686
+ var ObservabilityBus = class extends BaseObservabilityEventBus {
17687
+ exporters = [];
17688
+ bridge;
17689
+ /** In-flight handler promises from routeToHandler. Self-cleaning via .finally(). */
17690
+ pendingHandlers = /* @__PURE__ */ new Set();
17691
+ /** Resolved deepClean options applied to non-tracing events before fan-out. */
17692
+ deepCleanOptions;
17693
+ constructor(opts) {
17694
+ super({ name: "ObservabilityBus" });
17695
+ this.deepCleanOptions = mergeSerializationOptions(opts?.serializationOptions);
17696
+ }
17697
+ /**
17698
+ * Register an exporter to receive routed events.
17699
+ * Duplicate registrations (same instance) are silently ignored.
17700
+ *
17701
+ * @param exporter - The exporter to register.
17702
+ */
17703
+ registerExporter(exporter) {
17704
+ if (this.exporters.includes(exporter)) {
17705
+ return;
17706
+ }
17707
+ this.exporters.push(exporter);
17708
+ }
17709
+ /**
17710
+ * Unregister an exporter.
17711
+ *
17712
+ * @param exporter - The exporter instance to remove.
17713
+ * @returns `true` if the exporter was found and removed, `false` otherwise.
17714
+ */
17715
+ unregisterExporter(exporter) {
17716
+ const index = this.exporters.indexOf(exporter);
17717
+ if (index !== -1) {
17718
+ this.exporters.splice(index, 1);
17719
+ return true;
17720
+ }
17721
+ return false;
17722
+ }
17723
+ /**
17724
+ * Get registered exporters (read-only snapshot).
17725
+ */
17726
+ getExporters() {
17727
+ return [...this.exporters];
17728
+ }
17729
+ /**
17730
+ * Register a bridge to receive all routed events alongside exporters.
17731
+ * Only one bridge can be registered at a time; replacing an existing bridge
17732
+ * logs a warning.
17733
+ *
17734
+ * @param bridge - The bridge to register.
17735
+ */
17736
+ registerBridge(bridge) {
17737
+ if (this.bridge) {
17738
+ this.logger.warn(`[ObservabilityBus] Replacing existing bridge with new bridge`);
17739
+ }
17740
+ this.bridge = bridge;
17741
+ }
17742
+ /**
17743
+ * Unregister the bridge.
17744
+ *
17745
+ * @returns `true` if a bridge was registered and removed, `false` otherwise.
17746
+ */
17747
+ unregisterBridge() {
17748
+ if (this.bridge) {
17749
+ this.bridge = void 0;
17102
17750
  return true;
17103
17751
  }
17104
17752
  return false;
@@ -17117,13 +17765,14 @@ var ObservabilityBus = class extends BaseObservabilityEventBus {
17117
17765
  * and can be drained via flush().
17118
17766
  */
17119
17767
  emit(event) {
17768
+ const cleaned = cleanEvent(event, this.deepCleanOptions);
17120
17769
  for (const exporter of this.exporters) {
17121
- this.trackPromise(routeToHandler(exporter, event, this.logger));
17770
+ this.trackPromise(routeToHandler(exporter, cleaned, this.logger));
17122
17771
  }
17123
17772
  if (this.bridge) {
17124
- this.trackPromise(routeToHandler(this.bridge, event, this.logger));
17773
+ this.trackPromise(routeToHandler(this.bridge, cleaned, this.logger));
17125
17774
  }
17126
- super.emit(event);
17775
+ super.emit(cleaned);
17127
17776
  }
17128
17777
  /**
17129
17778
  * Track an async handler promise so flush() can await it.
@@ -18394,218 +19043,6 @@ var ModelSpanTracker = class {
18394
19043
  }
18395
19044
  };
18396
19045
 
18397
- // src/spans/serialization.ts
18398
- var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
18399
- "logger",
18400
- "experimental_providerMetadata",
18401
- "providerMetadata",
18402
- "steps",
18403
- "tracingContext",
18404
- "execute",
18405
- // Tool execute functions
18406
- "validate"
18407
- // Schema validate functions
18408
- ]);
18409
- var DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
18410
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
18411
- maxDepth: 8,
18412
- maxStringLength: 128 * 1024,
18413
- // 128KB - sufficient for large LLM prompts/responses
18414
- maxArrayLength: 50,
18415
- maxObjectKeys: 50
18416
- });
18417
- function mergeSerializationOptions(userOptions) {
18418
- if (!userOptions) {
18419
- return DEFAULT_DEEP_CLEAN_OPTIONS;
18420
- }
18421
- return {
18422
- keysToStrip: DEFAULT_KEYS_TO_STRIP,
18423
- maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
18424
- maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
18425
- maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
18426
- maxObjectKeys: userOptions.maxObjectKeys ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxObjectKeys
18427
- };
18428
- }
18429
- function truncateString(s, maxChars) {
18430
- if (s.length <= maxChars) {
18431
- return s;
18432
- }
18433
- return s.slice(0, maxChars) + "\u2026[truncated]";
18434
- }
18435
- function isJsonSchema(val) {
18436
- if (typeof val !== "object" || val === null) return false;
18437
- if (val.$schema && typeof val.$schema === "string" && val.$schema.includes("json-schema")) {
18438
- return true;
18439
- }
18440
- if (val.type === "object" && val.properties && typeof val.properties === "object") {
18441
- return true;
18442
- }
18443
- return false;
18444
- }
18445
- function compressJsonSchema(schema, depth = 0) {
18446
- if (depth > 3) {
18447
- return schema.type || "object";
18448
- }
18449
- const compositionKeys = ["oneOf", "anyOf", "allOf"].filter((key) => Array.isArray(schema[key]));
18450
- if (compositionKeys.length > 0) {
18451
- const compressed2 = {};
18452
- for (const key of compositionKeys) {
18453
- compressed2[key] = schema[key].map((entry) => compressJsonSchema(entry, depth + 1));
18454
- }
18455
- if (typeof schema.type === "string") {
18456
- compressed2.type = schema.type;
18457
- }
18458
- return compressed2;
18459
- }
18460
- if (schema.type !== "object" || !schema.properties) {
18461
- return schema.type || schema;
18462
- }
18463
- const required2 = new Set(Array.isArray(schema.required) ? schema.required : []);
18464
- const compressed = {};
18465
- for (const [key, propSchema] of Object.entries(schema.properties)) {
18466
- const prop = propSchema;
18467
- let value = prop.type || "unknown";
18468
- if (prop.type === "object" && prop.properties) {
18469
- value = compressJsonSchema(prop, depth + 1);
18470
- if (required2.has(key)) {
18471
- compressed[key + " (required)"] = value;
18472
- continue;
18473
- }
18474
- } else if (prop.type === "array" && prop.items) {
18475
- if (prop.items.type === "object" && prop.items.properties) {
18476
- value = [compressJsonSchema(prop.items, depth + 1)];
18477
- } else {
18478
- value = `${prop.items.type || "any"}[]`;
18479
- }
18480
- } else if (prop.enum) {
18481
- value = prop.enum.map((v) => JSON.stringify(v)).join(" | ");
18482
- }
18483
- if (required2.has(key) && typeof value === "string") {
18484
- value += " (required)";
18485
- }
18486
- compressed[key] = value;
18487
- }
18488
- return compressed;
18489
- }
18490
- function deepClean(value, options = DEFAULT_DEEP_CLEAN_OPTIONS) {
18491
- const { keysToStrip, maxDepth, maxStringLength, maxArrayLength, maxObjectKeys } = options;
18492
- const stripSet = keysToStrip instanceof Set ? keysToStrip : new Set(Array.isArray(keysToStrip) ? keysToStrip : Object.keys(keysToStrip));
18493
- const ancestors = /* @__PURE__ */ new WeakSet();
18494
- function helper(val, depth) {
18495
- if (depth > maxDepth) {
18496
- return "[MaxDepth]";
18497
- }
18498
- if (val === null || val === void 0) {
18499
- return val;
18500
- }
18501
- if (typeof val === "string") {
18502
- return truncateString(val, maxStringLength);
18503
- }
18504
- if (typeof val === "number" || typeof val === "boolean") {
18505
- return val;
18506
- }
18507
- if (typeof val === "bigint") {
18508
- return `${val}n`;
18509
- }
18510
- if (typeof val === "function") {
18511
- return "[Function]";
18512
- }
18513
- if (typeof val === "symbol") {
18514
- return val.description ? `[Symbol(${val.description})]` : "[Symbol]";
18515
- }
18516
- if (val instanceof Date) {
18517
- return val;
18518
- }
18519
- if (val instanceof Error) {
18520
- return {
18521
- name: val.name,
18522
- message: val.message ? truncateString(val.message, maxStringLength) : void 0
18523
- };
18524
- }
18525
- if (typeof val === "object") {
18526
- if (ancestors.has(val)) {
18527
- return "[Circular]";
18528
- }
18529
- ancestors.add(val);
18530
- }
18531
- try {
18532
- if (Array.isArray(val)) {
18533
- const cleaned2 = [];
18534
- for (let i = 0; i < Math.min(val.length, maxArrayLength); i++) {
18535
- try {
18536
- cleaned2.push(helper(val[i], depth + 1));
18537
- } catch (error48) {
18538
- cleaned2.push(`[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`);
18539
- }
18540
- }
18541
- if (val.length > maxArrayLength) {
18542
- cleaned2.push(`[\u2026${val.length - maxArrayLength} more items]`);
18543
- }
18544
- return cleaned2;
18545
- }
18546
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(val)) {
18547
- return `[Buffer length=${val.length}]`;
18548
- }
18549
- if (ArrayBuffer.isView(val)) {
18550
- const ctor = val.constructor?.name ?? "TypedArray";
18551
- const byteLength = val.byteLength ?? "?";
18552
- return `[${ctor} byteLength=${byteLength}]`;
18553
- }
18554
- if (val instanceof ArrayBuffer) {
18555
- return `[ArrayBuffer byteLength=${val.byteLength}]`;
18556
- }
18557
- let serializeForSpan;
18558
- try {
18559
- serializeForSpan = val.serializeForSpan;
18560
- } catch (error48) {
18561
- return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18562
- }
18563
- if (typeof serializeForSpan === "function") {
18564
- try {
18565
- return helper(serializeForSpan.call(val), depth);
18566
- } catch (error48) {
18567
- return `[serializeForSpan failed: ${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18568
- }
18569
- }
18570
- let looksLikeJsonSchema = false;
18571
- try {
18572
- looksLikeJsonSchema = isJsonSchema(val);
18573
- } catch {
18574
- looksLikeJsonSchema = false;
18575
- }
18576
- if (looksLikeJsonSchema) {
18577
- try {
18578
- const compressed = compressJsonSchema(val);
18579
- return compressed === val ? "[JSONSchema]" : helper(compressed, depth);
18580
- } catch {
18581
- }
18582
- }
18583
- const cleaned = {};
18584
- const keys = Object.keys(val).filter((key) => !stripSet.has(key));
18585
- let keyCount = 0;
18586
- for (const key of keys) {
18587
- if (keyCount >= maxObjectKeys) {
18588
- cleaned["__truncated"] = `${keys.length - keyCount} more keys omitted`;
18589
- break;
18590
- }
18591
- try {
18592
- cleaned[key] = helper(val[key], depth + 1);
18593
- keyCount++;
18594
- } catch (error48) {
18595
- cleaned[key] = `[${error48 instanceof Error ? truncateString(error48.message, 256) : "unknown error"}]`;
18596
- keyCount++;
18597
- }
18598
- }
18599
- return cleaned;
18600
- } finally {
18601
- if (typeof val === "object" && val !== null) {
18602
- ancestors.delete(val);
18603
- }
18604
- }
18605
- }
18606
- return helper(value, 0);
18607
- }
18608
-
18609
19046
  // src/spans/base.ts
18610
19047
  function isSpanInternal(spanType, flags) {
18611
19048
  if (flags === void 0 || flags === InternalSpans.NONE) {
@@ -19059,12 +19496,16 @@ var BaseObservabilityInstance = class extends MastraBase {
19059
19496
  spanOutputProcessors: config2.spanOutputProcessors ?? [],
19060
19497
  bridge: config2.bridge ?? void 0,
19061
19498
  includeInternalSpans: config2.includeInternalSpans ?? false,
19499
+ excludeSpanTypes: config2.excludeSpanTypes,
19500
+ spanFilter: config2.spanFilter,
19062
19501
  requestContextKeys: config2.requestContextKeys ?? [],
19063
19502
  serializationOptions: config2.serializationOptions,
19064
19503
  logging: config2.logging
19065
19504
  };
19066
19505
  this.cardinalityFilter = new CardinalityFilter(config2.cardinality);
19067
- this.observabilityBus = new ObservabilityBus();
19506
+ this.observabilityBus = new ObservabilityBus({
19507
+ serializationOptions: this.config.serializationOptions
19508
+ });
19068
19509
  for (const exporter of this.exporters) {
19069
19510
  this.observabilityBus.registerExporter(exporter);
19070
19511
  }
@@ -19442,8 +19883,18 @@ var BaseObservabilityInstance = class extends MastraBase {
19442
19883
  getSpanForExport(span) {
19443
19884
  if (!span.isValid) return void 0;
19444
19885
  if (span.isInternal && !this.config.includeInternalSpans) return void 0;
19886
+ if (this.config.excludeSpanTypes?.includes(span.type)) return void 0;
19445
19887
  const processedSpan = this.processSpan(span);
19446
- return processedSpan?.exportSpan(this.config.includeInternalSpans);
19888
+ const exportedSpan = processedSpan?.exportSpan(this.config.includeInternalSpans);
19889
+ if (!exportedSpan) return void 0;
19890
+ if (this.config.spanFilter) {
19891
+ try {
19892
+ if (!this.config.spanFilter(exportedSpan)) return void 0;
19893
+ } catch (error48) {
19894
+ this.logger.error(`[Observability] spanFilter error`, error48);
19895
+ }
19896
+ }
19897
+ return exportedSpan;
19447
19898
  }
19448
19899
  /**
19449
19900
  * Emit a span started event.
@@ -20397,6 +20848,6 @@ function buildTracingOptions(...updaters) {
20397
20848
  return updaters.reduce((opts, updater) => updater(opts), {});
20398
20849
  }
20399
20850
 
20400
- export { BaseExporter, BaseObservabilityEventBus, BaseObservabilityInstance, BaseSpan, CardinalityFilter, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, JsonExporter, LoggerContextImpl, MetricsContextImpl, ModelSpanTracker, NoOpSpan, Observability, ObservabilityBus, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildTracingOptions, chainFormatters, deepClean, getExternalParentId, mergeSerializationOptions, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, routeToHandler, samplingStrategySchema, serializationOptionsSchema, truncateString };
20851
+ export { BaseExporter, BaseObservabilityEventBus, BaseObservabilityInstance, BaseSpan, CardinalityFilter, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, JsonExporter, LoggerContextImpl, MetricsContextImpl, ModelSpanTracker, NoOpSpan, Observability, ObservabilityBus, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildTracingOptions, chainFormatters, deepClean, getExternalParentId, isSerializedMap, mergeSerializationOptions, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, reconstructSerializedMap, routeToHandler, samplingStrategySchema, serializationOptionsSchema, truncateString };
20401
20852
  //# sourceMappingURL=index.js.map
20402
20853
  //# sourceMappingURL=index.js.map