@mastra/observability 0.0.0-safe-stringify-telemetry-20251205024938 → 0.0.0-scorers-logs-20251208093427

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +161 -34
  2. package/README.md +99 -0
  3. package/dist/config.d.ts +445 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/default.d.ts +29 -0
  6. package/dist/default.d.ts.map +1 -0
  7. package/dist/exporters/base.d.ts +111 -0
  8. package/dist/exporters/base.d.ts.map +1 -0
  9. package/dist/exporters/cloud.d.ts +30 -0
  10. package/dist/exporters/cloud.d.ts.map +1 -0
  11. package/dist/exporters/console.d.ts +10 -0
  12. package/dist/exporters/console.d.ts.map +1 -0
  13. package/dist/exporters/default.d.ts +89 -0
  14. package/dist/exporters/default.d.ts.map +1 -0
  15. package/dist/exporters/index.d.ts +10 -0
  16. package/dist/exporters/index.d.ts.map +1 -0
  17. package/dist/exporters/test.d.ts +13 -0
  18. package/dist/exporters/test.d.ts.map +1 -0
  19. package/dist/index.cjs +2460 -0
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.ts +9 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2439 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/instances/base.d.ts +110 -0
  26. package/dist/instances/base.d.ts.map +1 -0
  27. package/dist/instances/default.d.ts +8 -0
  28. package/dist/instances/default.d.ts.map +1 -0
  29. package/dist/instances/index.d.ts +6 -0
  30. package/dist/instances/index.d.ts.map +1 -0
  31. package/dist/model-tracing.d.ts +42 -0
  32. package/dist/model-tracing.d.ts.map +1 -0
  33. package/dist/registry.d.ts +49 -0
  34. package/dist/registry.d.ts.map +1 -0
  35. package/dist/span_processors/index.d.ts +5 -0
  36. package/dist/span_processors/index.d.ts.map +1 -0
  37. package/dist/span_processors/sensitive-data-filter.d.ts +92 -0
  38. package/dist/span_processors/sensitive-data-filter.d.ts.map +1 -0
  39. package/dist/spans/base.d.ts +110 -0
  40. package/dist/spans/base.d.ts.map +1 -0
  41. package/dist/spans/default.d.ts +13 -0
  42. package/dist/spans/default.d.ts.map +1 -0
  43. package/dist/spans/index.d.ts +7 -0
  44. package/dist/spans/index.d.ts.map +1 -0
  45. package/dist/spans/no-op.d.ts +15 -0
  46. package/dist/spans/no-op.d.ts.map +1 -0
  47. package/dist/tracing-options.d.ts +27 -0
  48. package/dist/tracing-options.d.ts.map +1 -0
  49. package/package.json +17 -15
package/dist/index.cjs CHANGED
@@ -1,4 +1,2464 @@
1
1
  'use strict';
2
2
 
3
+ var base = require('@mastra/core/base');
4
+ var error = require('@mastra/core/error');
5
+ var logger = require('@mastra/core/logger');
6
+ var zod = require('zod');
7
+ var observability = require('@mastra/core/observability');
8
+ var utils = require('@mastra/core/utils');
9
+ var web = require('stream/web');
10
+
11
+ // src/default.ts
12
+ var SamplingStrategyType = /* @__PURE__ */ ((SamplingStrategyType2) => {
13
+ SamplingStrategyType2["ALWAYS"] = "always";
14
+ SamplingStrategyType2["NEVER"] = "never";
15
+ SamplingStrategyType2["RATIO"] = "ratio";
16
+ SamplingStrategyType2["CUSTOM"] = "custom";
17
+ return SamplingStrategyType2;
18
+ })(SamplingStrategyType || {});
19
+ var samplingStrategySchema = zod.z.discriminatedUnion("type", [
20
+ zod.z.object({
21
+ type: zod.z.literal("always" /* ALWAYS */)
22
+ }),
23
+ zod.z.object({
24
+ type: zod.z.literal("never" /* NEVER */)
25
+ }),
26
+ zod.z.object({
27
+ type: zod.z.literal("ratio" /* RATIO */),
28
+ probability: zod.z.number().min(0, "Probability must be between 0 and 1").max(1, "Probability must be between 0 and 1")
29
+ }),
30
+ zod.z.object({
31
+ type: zod.z.literal("custom" /* CUSTOM */),
32
+ sampler: zod.z.function().args(zod.z.any().optional()).returns(zod.z.boolean())
33
+ })
34
+ ]);
35
+ var observabilityInstanceConfigSchema = zod.z.object({
36
+ name: zod.z.string().min(1, "Name is required"),
37
+ serviceName: zod.z.string().min(1, "Service name is required"),
38
+ sampling: samplingStrategySchema.optional(),
39
+ exporters: zod.z.array(zod.z.any()).optional(),
40
+ bridge: zod.z.any().optional(),
41
+ spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
42
+ includeInternalSpans: zod.z.boolean().optional(),
43
+ requestContextKeys: zod.z.array(zod.z.string()).optional()
44
+ }).refine(
45
+ (data) => {
46
+ const hasExporters = data.exporters && data.exporters.length > 0;
47
+ const hasBridge = !!data.bridge;
48
+ return hasExporters || hasBridge;
49
+ },
50
+ {
51
+ message: "At least one exporter or a bridge is required"
52
+ }
53
+ );
54
+ var observabilityConfigValueSchema = zod.z.object({
55
+ serviceName: zod.z.string().min(1, "Service name is required"),
56
+ sampling: samplingStrategySchema.optional(),
57
+ exporters: zod.z.array(zod.z.any()).optional(),
58
+ bridge: zod.z.any().optional(),
59
+ spanOutputProcessors: zod.z.array(zod.z.any()).optional(),
60
+ includeInternalSpans: zod.z.boolean().optional(),
61
+ requestContextKeys: zod.z.array(zod.z.string()).optional()
62
+ }).refine(
63
+ (data) => {
64
+ const hasExporters = data.exporters && data.exporters.length > 0;
65
+ const hasBridge = !!data.bridge;
66
+ return hasExporters || hasBridge;
67
+ },
68
+ {
69
+ message: "At least one exporter or a bridge is required"
70
+ }
71
+ );
72
+ var observabilityRegistryConfigSchema = zod.z.object({
73
+ default: zod.z.object({
74
+ enabled: zod.z.boolean().optional()
75
+ }).optional().nullable(),
76
+ configs: zod.z.union([zod.z.record(zod.z.string(), zod.z.any()), zod.z.array(zod.z.any()), zod.z.null()]).optional(),
77
+ configSelector: zod.z.function().optional()
78
+ }).passthrough().refine(
79
+ (data) => {
80
+ const isDefaultEnabled = data.default?.enabled === true;
81
+ const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
82
+ return !(isDefaultEnabled && hasConfigs);
83
+ },
84
+ {
85
+ message: 'Cannot specify both "default" (when enabled) and "configs". Use either default observability or custom configs, but not both.'
86
+ }
87
+ ).refine(
88
+ (data) => {
89
+ const configCount = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length : 0;
90
+ if (configCount > 1 && !data.configSelector) {
91
+ return false;
92
+ }
93
+ return true;
94
+ },
95
+ {
96
+ message: 'A "configSelector" function is required when multiple configs are specified to determine which config to use.'
97
+ }
98
+ ).refine(
99
+ (data) => {
100
+ if (data.configSelector) {
101
+ const isDefaultEnabled = data.default?.enabled === true;
102
+ const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
103
+ return isDefaultEnabled || hasConfigs;
104
+ }
105
+ return true;
106
+ },
107
+ {
108
+ message: 'A "configSelector" requires at least one config or default observability to be configured.'
109
+ }
110
+ );
111
+ var BaseExporter = class {
112
+ /** Mastra logger instance */
113
+ logger;
114
+ /** Whether this exporter is disabled */
115
+ isDisabled = false;
116
+ /**
117
+ * Initialize the base exporter with logger
118
+ */
119
+ constructor(config = {}) {
120
+ const logLevel = this.resolveLogLevel(config.logLevel);
121
+ this.logger = config.logger ?? new logger.ConsoleLogger({ level: logLevel, name: this.constructor.name });
122
+ }
123
+ /**
124
+ * Set the logger for the exporter (called by Mastra/ObservabilityInstance during initialization)
125
+ */
126
+ __setLogger(logger) {
127
+ this.logger = logger;
128
+ this.logger.debug(`Logger updated for exporter [name=${this.name}]`);
129
+ }
130
+ /**
131
+ * Convert string log level to LogLevel enum
132
+ */
133
+ resolveLogLevel(logLevel) {
134
+ if (!logLevel) {
135
+ return logger.LogLevel.INFO;
136
+ }
137
+ if (typeof logLevel === "number") {
138
+ return logLevel;
139
+ }
140
+ const logLevelMap = {
141
+ debug: logger.LogLevel.DEBUG,
142
+ info: logger.LogLevel.INFO,
143
+ warn: logger.LogLevel.WARN,
144
+ error: logger.LogLevel.ERROR
145
+ };
146
+ return logLevelMap[logLevel] ?? logger.LogLevel.INFO;
147
+ }
148
+ /**
149
+ * Mark the exporter as disabled and log a message
150
+ *
151
+ * @param reason - Reason why the exporter is disabled
152
+ */
153
+ setDisabled(reason) {
154
+ this.isDisabled = true;
155
+ this.logger.warn(`${this.name} disabled: ${reason}`);
156
+ }
157
+ /**
158
+ * Export a tracing event
159
+ *
160
+ * This method checks if the exporter is disabled before calling _exportEvent.
161
+ * Subclasses should implement _exportEvent instead of overriding this method.
162
+ */
163
+ async exportTracingEvent(event) {
164
+ if (this.isDisabled) {
165
+ return;
166
+ }
167
+ await this._exportTracingEvent(event);
168
+ }
169
+ /**
170
+ * Shutdown the exporter and clean up resources
171
+ *
172
+ * Default implementation just logs. Override to add custom cleanup.
173
+ */
174
+ async shutdown() {
175
+ this.logger.info(`${this.name} shutdown complete`);
176
+ }
177
+ };
178
+ var CloudExporter = class extends BaseExporter {
179
+ name = "mastra-cloud-observability-exporter";
180
+ config;
181
+ buffer;
182
+ flushTimer = null;
183
+ constructor(config = {}) {
184
+ super(config);
185
+ const accessToken = config.accessToken ?? process.env.MASTRA_CLOUD_ACCESS_TOKEN;
186
+ if (!accessToken) {
187
+ this.setDisabled(
188
+ "MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.\n\u{1F680} Sign up at https://cloud.mastra.ai to see your AI traces online and obtain your access token."
189
+ );
190
+ }
191
+ const endpoint = config.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
192
+ this.config = {
193
+ logger: this.logger,
194
+ logLevel: config.logLevel ?? logger.LogLevel.INFO,
195
+ maxBatchSize: config.maxBatchSize ?? 1e3,
196
+ maxBatchWaitMs: config.maxBatchWaitMs ?? 5e3,
197
+ maxRetries: config.maxRetries ?? 3,
198
+ accessToken: accessToken || "",
199
+ endpoint
200
+ };
201
+ this.buffer = {
202
+ spans: [],
203
+ totalSize: 0
204
+ };
205
+ }
206
+ async _exportTracingEvent(event) {
207
+ if (event.type !== observability.TracingEventType.SPAN_ENDED) {
208
+ return;
209
+ }
210
+ this.addToBuffer(event);
211
+ if (this.shouldFlush()) {
212
+ this.flush().catch((error) => {
213
+ this.logger.error("Batch flush failed", {
214
+ error: error instanceof Error ? error.message : String(error)
215
+ });
216
+ });
217
+ } else if (this.buffer.totalSize === 1) {
218
+ this.scheduleFlush();
219
+ }
220
+ }
221
+ addToBuffer(event) {
222
+ if (this.buffer.totalSize === 0) {
223
+ this.buffer.firstEventTime = /* @__PURE__ */ new Date();
224
+ }
225
+ const spanRecord = this.formatSpan(event.exportedSpan);
226
+ this.buffer.spans.push(spanRecord);
227
+ this.buffer.totalSize++;
228
+ }
229
+ formatSpan(span) {
230
+ const spanRecord = {
231
+ traceId: span.traceId,
232
+ spanId: span.id,
233
+ parentSpanId: span.parentSpanId ?? null,
234
+ name: span.name,
235
+ spanType: span.type,
236
+ attributes: span.attributes ?? null,
237
+ metadata: span.metadata ?? null,
238
+ startedAt: span.startTime,
239
+ endedAt: span.endTime ?? null,
240
+ input: span.input ?? null,
241
+ output: span.output ?? null,
242
+ error: span.errorInfo,
243
+ isEvent: span.isEvent,
244
+ createdAt: /* @__PURE__ */ new Date(),
245
+ updatedAt: null
246
+ };
247
+ return spanRecord;
248
+ }
249
+ shouldFlush() {
250
+ if (this.buffer.totalSize >= this.config.maxBatchSize) {
251
+ return true;
252
+ }
253
+ if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
254
+ const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
255
+ if (elapsed >= this.config.maxBatchWaitMs) {
256
+ return true;
257
+ }
258
+ }
259
+ return false;
260
+ }
261
+ scheduleFlush() {
262
+ if (this.flushTimer) {
263
+ clearTimeout(this.flushTimer);
264
+ }
265
+ this.flushTimer = setTimeout(() => {
266
+ this.flush().catch((error$1) => {
267
+ const mastraError = new error.MastraError(
268
+ {
269
+ id: `CLOUD_EXPORTER_FAILED_TO_SCHEDULE_FLUSH`,
270
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
271
+ category: error.ErrorCategory.USER
272
+ },
273
+ error$1
274
+ );
275
+ this.logger.trackException(mastraError);
276
+ this.logger.error("Scheduled flush failed", mastraError);
277
+ });
278
+ }, this.config.maxBatchWaitMs);
279
+ }
280
+ async flush() {
281
+ if (this.flushTimer) {
282
+ clearTimeout(this.flushTimer);
283
+ this.flushTimer = null;
284
+ }
285
+ if (this.buffer.totalSize === 0) {
286
+ return;
287
+ }
288
+ const startTime = Date.now();
289
+ const spansCopy = [...this.buffer.spans];
290
+ const flushReason = this.buffer.totalSize >= this.config.maxBatchSize ? "size" : "time";
291
+ this.resetBuffer();
292
+ try {
293
+ await this.batchUpload(spansCopy);
294
+ const elapsed = Date.now() - startTime;
295
+ this.logger.debug("Batch flushed successfully", {
296
+ batchSize: spansCopy.length,
297
+ flushReason,
298
+ durationMs: elapsed
299
+ });
300
+ } catch (error$1) {
301
+ const mastraError = new error.MastraError(
302
+ {
303
+ id: `CLOUD_EXPORTER_FAILED_TO_BATCH_UPLOAD`,
304
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
305
+ category: error.ErrorCategory.USER,
306
+ details: {
307
+ droppedBatchSize: spansCopy.length
308
+ }
309
+ },
310
+ error$1
311
+ );
312
+ this.logger.trackException(mastraError);
313
+ this.logger.error("Batch upload failed after all retries, dropping batch", mastraError);
314
+ }
315
+ }
316
+ /**
317
+ * Uploads spans to cloud API using fetchWithRetry for all retry logic
318
+ */
319
+ async batchUpload(spans) {
320
+ const headers = {
321
+ Authorization: `Bearer ${this.config.accessToken}`,
322
+ "Content-Type": "application/json"
323
+ };
324
+ const options = {
325
+ method: "POST",
326
+ headers,
327
+ body: JSON.stringify({ spans })
328
+ };
329
+ await utils.fetchWithRetry(this.config.endpoint, options, this.config.maxRetries);
330
+ }
331
+ resetBuffer() {
332
+ this.buffer.spans = [];
333
+ this.buffer.firstEventTime = void 0;
334
+ this.buffer.totalSize = 0;
335
+ }
336
+ async shutdown() {
337
+ if (this.isDisabled) {
338
+ return;
339
+ }
340
+ if (this.flushTimer) {
341
+ clearTimeout(this.flushTimer);
342
+ this.flushTimer = null;
343
+ }
344
+ if (this.buffer.totalSize > 0) {
345
+ this.logger.info("Flushing remaining events on shutdown", {
346
+ remainingEvents: this.buffer.totalSize
347
+ });
348
+ try {
349
+ await this.flush();
350
+ } catch (error$1) {
351
+ const mastraError = new error.MastraError(
352
+ {
353
+ id: `CLOUD_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
354
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
355
+ category: error.ErrorCategory.USER,
356
+ details: {
357
+ remainingEvents: this.buffer.totalSize
358
+ }
359
+ },
360
+ error$1
361
+ );
362
+ this.logger.trackException(mastraError);
363
+ this.logger.error("Failed to flush remaining events during shutdown", mastraError);
364
+ }
365
+ }
366
+ this.logger.info("CloudExporter shutdown complete");
367
+ }
368
+ };
369
+ var ConsoleExporter = class extends BaseExporter {
370
+ name = "tracing-console-exporter";
371
+ constructor(config = {}) {
372
+ super(config);
373
+ }
374
+ async _exportTracingEvent(event) {
375
+ const span = event.exportedSpan;
376
+ const formatAttributes = (attributes) => {
377
+ try {
378
+ return JSON.stringify(attributes, null, 2);
379
+ } catch (error) {
380
+ const errMsg = error instanceof Error ? error.message : "Unknown formatting error";
381
+ return `[Unable to serialize attributes: ${errMsg}]`;
382
+ }
383
+ };
384
+ const formatDuration = (startTime, endTime) => {
385
+ if (!endTime) return "N/A";
386
+ const duration = endTime.getTime() - startTime.getTime();
387
+ return `${duration}ms`;
388
+ };
389
+ switch (event.type) {
390
+ case observability.TracingEventType.SPAN_STARTED:
391
+ this.logger.info(`\u{1F680} SPAN_STARTED`);
392
+ this.logger.info(` Type: ${span.type}`);
393
+ this.logger.info(` Name: ${span.name}`);
394
+ this.logger.info(` ID: ${span.id}`);
395
+ this.logger.info(` Trace ID: ${span.traceId}`);
396
+ if (span.input !== void 0) {
397
+ this.logger.info(` Input: ${formatAttributes(span.input)}`);
398
+ }
399
+ this.logger.info(` Attributes: ${formatAttributes(span.attributes)}`);
400
+ this.logger.info("\u2500".repeat(80));
401
+ break;
402
+ case observability.TracingEventType.SPAN_ENDED:
403
+ const duration = formatDuration(span.startTime, span.endTime);
404
+ this.logger.info(`\u2705 SPAN_ENDED`);
405
+ this.logger.info(` Type: ${span.type}`);
406
+ this.logger.info(` Name: ${span.name}`);
407
+ this.logger.info(` ID: ${span.id}`);
408
+ this.logger.info(` Duration: ${duration}`);
409
+ this.logger.info(` Trace ID: ${span.traceId}`);
410
+ if (span.input !== void 0) {
411
+ this.logger.info(` Input: ${formatAttributes(span.input)}`);
412
+ }
413
+ if (span.output !== void 0) {
414
+ this.logger.info(` Output: ${formatAttributes(span.output)}`);
415
+ }
416
+ if (span.errorInfo) {
417
+ this.logger.info(` Error: ${formatAttributes(span.errorInfo)}`);
418
+ }
419
+ this.logger.info(` Attributes: ${formatAttributes(span.attributes)}`);
420
+ this.logger.info("\u2500".repeat(80));
421
+ break;
422
+ case observability.TracingEventType.SPAN_UPDATED:
423
+ this.logger.info(`\u{1F4DD} SPAN_UPDATED`);
424
+ this.logger.info(` Type: ${span.type}`);
425
+ this.logger.info(` Name: ${span.name}`);
426
+ this.logger.info(` ID: ${span.id}`);
427
+ this.logger.info(` Trace ID: ${span.traceId}`);
428
+ if (span.input !== void 0) {
429
+ this.logger.info(` Input: ${formatAttributes(span.input)}`);
430
+ }
431
+ if (span.output !== void 0) {
432
+ this.logger.info(` Output: ${formatAttributes(span.output)}`);
433
+ }
434
+ if (span.errorInfo) {
435
+ this.logger.info(` Error: ${formatAttributes(span.errorInfo)}`);
436
+ }
437
+ this.logger.info(` Updated Attributes: ${formatAttributes(span.attributes)}`);
438
+ this.logger.info("\u2500".repeat(80));
439
+ break;
440
+ default:
441
+ this.logger.warn(`Tracing event type not implemented: ${event.type}`);
442
+ }
443
+ }
444
+ async shutdown() {
445
+ this.logger.info("ConsoleExporter shutdown");
446
+ }
447
+ };
448
+ function resolveTracingStorageStrategy(config, storage, logger) {
449
+ if (config.strategy && config.strategy !== "auto") {
450
+ const hints = storage.tracingStrategy;
451
+ if (hints.supported.includes(config.strategy)) {
452
+ return config.strategy;
453
+ }
454
+ logger.warn("User-specified tracing strategy not supported by storage adapter, falling back to auto-selection", {
455
+ userStrategy: config.strategy,
456
+ storageAdapter: storage.constructor.name,
457
+ supportedStrategies: hints.supported,
458
+ fallbackStrategy: hints.preferred
459
+ });
460
+ }
461
+ return storage.tracingStrategy.preferred;
462
+ }
463
+ var DefaultExporter = class extends BaseExporter {
464
+ name = "mastra-default-observability-exporter";
465
+ #storage;
466
+ #config;
467
+ #resolvedStrategy;
468
+ buffer;
469
+ #flushTimer = null;
470
+ // Track all spans that have been created, persists across flushes
471
+ allCreatedSpans = /* @__PURE__ */ new Set();
472
+ constructor(config = {}) {
473
+ super(config);
474
+ if (config === void 0) {
475
+ config = {};
476
+ }
477
+ this.#config = {
478
+ ...config,
479
+ maxBatchSize: config.maxBatchSize ?? 1e3,
480
+ maxBufferSize: config.maxBufferSize ?? 1e4,
481
+ maxBatchWaitMs: config.maxBatchWaitMs ?? 5e3,
482
+ maxRetries: config.maxRetries ?? 4,
483
+ retryDelayMs: config.retryDelayMs ?? 500,
484
+ strategy: config.strategy ?? "auto"
485
+ };
486
+ this.buffer = {
487
+ creates: [],
488
+ updates: [],
489
+ insertOnly: [],
490
+ seenSpans: /* @__PURE__ */ new Set(),
491
+ spanSequences: /* @__PURE__ */ new Map(),
492
+ completedSpans: /* @__PURE__ */ new Set(),
493
+ outOfOrderCount: 0,
494
+ totalSize: 0
495
+ };
496
+ this.#resolvedStrategy = "batch-with-updates";
497
+ }
498
+ #strategyInitialized = false;
499
+ /**
500
+ * Initialize the exporter (called after all dependencies are ready)
501
+ */
502
+ init(options) {
503
+ this.#storage = options.mastra?.getStorage();
504
+ if (!this.#storage) {
505
+ this.logger.warn("DefaultExporter disabled: Storage not available. Traces will not be persisted.");
506
+ return;
507
+ }
508
+ this.initializeStrategy(this.#storage);
509
+ }
510
+ /**
511
+ * Initialize the resolved strategy once storage is available
512
+ */
513
+ initializeStrategy(storage) {
514
+ if (this.#strategyInitialized) return;
515
+ this.#resolvedStrategy = resolveTracingStorageStrategy(this.#config, storage, this.logger);
516
+ this.#strategyInitialized = true;
517
+ this.logger.debug("tracing storage exporter initialized", {
518
+ strategy: this.#resolvedStrategy,
519
+ source: this.#config.strategy !== "auto" ? "user" : "auto",
520
+ storageAdapter: storage.constructor.name,
521
+ maxBatchSize: this.#config.maxBatchSize,
522
+ maxBatchWaitMs: this.#config.maxBatchWaitMs
523
+ });
524
+ }
525
+ /**
526
+ * Builds a unique span key for tracking
527
+ */
528
+ buildSpanKey(traceId, spanId) {
529
+ return `${traceId}:${spanId}`;
530
+ }
531
+ /**
532
+ * Gets the next sequence number for a span
533
+ */
534
+ getNextSequence(spanKey) {
535
+ const current = this.buffer.spanSequences.get(spanKey) || 0;
536
+ const next = current + 1;
537
+ this.buffer.spanSequences.set(spanKey, next);
538
+ return next;
539
+ }
540
+ /**
541
+ * Handles out-of-order span updates by logging and skipping
542
+ */
543
+ handleOutOfOrderUpdate(event) {
544
+ this.logger.warn("Out-of-order span update detected - skipping event", {
545
+ spanId: event.exportedSpan.id,
546
+ traceId: event.exportedSpan.traceId,
547
+ spanName: event.exportedSpan.name,
548
+ eventType: event.type
549
+ });
550
+ }
551
+ /**
552
+ * Adds an event to the appropriate buffer based on strategy
553
+ */
554
+ addToBuffer(event) {
555
+ const spanKey = this.buildSpanKey(event.exportedSpan.traceId, event.exportedSpan.id);
556
+ if (this.buffer.totalSize === 0) {
557
+ this.buffer.firstEventTime = /* @__PURE__ */ new Date();
558
+ }
559
+ switch (event.type) {
560
+ case observability.TracingEventType.SPAN_STARTED:
561
+ if (this.#resolvedStrategy === "batch-with-updates") {
562
+ const createRecord = this.buildCreateRecord(event.exportedSpan);
563
+ this.buffer.creates.push(createRecord);
564
+ this.buffer.seenSpans.add(spanKey);
565
+ this.allCreatedSpans.add(spanKey);
566
+ }
567
+ break;
568
+ case observability.TracingEventType.SPAN_UPDATED:
569
+ if (this.#resolvedStrategy === "batch-with-updates") {
570
+ if (this.allCreatedSpans.has(spanKey)) {
571
+ this.buffer.updates.push({
572
+ traceId: event.exportedSpan.traceId,
573
+ spanId: event.exportedSpan.id,
574
+ updates: this.buildUpdateRecord(event.exportedSpan),
575
+ sequenceNumber: this.getNextSequence(spanKey)
576
+ });
577
+ } else {
578
+ this.handleOutOfOrderUpdate(event);
579
+ this.buffer.outOfOrderCount++;
580
+ }
581
+ }
582
+ break;
583
+ case observability.TracingEventType.SPAN_ENDED:
584
+ if (this.#resolvedStrategy === "batch-with-updates") {
585
+ if (this.allCreatedSpans.has(spanKey)) {
586
+ this.buffer.updates.push({
587
+ traceId: event.exportedSpan.traceId,
588
+ spanId: event.exportedSpan.id,
589
+ updates: this.buildUpdateRecord(event.exportedSpan),
590
+ sequenceNumber: this.getNextSequence(spanKey)
591
+ });
592
+ this.buffer.completedSpans.add(spanKey);
593
+ } else if (event.exportedSpan.isEvent) {
594
+ const createRecord = this.buildCreateRecord(event.exportedSpan);
595
+ this.buffer.creates.push(createRecord);
596
+ this.buffer.seenSpans.add(spanKey);
597
+ this.allCreatedSpans.add(spanKey);
598
+ this.buffer.completedSpans.add(spanKey);
599
+ } else {
600
+ this.handleOutOfOrderUpdate(event);
601
+ this.buffer.outOfOrderCount++;
602
+ }
603
+ } else if (this.#resolvedStrategy === "insert-only") {
604
+ const createRecord = this.buildCreateRecord(event.exportedSpan);
605
+ this.buffer.insertOnly.push(createRecord);
606
+ this.buffer.completedSpans.add(spanKey);
607
+ this.allCreatedSpans.add(spanKey);
608
+ }
609
+ break;
610
+ }
611
+ this.buffer.totalSize = this.buffer.creates.length + this.buffer.updates.length + this.buffer.insertOnly.length;
612
+ }
613
+ /**
614
+ * Checks if buffer should be flushed based on size or time triggers
615
+ */
616
+ shouldFlush() {
617
+ if (this.buffer.totalSize >= this.#config.maxBufferSize) {
618
+ return true;
619
+ }
620
+ if (this.buffer.totalSize >= this.#config.maxBatchSize) {
621
+ return true;
622
+ }
623
+ if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
624
+ const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
625
+ if (elapsed >= this.#config.maxBatchWaitMs) {
626
+ return true;
627
+ }
628
+ }
629
+ return false;
630
+ }
631
+ /**
632
+ * Resets the buffer after successful flush
633
+ */
634
+ resetBuffer(completedSpansToCleanup = /* @__PURE__ */ new Set()) {
635
+ this.buffer.creates = [];
636
+ this.buffer.updates = [];
637
+ this.buffer.insertOnly = [];
638
+ this.buffer.seenSpans.clear();
639
+ this.buffer.spanSequences.clear();
640
+ this.buffer.completedSpans.clear();
641
+ this.buffer.outOfOrderCount = 0;
642
+ this.buffer.firstEventTime = void 0;
643
+ this.buffer.totalSize = 0;
644
+ for (const spanKey of completedSpansToCleanup) {
645
+ this.allCreatedSpans.delete(spanKey);
646
+ }
647
+ }
648
+ /**
649
+ * Schedules a flush using setTimeout
650
+ */
651
+ scheduleFlush() {
652
+ if (this.#flushTimer) {
653
+ clearTimeout(this.#flushTimer);
654
+ }
655
+ this.#flushTimer = setTimeout(() => {
656
+ this.flush().catch((error) => {
657
+ this.logger.error("Scheduled flush failed", {
658
+ error: error instanceof Error ? error.message : String(error)
659
+ });
660
+ });
661
+ }, this.#config.maxBatchWaitMs);
662
+ }
663
+ /**
664
+ * Serializes span attributes to storage record format
665
+ * Handles all Span types and their specific attributes
666
+ */
667
+ serializeAttributes(span) {
668
+ if (!span.attributes) {
669
+ return null;
670
+ }
671
+ try {
672
+ return JSON.parse(
673
+ JSON.stringify(span.attributes, (_key, value) => {
674
+ if (value instanceof Date) {
675
+ return value.toISOString();
676
+ }
677
+ if (typeof value === "object" && value !== null) {
678
+ return value;
679
+ }
680
+ return value;
681
+ })
682
+ );
683
+ } catch (error) {
684
+ this.logger.warn("Failed to serialize span attributes, storing as null", {
685
+ spanId: span.id,
686
+ spanType: span.type,
687
+ error: error instanceof Error ? error.message : String(error)
688
+ });
689
+ return null;
690
+ }
691
+ }
692
+ buildCreateRecord(span) {
693
+ return {
694
+ traceId: span.traceId,
695
+ spanId: span.id,
696
+ parentSpanId: span.parentSpanId ?? null,
697
+ name: span.name,
698
+ scope: null,
699
+ spanType: span.type,
700
+ attributes: this.serializeAttributes(span),
701
+ metadata: span.metadata ?? null,
702
+ links: null,
703
+ startedAt: span.startTime,
704
+ endedAt: span.endTime ?? null,
705
+ input: span.input,
706
+ output: span.output,
707
+ error: span.errorInfo,
708
+ isEvent: span.isEvent
709
+ };
710
+ }
711
+ buildUpdateRecord(span) {
712
+ return {
713
+ name: span.name,
714
+ scope: null,
715
+ attributes: this.serializeAttributes(span),
716
+ metadata: span.metadata ?? null,
717
+ links: null,
718
+ endedAt: span.endTime ?? null,
719
+ input: span.input,
720
+ output: span.output,
721
+ error: span.errorInfo
722
+ };
723
+ }
724
+ /**
725
+ * Handles realtime strategy - processes each event immediately
726
+ */
727
+ async handleRealtimeEvent(event, storage) {
728
+ const span = event.exportedSpan;
729
+ const spanKey = this.buildSpanKey(span.traceId, span.id);
730
+ if (span.isEvent) {
731
+ if (event.type === observability.TracingEventType.SPAN_ENDED) {
732
+ await storage.createSpan(this.buildCreateRecord(event.exportedSpan));
733
+ } else {
734
+ this.logger.warn(`Tracing event type not implemented for event spans: ${event.type}`);
735
+ }
736
+ } else {
737
+ switch (event.type) {
738
+ case observability.TracingEventType.SPAN_STARTED:
739
+ await storage.createSpan(this.buildCreateRecord(event.exportedSpan));
740
+ this.allCreatedSpans.add(spanKey);
741
+ break;
742
+ case observability.TracingEventType.SPAN_UPDATED:
743
+ await storage.updateSpan({
744
+ traceId: span.traceId,
745
+ spanId: span.id,
746
+ updates: this.buildUpdateRecord(span)
747
+ });
748
+ break;
749
+ case observability.TracingEventType.SPAN_ENDED:
750
+ await storage.updateSpan({
751
+ traceId: span.traceId,
752
+ spanId: span.id,
753
+ updates: this.buildUpdateRecord(span)
754
+ });
755
+ this.allCreatedSpans.delete(spanKey);
756
+ break;
757
+ default:
758
+ this.logger.warn(`Tracing event type not implemented for span spans: ${event.type}`);
759
+ }
760
+ }
761
+ }
762
+ /**
763
+ * Handles batch-with-updates strategy - buffers events and processes in batches
764
+ */
765
+ handleBatchWithUpdatesEvent(event) {
766
+ this.addToBuffer(event);
767
+ if (this.shouldFlush()) {
768
+ this.flush().catch((error) => {
769
+ this.logger.error("Batch flush failed", {
770
+ error: error instanceof Error ? error.message : String(error)
771
+ });
772
+ });
773
+ } else if (this.buffer.totalSize === 1) {
774
+ this.scheduleFlush();
775
+ }
776
+ }
777
+ /**
778
+ * Handles insert-only strategy - only processes SPAN_ENDED events in batches
779
+ */
780
+ handleInsertOnlyEvent(event) {
781
+ if (event.type === observability.TracingEventType.SPAN_ENDED) {
782
+ this.addToBuffer(event);
783
+ if (this.shouldFlush()) {
784
+ this.flush().catch((error) => {
785
+ this.logger.error("Batch flush failed", {
786
+ error: error instanceof Error ? error.message : String(error)
787
+ });
788
+ });
789
+ } else if (this.buffer.totalSize === 1) {
790
+ this.scheduleFlush();
791
+ }
792
+ }
793
+ }
794
+ /**
795
+ * Calculates retry delay using exponential backoff
796
+ */
797
+ calculateRetryDelay(attempt) {
798
+ return this.#config.retryDelayMs * Math.pow(2, attempt);
799
+ }
800
+ /**
801
+ * Flushes the current buffer to storage with retry logic
802
+ */
803
+ async flush() {
804
+ if (!this.#storage) {
805
+ this.logger.debug("Cannot flush traces. Mastra storage is not initialized");
806
+ return;
807
+ }
808
+ if (this.#flushTimer) {
809
+ clearTimeout(this.#flushTimer);
810
+ this.#flushTimer = null;
811
+ }
812
+ if (this.buffer.totalSize === 0) {
813
+ return;
814
+ }
815
+ const startTime = Date.now();
816
+ const flushReason = this.buffer.totalSize >= this.#config.maxBufferSize ? "overflow" : this.buffer.totalSize >= this.#config.maxBatchSize ? "size" : "time";
817
+ const bufferCopy = {
818
+ creates: [...this.buffer.creates],
819
+ updates: [...this.buffer.updates],
820
+ insertOnly: [...this.buffer.insertOnly],
821
+ seenSpans: new Set(this.buffer.seenSpans),
822
+ spanSequences: new Map(this.buffer.spanSequences),
823
+ completedSpans: new Set(this.buffer.completedSpans),
824
+ outOfOrderCount: this.buffer.outOfOrderCount,
825
+ firstEventTime: this.buffer.firstEventTime,
826
+ totalSize: this.buffer.totalSize
827
+ };
828
+ this.resetBuffer();
829
+ await this.flushWithRetries(this.#storage, bufferCopy, 0);
830
+ const elapsed = Date.now() - startTime;
831
+ this.logger.debug("Batch flushed", {
832
+ strategy: this.#resolvedStrategy,
833
+ batchSize: bufferCopy.totalSize,
834
+ flushReason,
835
+ durationMs: elapsed,
836
+ outOfOrderCount: bufferCopy.outOfOrderCount > 0 ? bufferCopy.outOfOrderCount : void 0
837
+ });
838
+ }
839
+ /**
840
+ * Attempts to flush with exponential backoff retry logic
841
+ */
842
+ async flushWithRetries(storage, buffer, attempt) {
843
+ try {
844
+ if (this.#resolvedStrategy === "batch-with-updates") {
845
+ if (buffer.creates.length > 0) {
846
+ await storage.batchCreateSpans({ records: buffer.creates });
847
+ }
848
+ if (buffer.updates.length > 0) {
849
+ const sortedUpdates = buffer.updates.sort((a, b) => {
850
+ const spanCompare = this.buildSpanKey(a.traceId, a.spanId).localeCompare(
851
+ this.buildSpanKey(b.traceId, b.spanId)
852
+ );
853
+ if (spanCompare !== 0) return spanCompare;
854
+ return a.sequenceNumber - b.sequenceNumber;
855
+ });
856
+ await storage.batchUpdateSpans({ records: sortedUpdates });
857
+ }
858
+ } else if (this.#resolvedStrategy === "insert-only") {
859
+ if (buffer.insertOnly.length > 0) {
860
+ await storage.batchCreateSpans({ records: buffer.insertOnly });
861
+ }
862
+ }
863
+ for (const spanKey of buffer.completedSpans) {
864
+ this.allCreatedSpans.delete(spanKey);
865
+ }
866
+ } catch (error) {
867
+ if (attempt < this.#config.maxRetries) {
868
+ const retryDelay = this.calculateRetryDelay(attempt);
869
+ this.logger.warn("Batch flush failed, retrying", {
870
+ attempt: attempt + 1,
871
+ maxRetries: this.#config.maxRetries,
872
+ nextRetryInMs: retryDelay,
873
+ error: error instanceof Error ? error.message : String(error)
874
+ });
875
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
876
+ return this.flushWithRetries(storage, buffer, attempt + 1);
877
+ } else {
878
+ this.logger.error("Batch flush failed after all retries, dropping batch", {
879
+ finalAttempt: attempt + 1,
880
+ maxRetries: this.#config.maxRetries,
881
+ droppedBatchSize: buffer.totalSize,
882
+ error: error instanceof Error ? error.message : String(error)
883
+ });
884
+ for (const spanKey of buffer.completedSpans) {
885
+ this.allCreatedSpans.delete(spanKey);
886
+ }
887
+ }
888
+ }
889
+ }
890
+ async _exportTracingEvent(event) {
891
+ if (!this.#storage) {
892
+ this.logger.debug("Cannot store traces. Mastra storage is not initialized");
893
+ return;
894
+ }
895
+ if (!this.#strategyInitialized) {
896
+ this.initializeStrategy(this.#storage);
897
+ }
898
+ switch (this.#resolvedStrategy) {
899
+ case "realtime":
900
+ await this.handleRealtimeEvent(event, this.#storage);
901
+ break;
902
+ case "batch-with-updates":
903
+ this.handleBatchWithUpdatesEvent(event);
904
+ break;
905
+ case "insert-only":
906
+ this.handleInsertOnlyEvent(event);
907
+ break;
908
+ }
909
+ }
910
+ async shutdown() {
911
+ if (this.#flushTimer) {
912
+ clearTimeout(this.#flushTimer);
913
+ this.#flushTimer = null;
914
+ }
915
+ if (this.buffer.totalSize > 0) {
916
+ this.logger.info("Flushing remaining events on shutdown", {
917
+ remainingEvents: this.buffer.totalSize
918
+ });
919
+ try {
920
+ await this.flush();
921
+ } catch (error) {
922
+ this.logger.error("Failed to flush remaining events during shutdown", {
923
+ error: error instanceof Error ? error.message : String(error)
924
+ });
925
+ }
926
+ }
927
+ this.logger.info("DefaultExporter shutdown complete");
928
+ }
929
+ };
930
+
931
+ // src/exporters/test.ts
932
+ var TestExporter = class extends BaseExporter {
933
+ name = "tracing-test-exporter";
934
+ #events = [];
935
+ constructor(config = {}) {
936
+ super(config);
937
+ }
938
+ async _exportTracingEvent(event) {
939
+ this.#events.push(event);
940
+ }
941
+ clearEvents() {
942
+ this.#events = [];
943
+ }
944
+ get events() {
945
+ return this.#events;
946
+ }
947
+ async shutdown() {
948
+ this.logger.info("TestExporter shutdown");
949
+ }
950
+ };
951
+ var ModelSpanTracker = class {
952
+ #modelSpan;
953
+ #currentStepSpan;
954
+ #currentChunkSpan;
955
+ #accumulator = {};
956
+ #stepIndex = 0;
957
+ #chunkSequence = 0;
958
+ /** Tracks whether completionStartTime has been captured for this generation */
959
+ #completionStartTimeCaptured = false;
960
+ /** Tracks tool output accumulators by toolCallId for consolidating sub-agent streams */
961
+ #toolOutputAccumulators = /* @__PURE__ */ new Map();
962
+ /** Tracks toolCallIds that had streaming output (to skip redundant tool-result spans) */
963
+ #streamedToolCallIds = /* @__PURE__ */ new Set();
964
+ constructor(modelSpan) {
965
+ this.#modelSpan = modelSpan;
966
+ }
967
+ /**
968
+ * Capture the completion start time (time to first token) when the first content chunk arrives.
969
+ * This is used by observability providers like Langfuse to calculate TTFT metrics.
970
+ */
971
+ #captureCompletionStartTime() {
972
+ if (this.#completionStartTimeCaptured || !this.#modelSpan) {
973
+ return;
974
+ }
975
+ this.#completionStartTimeCaptured = true;
976
+ this.#modelSpan.update({
977
+ attributes: {
978
+ completionStartTime: /* @__PURE__ */ new Date()
979
+ }
980
+ });
981
+ }
982
+ /**
983
+ * Get the tracing context for creating child spans.
984
+ * Returns the current step span if active, otherwise the model span.
985
+ */
986
+ getTracingContext() {
987
+ return {
988
+ currentSpan: this.#currentStepSpan ?? this.#modelSpan
989
+ };
990
+ }
991
+ /**
992
+ * Report an error on the generation span
993
+ */
994
+ reportGenerationError(options) {
995
+ this.#modelSpan?.error(options);
996
+ }
997
+ /**
998
+ * End the generation span
999
+ */
1000
+ endGeneration(options) {
1001
+ this.#modelSpan?.end(options);
1002
+ }
1003
+ /**
1004
+ * Update the generation span
1005
+ */
1006
+ updateGeneration(options) {
1007
+ this.#modelSpan?.update(options);
1008
+ }
1009
+ /**
1010
+ * Start a new Model execution step
1011
+ */
1012
+ #startStepSpan(payload) {
1013
+ this.#currentStepSpan = this.#modelSpan?.createChildSpan({
1014
+ name: `step: ${this.#stepIndex}`,
1015
+ type: observability.SpanType.MODEL_STEP,
1016
+ attributes: {
1017
+ stepIndex: this.#stepIndex,
1018
+ ...payload?.messageId ? { messageId: payload.messageId } : {},
1019
+ ...payload?.warnings?.length ? { warnings: payload.warnings } : {}
1020
+ },
1021
+ input: payload?.request
1022
+ });
1023
+ this.#chunkSequence = 0;
1024
+ }
1025
+ /**
1026
+ * End the current Model execution step with token usage, finish reason, output, and metadata
1027
+ */
1028
+ #endStepSpan(payload) {
1029
+ if (!this.#currentStepSpan) return;
1030
+ const output = payload.output;
1031
+ const { usage, ...otherOutput } = output;
1032
+ const stepResult = payload.stepResult;
1033
+ const metadata = payload.metadata;
1034
+ const cleanMetadata = metadata ? { ...metadata } : void 0;
1035
+ if (cleanMetadata?.request) {
1036
+ delete cleanMetadata.request;
1037
+ }
1038
+ this.#currentStepSpan.end({
1039
+ output: otherOutput,
1040
+ attributes: {
1041
+ usage,
1042
+ isContinued: stepResult.isContinued,
1043
+ finishReason: stepResult.reason,
1044
+ warnings: stepResult.warnings
1045
+ },
1046
+ metadata: {
1047
+ ...cleanMetadata
1048
+ }
1049
+ });
1050
+ this.#currentStepSpan = void 0;
1051
+ this.#stepIndex++;
1052
+ }
1053
+ /**
1054
+ * Create a new chunk span (for multi-part chunks like text-start/delta/end)
1055
+ */
1056
+ #startChunkSpan(chunkType, initialData) {
1057
+ if (!this.#currentStepSpan) {
1058
+ this.#startStepSpan();
1059
+ }
1060
+ this.#currentChunkSpan = this.#currentStepSpan?.createChildSpan({
1061
+ name: `chunk: '${chunkType}'`,
1062
+ type: observability.SpanType.MODEL_CHUNK,
1063
+ attributes: {
1064
+ chunkType,
1065
+ sequenceNumber: this.#chunkSequence
1066
+ }
1067
+ });
1068
+ this.#accumulator = initialData || {};
1069
+ }
1070
+ /**
1071
+ * Append string content to a specific field in the accumulator
1072
+ */
1073
+ #appendToAccumulator(field, text) {
1074
+ if (this.#accumulator[field] === void 0) {
1075
+ this.#accumulator[field] = text;
1076
+ } else {
1077
+ this.#accumulator[field] += text;
1078
+ }
1079
+ }
1080
+ /**
1081
+ * End the current chunk span.
1082
+ * Safe to call multiple times - will no-op if span already ended.
1083
+ */
1084
+ #endChunkSpan(output) {
1085
+ if (!this.#currentChunkSpan) return;
1086
+ this.#currentChunkSpan.end({
1087
+ output: output !== void 0 ? output : this.#accumulator
1088
+ });
1089
+ this.#currentChunkSpan = void 0;
1090
+ this.#accumulator = {};
1091
+ this.#chunkSequence++;
1092
+ }
1093
+ /**
1094
+ * Create an event span (for single chunks like tool-call)
1095
+ */
1096
+ #createEventSpan(chunkType, output) {
1097
+ if (!this.#currentStepSpan) {
1098
+ this.#startStepSpan();
1099
+ }
1100
+ const span = this.#currentStepSpan?.createEventSpan({
1101
+ name: `chunk: '${chunkType}'`,
1102
+ type: observability.SpanType.MODEL_CHUNK,
1103
+ attributes: {
1104
+ chunkType,
1105
+ sequenceNumber: this.#chunkSequence
1106
+ },
1107
+ output
1108
+ });
1109
+ if (span) {
1110
+ this.#chunkSequence++;
1111
+ }
1112
+ }
1113
+ /**
1114
+ * Check if there is currently an active chunk span
1115
+ */
1116
+ #hasActiveChunkSpan() {
1117
+ return !!this.#currentChunkSpan;
1118
+ }
1119
+ /**
1120
+ * Get the current accumulator value
1121
+ */
1122
+ #getAccumulator() {
1123
+ return this.#accumulator;
1124
+ }
1125
+ /**
1126
+ * Handle text chunk spans (text-start/delta/end)
1127
+ */
1128
+ #handleTextChunk(chunk) {
1129
+ switch (chunk.type) {
1130
+ case "text-start":
1131
+ this.#startChunkSpan("text");
1132
+ break;
1133
+ case "text-delta":
1134
+ this.#appendToAccumulator("text", chunk.payload.text);
1135
+ break;
1136
+ case "text-end": {
1137
+ this.#endChunkSpan();
1138
+ break;
1139
+ }
1140
+ }
1141
+ }
1142
+ /**
1143
+ * Handle reasoning chunk spans (reasoning-start/delta/end)
1144
+ */
1145
+ #handleReasoningChunk(chunk) {
1146
+ switch (chunk.type) {
1147
+ case "reasoning-start":
1148
+ this.#startChunkSpan("reasoning");
1149
+ break;
1150
+ case "reasoning-delta":
1151
+ this.#appendToAccumulator("text", chunk.payload.text);
1152
+ break;
1153
+ case "reasoning-end": {
1154
+ this.#endChunkSpan();
1155
+ break;
1156
+ }
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Handle tool call chunk spans (tool-call-input-streaming-start/delta/end, tool-call)
1161
+ */
1162
+ #handleToolCallChunk(chunk) {
1163
+ switch (chunk.type) {
1164
+ case "tool-call-input-streaming-start":
1165
+ this.#startChunkSpan("tool-call", {
1166
+ toolName: chunk.payload.toolName,
1167
+ toolCallId: chunk.payload.toolCallId
1168
+ });
1169
+ break;
1170
+ case "tool-call-delta":
1171
+ this.#appendToAccumulator("toolInput", chunk.payload.argsTextDelta);
1172
+ break;
1173
+ case "tool-call-input-streaming-end":
1174
+ case "tool-call": {
1175
+ const acc = this.#getAccumulator();
1176
+ let toolInput;
1177
+ try {
1178
+ toolInput = acc.toolInput ? JSON.parse(acc.toolInput) : {};
1179
+ } catch {
1180
+ toolInput = acc.toolInput;
1181
+ }
1182
+ this.#endChunkSpan({
1183
+ toolName: acc.toolName,
1184
+ toolCallId: acc.toolCallId,
1185
+ toolInput
1186
+ });
1187
+ break;
1188
+ }
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Handle object chunk spans (object, object-result)
1193
+ */
1194
+ #handleObjectChunk(chunk) {
1195
+ switch (chunk.type) {
1196
+ case "object":
1197
+ if (!this.#hasActiveChunkSpan()) {
1198
+ this.#startChunkSpan("object");
1199
+ }
1200
+ break;
1201
+ case "object-result":
1202
+ this.#endChunkSpan(chunk.object);
1203
+ break;
1204
+ }
1205
+ }
1206
+ /**
1207
+ * Handle tool-output chunks from sub-agents.
1208
+ * Consolidates streaming text/reasoning deltas into a single span per tool call.
1209
+ */
1210
+ #handleToolOutputChunk(chunk) {
1211
+ if (chunk.type !== "tool-output") return;
1212
+ const payload = chunk.payload;
1213
+ const { output, toolCallId, toolName } = payload;
1214
+ let acc = this.#toolOutputAccumulators.get(toolCallId);
1215
+ if (!acc) {
1216
+ if (!this.#currentStepSpan) {
1217
+ this.#startStepSpan();
1218
+ }
1219
+ acc = {
1220
+ toolName: toolName || "unknown",
1221
+ toolCallId,
1222
+ text: "",
1223
+ reasoning: "",
1224
+ sequenceNumber: this.#chunkSequence++,
1225
+ // Name the span 'tool-result' for consistency (tool-call → tool-result)
1226
+ span: this.#currentStepSpan?.createChildSpan({
1227
+ name: `chunk: 'tool-result'`,
1228
+ type: observability.SpanType.MODEL_CHUNK,
1229
+ attributes: {
1230
+ chunkType: "tool-result",
1231
+ sequenceNumber: this.#chunkSequence - 1
1232
+ }
1233
+ })
1234
+ };
1235
+ this.#toolOutputAccumulators.set(toolCallId, acc);
1236
+ }
1237
+ if (output && typeof output === "object" && "type" in output) {
1238
+ const innerType = output.type;
1239
+ switch (innerType) {
1240
+ case "text-delta":
1241
+ if (output.payload?.text) {
1242
+ acc.text += output.payload.text;
1243
+ }
1244
+ break;
1245
+ case "reasoning-delta":
1246
+ if (output.payload?.text) {
1247
+ acc.reasoning += output.payload.text;
1248
+ }
1249
+ break;
1250
+ case "finish":
1251
+ case "workflow-finish":
1252
+ this.#endToolOutputSpan(toolCallId);
1253
+ break;
1254
+ }
1255
+ }
1256
+ }
1257
+ /**
1258
+ * End a tool output span and clean up the accumulator
1259
+ */
1260
+ #endToolOutputSpan(toolCallId) {
1261
+ const acc = this.#toolOutputAccumulators.get(toolCallId);
1262
+ if (!acc) return;
1263
+ const output = {
1264
+ toolCallId: acc.toolCallId,
1265
+ toolName: acc.toolName
1266
+ };
1267
+ if (acc.text) {
1268
+ output.text = acc.text;
1269
+ }
1270
+ if (acc.reasoning) {
1271
+ output.reasoning = acc.reasoning;
1272
+ }
1273
+ acc.span?.end({ output });
1274
+ this.#toolOutputAccumulators.delete(toolCallId);
1275
+ this.#streamedToolCallIds.add(toolCallId);
1276
+ }
1277
+ /**
1278
+ * Wraps a stream with model tracing transform to track MODEL_STEP and MODEL_CHUNK spans.
1279
+ *
1280
+ * This should be added to the stream pipeline to automatically
1281
+ * create MODEL_STEP and MODEL_CHUNK spans for each semantic unit in the stream.
1282
+ */
1283
+ wrapStream(stream) {
1284
+ let captureCompletionStartTime = false;
1285
+ return stream.pipeThrough(
1286
+ new web.TransformStream({
1287
+ transform: (chunk, controller) => {
1288
+ if (!captureCompletionStartTime) {
1289
+ captureCompletionStartTime = true;
1290
+ this.#captureCompletionStartTime();
1291
+ }
1292
+ controller.enqueue(chunk);
1293
+ switch (chunk.type) {
1294
+ case "text-start":
1295
+ case "text-delta":
1296
+ case "text-end":
1297
+ this.#handleTextChunk(chunk);
1298
+ break;
1299
+ case "tool-call-input-streaming-start":
1300
+ case "tool-call-delta":
1301
+ case "tool-call-input-streaming-end":
1302
+ case "tool-call":
1303
+ this.#handleToolCallChunk(chunk);
1304
+ break;
1305
+ case "reasoning-start":
1306
+ case "reasoning-delta":
1307
+ case "reasoning-end":
1308
+ this.#handleReasoningChunk(chunk);
1309
+ break;
1310
+ case "object":
1311
+ case "object-result":
1312
+ this.#handleObjectChunk(chunk);
1313
+ break;
1314
+ case "step-start":
1315
+ this.#startStepSpan(chunk.payload);
1316
+ break;
1317
+ case "step-finish":
1318
+ this.#endStepSpan(chunk.payload);
1319
+ break;
1320
+ case "raw":
1321
+ // Skip raw chunks as they're redundant
1322
+ case "start":
1323
+ case "finish":
1324
+ break;
1325
+ case "tool-output":
1326
+ this.#handleToolOutputChunk(chunk);
1327
+ break;
1328
+ case "tool-result": {
1329
+ const toolCallId = chunk.payload?.toolCallId;
1330
+ if (toolCallId && this.#streamedToolCallIds.has(toolCallId)) {
1331
+ this.#streamedToolCallIds.delete(toolCallId);
1332
+ break;
1333
+ }
1334
+ const { args, ...cleanPayload } = chunk.payload || {};
1335
+ this.#createEventSpan(chunk.type, cleanPayload);
1336
+ break;
1337
+ }
1338
+ // Default: auto-create event span for all other chunk types
1339
+ default: {
1340
+ let outputPayload = chunk.payload;
1341
+ if (outputPayload && typeof outputPayload === "object" && "data" in outputPayload) {
1342
+ const typedPayload = outputPayload;
1343
+ outputPayload = { ...typedPayload };
1344
+ if (typedPayload.data) {
1345
+ outputPayload.size = typeof typedPayload.data === "string" ? typedPayload.data.length : typedPayload.data instanceof Uint8Array ? typedPayload.data.length : void 0;
1346
+ delete outputPayload.data;
1347
+ }
1348
+ }
1349
+ this.#createEventSpan(chunk.type, outputPayload);
1350
+ break;
1351
+ }
1352
+ }
1353
+ }
1354
+ })
1355
+ );
1356
+ }
1357
+ };
1358
+
1359
+ // src/spans/base.ts
1360
+ function isSpanInternal(spanType, flags) {
1361
+ if (flags === void 0 || flags === observability.InternalSpans.NONE) {
1362
+ return false;
1363
+ }
1364
+ switch (spanType) {
1365
+ // Workflow-related spans
1366
+ case observability.SpanType.WORKFLOW_RUN:
1367
+ case observability.SpanType.WORKFLOW_STEP:
1368
+ case observability.SpanType.WORKFLOW_CONDITIONAL:
1369
+ case observability.SpanType.WORKFLOW_CONDITIONAL_EVAL:
1370
+ case observability.SpanType.WORKFLOW_PARALLEL:
1371
+ case observability.SpanType.WORKFLOW_LOOP:
1372
+ case observability.SpanType.WORKFLOW_SLEEP:
1373
+ case observability.SpanType.WORKFLOW_WAIT_EVENT:
1374
+ return (flags & observability.InternalSpans.WORKFLOW) !== 0;
1375
+ // Agent-related spans
1376
+ case observability.SpanType.AGENT_RUN:
1377
+ return (flags & observability.InternalSpans.AGENT) !== 0;
1378
+ // Tool-related spans
1379
+ case observability.SpanType.TOOL_CALL:
1380
+ case observability.SpanType.MCP_TOOL_CALL:
1381
+ return (flags & observability.InternalSpans.TOOL) !== 0;
1382
+ // Model-related spans
1383
+ case observability.SpanType.MODEL_GENERATION:
1384
+ case observability.SpanType.MODEL_STEP:
1385
+ case observability.SpanType.MODEL_CHUNK:
1386
+ return (flags & observability.InternalSpans.MODEL) !== 0;
1387
+ // Default: never internal
1388
+ default:
1389
+ return false;
1390
+ }
1391
+ }
1392
+ function getExternalParentId(options) {
1393
+ if (!options.parent) {
1394
+ return void 0;
1395
+ }
1396
+ if (options.parent.isInternal) {
1397
+ return options.parent.getParentSpanId(false);
1398
+ } else {
1399
+ return options.parent.id;
1400
+ }
1401
+ }
1402
+ var BaseSpan = class {
1403
+ name;
1404
+ type;
1405
+ attributes;
1406
+ parent;
1407
+ startTime;
1408
+ endTime;
1409
+ isEvent;
1410
+ isInternal;
1411
+ observabilityInstance;
1412
+ input;
1413
+ output;
1414
+ errorInfo;
1415
+ metadata;
1416
+ tags;
1417
+ traceState;
1418
+ /** Parent span ID (for root spans that are children of external spans) */
1419
+ parentSpanId;
1420
+ constructor(options, observabilityInstance) {
1421
+ this.name = options.name;
1422
+ this.type = options.type;
1423
+ this.attributes = deepClean(options.attributes) || {};
1424
+ this.metadata = deepClean(options.metadata);
1425
+ this.parent = options.parent;
1426
+ this.startTime = /* @__PURE__ */ new Date();
1427
+ this.observabilityInstance = observabilityInstance;
1428
+ this.isEvent = options.isEvent ?? false;
1429
+ this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
1430
+ this.traceState = options.traceState;
1431
+ this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
1432
+ if (this.isEvent) {
1433
+ this.output = deepClean(options.output);
1434
+ } else {
1435
+ this.input = deepClean(options.input);
1436
+ }
1437
+ }
1438
+ createChildSpan(options) {
1439
+ return this.observabilityInstance.startSpan({ ...options, parent: this, isEvent: false });
1440
+ }
1441
+ createEventSpan(options) {
1442
+ return this.observabilityInstance.startSpan({ ...options, parent: this, isEvent: true });
1443
+ }
1444
+ /**
1445
+ * Create a ModelSpanTracker for this span (only works if this is a MODEL_GENERATION span)
1446
+ * Returns undefined for non-MODEL_GENERATION spans
1447
+ */
1448
+ createTracker() {
1449
+ if (this.type !== observability.SpanType.MODEL_GENERATION) {
1450
+ return void 0;
1451
+ }
1452
+ return new ModelSpanTracker(this);
1453
+ }
1454
+ /** Returns `TRUE` if the span is the root span of a trace */
1455
+ get isRootSpan() {
1456
+ return !this.parent;
1457
+ }
1458
+ /** Get the closest parent spanId that isn't an internal span */
1459
+ getParentSpanId(includeInternalSpans) {
1460
+ if (!this.parent) {
1461
+ return this.parentSpanId;
1462
+ }
1463
+ if (includeInternalSpans) return this.parent.id;
1464
+ if (this.parent.isInternal) return this.parent.getParentSpanId(includeInternalSpans);
1465
+ return this.parent.id;
1466
+ }
1467
+ /** Find the closest parent span of a specific type by walking up the parent chain */
1468
+ findParent(spanType) {
1469
+ let current = this.parent;
1470
+ while (current) {
1471
+ if (current.type === spanType) {
1472
+ return current;
1473
+ }
1474
+ current = current.parent;
1475
+ }
1476
+ return void 0;
1477
+ }
1478
+ /** Returns a lightweight span ready for export */
1479
+ exportSpan(includeInternalSpans) {
1480
+ return {
1481
+ id: this.id,
1482
+ traceId: this.traceId,
1483
+ name: this.name,
1484
+ type: this.type,
1485
+ attributes: this.attributes,
1486
+ metadata: this.metadata,
1487
+ startTime: this.startTime,
1488
+ endTime: this.endTime,
1489
+ input: this.input,
1490
+ output: this.output,
1491
+ errorInfo: this.errorInfo,
1492
+ isEvent: this.isEvent,
1493
+ isRootSpan: this.isRootSpan,
1494
+ parentSpanId: this.getParentSpanId(includeInternalSpans),
1495
+ // Tags are only included for root spans
1496
+ ...this.isRootSpan && this.tags?.length ? { tags: this.tags } : {}
1497
+ };
1498
+ }
1499
+ get externalTraceId() {
1500
+ return this.isValid ? this.traceId : void 0;
1501
+ }
1502
+ /**
1503
+ * Execute an async function within this span's tracing context.
1504
+ * Delegates to the bridge if available.
1505
+ */
1506
+ async executeInContext(fn) {
1507
+ const bridge = this.observabilityInstance.getBridge();
1508
+ if (bridge?.executeInContext) {
1509
+ return bridge.executeInContext(this.id, fn);
1510
+ }
1511
+ return fn();
1512
+ }
1513
+ /**
1514
+ * Execute a synchronous function within this span's tracing context.
1515
+ * Delegates to the bridge if available.
1516
+ */
1517
+ executeInContextSync(fn) {
1518
+ const bridge = this.observabilityInstance.getBridge();
1519
+ if (bridge?.executeInContextSync) {
1520
+ return bridge.executeInContextSync(this.id, fn);
1521
+ }
1522
+ return fn();
1523
+ }
1524
+ };
1525
+ var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
1526
+ "logger",
1527
+ "experimental_providerMetadata",
1528
+ "providerMetadata",
1529
+ "steps",
1530
+ "tracingContext"
1531
+ ]);
1532
+ function deepClean(value, options = {}, _seen = /* @__PURE__ */ new WeakSet(), _depth = 0) {
1533
+ const { keysToStrip = DEFAULT_KEYS_TO_STRIP, maxDepth = 10 } = options;
1534
+ if (_depth > maxDepth) {
1535
+ return "[MaxDepth]";
1536
+ }
1537
+ if (value === null || typeof value !== "object") {
1538
+ try {
1539
+ JSON.stringify(value);
1540
+ return value;
1541
+ } catch (error) {
1542
+ return `[${error instanceof Error ? error.message : String(error)}]`;
1543
+ }
1544
+ }
1545
+ if (_seen.has(value)) {
1546
+ return "[Circular]";
1547
+ }
1548
+ _seen.add(value);
1549
+ if (Array.isArray(value)) {
1550
+ return value.map((item) => deepClean(item, options, _seen, _depth + 1));
1551
+ }
1552
+ const cleaned = {};
1553
+ for (const [key, val] of Object.entries(value)) {
1554
+ if (keysToStrip.has(key)) {
1555
+ continue;
1556
+ }
1557
+ try {
1558
+ cleaned[key] = deepClean(val, options, _seen, _depth + 1);
1559
+ } catch (error) {
1560
+ cleaned[key] = `[${error instanceof Error ? error.message : String(error)}]`;
1561
+ }
1562
+ }
1563
+ return cleaned;
1564
+ }
1565
+ var DefaultSpan = class extends BaseSpan {
1566
+ id;
1567
+ traceId;
1568
+ constructor(options, observabilityInstance) {
1569
+ super(options, observabilityInstance);
1570
+ const bridge = observabilityInstance.getBridge();
1571
+ if (bridge && !this.isInternal) {
1572
+ const bridgeIds = bridge.createSpan(options);
1573
+ if (bridgeIds) {
1574
+ this.id = bridgeIds.spanId;
1575
+ this.traceId = bridgeIds.traceId;
1576
+ this.parentSpanId = bridgeIds.parentSpanId;
1577
+ return;
1578
+ }
1579
+ }
1580
+ if (options.parent) {
1581
+ this.traceId = options.parent.traceId;
1582
+ this.parentSpanId = options.parent.id;
1583
+ this.id = generateSpanId();
1584
+ return;
1585
+ }
1586
+ this.traceId = getOrCreateTraceId(options);
1587
+ this.id = generateSpanId();
1588
+ if (options.parentSpanId) {
1589
+ if (isValidSpanId(options.parentSpanId)) {
1590
+ this.parentSpanId = options.parentSpanId;
1591
+ } else {
1592
+ console.error(
1593
+ `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring.`
1594
+ );
1595
+ }
1596
+ }
1597
+ }
1598
+ end(options) {
1599
+ if (this.isEvent) {
1600
+ return;
1601
+ }
1602
+ this.endTime = /* @__PURE__ */ new Date();
1603
+ if (options?.output !== void 0) {
1604
+ this.output = deepClean(options.output);
1605
+ }
1606
+ if (options?.attributes) {
1607
+ this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
1608
+ }
1609
+ if (options?.metadata) {
1610
+ this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
1611
+ }
1612
+ }
1613
+ error(options) {
1614
+ if (this.isEvent) {
1615
+ return;
1616
+ }
1617
+ const { error: error$1, endSpan = true, attributes, metadata } = options;
1618
+ this.errorInfo = error$1 instanceof error.MastraError ? {
1619
+ id: error$1.id,
1620
+ details: error$1.details,
1621
+ category: error$1.category,
1622
+ domain: error$1.domain,
1623
+ message: error$1.message
1624
+ } : {
1625
+ message: error$1.message
1626
+ };
1627
+ if (attributes) {
1628
+ this.attributes = { ...this.attributes, ...deepClean(attributes) };
1629
+ }
1630
+ if (metadata) {
1631
+ this.metadata = { ...this.metadata, ...deepClean(metadata) };
1632
+ }
1633
+ if (endSpan) {
1634
+ this.end();
1635
+ } else {
1636
+ this.update({});
1637
+ }
1638
+ }
1639
+ update(options) {
1640
+ if (this.isEvent) {
1641
+ return;
1642
+ }
1643
+ if (options.input !== void 0) {
1644
+ this.input = deepClean(options.input);
1645
+ }
1646
+ if (options.output !== void 0) {
1647
+ this.output = deepClean(options.output);
1648
+ }
1649
+ if (options.attributes) {
1650
+ this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
1651
+ }
1652
+ if (options.metadata) {
1653
+ this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
1654
+ }
1655
+ }
1656
+ get isValid() {
1657
+ return true;
1658
+ }
1659
+ async export() {
1660
+ return JSON.stringify({
1661
+ spanId: this.id,
1662
+ traceId: this.traceId,
1663
+ startTime: this.startTime,
1664
+ endTime: this.endTime,
1665
+ attributes: this.attributes,
1666
+ metadata: this.metadata
1667
+ });
1668
+ }
1669
+ };
1670
+ function generateSpanId() {
1671
+ const bytes = new Uint8Array(8);
1672
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
1673
+ crypto.getRandomValues(bytes);
1674
+ } else {
1675
+ for (let i = 0; i < 8; i++) {
1676
+ bytes[i] = Math.floor(Math.random() * 256);
1677
+ }
1678
+ }
1679
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
1680
+ }
1681
+ function generateTraceId() {
1682
+ const bytes = new Uint8Array(16);
1683
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
1684
+ crypto.getRandomValues(bytes);
1685
+ } else {
1686
+ for (let i = 0; i < 16; i++) {
1687
+ bytes[i] = Math.floor(Math.random() * 256);
1688
+ }
1689
+ }
1690
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
1691
+ }
1692
+ function isValidTraceId(traceId) {
1693
+ return /^[0-9a-f]{1,32}$/i.test(traceId);
1694
+ }
1695
+ function isValidSpanId(spanId) {
1696
+ return /^[0-9a-f]{1,16}$/i.test(spanId);
1697
+ }
1698
+ function getOrCreateTraceId(options) {
1699
+ if (options.traceId) {
1700
+ if (isValidTraceId(options.traceId)) {
1701
+ return options.traceId;
1702
+ } else {
1703
+ console.error(
1704
+ `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1705
+ );
1706
+ }
1707
+ }
1708
+ return generateTraceId();
1709
+ }
1710
+
1711
+ // src/spans/no-op.ts
1712
+ var NoOpSpan = class extends BaseSpan {
1713
+ id;
1714
+ traceId;
1715
+ constructor(options, observabilityInstance) {
1716
+ super(options, observabilityInstance);
1717
+ this.id = "no-op";
1718
+ this.traceId = "no-op-trace";
1719
+ }
1720
+ end(_options) {
1721
+ }
1722
+ error(_options) {
1723
+ }
1724
+ update(_options) {
1725
+ }
1726
+ get isValid() {
1727
+ return false;
1728
+ }
1729
+ };
1730
+
1731
+ // src/instances/base.ts
1732
+ var BaseObservabilityInstance = class extends base.MastraBase {
1733
+ config;
1734
+ constructor(config) {
1735
+ super({ component: logger.RegisteredLogger.OBSERVABILITY, name: config.serviceName });
1736
+ this.config = {
1737
+ serviceName: config.serviceName,
1738
+ name: config.name,
1739
+ sampling: config.sampling ?? { type: "always" /* ALWAYS */ },
1740
+ exporters: config.exporters ?? [],
1741
+ spanOutputProcessors: config.spanOutputProcessors ?? [],
1742
+ bridge: config.bridge ?? void 0,
1743
+ includeInternalSpans: config.includeInternalSpans ?? false,
1744
+ requestContextKeys: config.requestContextKeys ?? []
1745
+ };
1746
+ if (this.config.bridge?.init) {
1747
+ this.config.bridge.init({ config: this.config });
1748
+ }
1749
+ }
1750
+ /**
1751
+ * Override setLogger to add Observability specific initialization log
1752
+ * and propagate logger to exporters and bridge
1753
+ */
1754
+ __setLogger(logger) {
1755
+ super.__setLogger(logger);
1756
+ this.exporters.forEach((exporter) => {
1757
+ if (typeof exporter.__setLogger === "function") {
1758
+ exporter.__setLogger(logger);
1759
+ }
1760
+ });
1761
+ if (this.config.bridge?.__setLogger) {
1762
+ this.config.bridge.__setLogger(logger);
1763
+ }
1764
+ this.logger.debug(
1765
+ `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling?.type}] [bridge=${!!this.config.bridge}]`
1766
+ );
1767
+ }
1768
+ // ============================================================================
1769
+ // Protected getters for clean config access
1770
+ // ============================================================================
1771
+ get exporters() {
1772
+ return this.config.exporters || [];
1773
+ }
1774
+ get spanOutputProcessors() {
1775
+ return this.config.spanOutputProcessors || [];
1776
+ }
1777
+ // ============================================================================
1778
+ // Public API - Single type-safe span creation method
1779
+ // ============================================================================
1780
+ /**
1781
+ * Start a new span of a specific SpanType
1782
+ */
1783
+ startSpan(options) {
1784
+ const { customSamplerOptions, requestContext, metadata, tracingOptions, ...rest } = options;
1785
+ if (!this.shouldSample(customSamplerOptions)) {
1786
+ return new NoOpSpan({ ...rest, metadata }, this);
1787
+ }
1788
+ let traceState;
1789
+ if (options.parent) {
1790
+ traceState = options.parent.traceState;
1791
+ } else {
1792
+ traceState = this.computeTraceState(tracingOptions);
1793
+ }
1794
+ const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
1795
+ const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
1796
+ const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
1797
+ const tags = !options.parent ? tracingOptions?.tags : void 0;
1798
+ const span = this.createSpan({
1799
+ ...rest,
1800
+ metadata: enrichedMetadata,
1801
+ traceState,
1802
+ tags
1803
+ });
1804
+ if (span.isEvent) {
1805
+ this.emitSpanEnded(span);
1806
+ } else {
1807
+ this.wireSpanLifecycle(span);
1808
+ this.emitSpanStarted(span);
1809
+ }
1810
+ return span;
1811
+ }
1812
+ // ============================================================================
1813
+ // Configuration Management
1814
+ // ============================================================================
1815
+ /**
1816
+ * Get current configuration
1817
+ */
1818
+ getConfig() {
1819
+ return { ...this.config };
1820
+ }
1821
+ // ============================================================================
1822
+ // Plugin Access
1823
+ // ============================================================================
1824
+ /**
1825
+ * Get all exporters
1826
+ */
1827
+ getExporters() {
1828
+ return [...this.exporters];
1829
+ }
1830
+ /**
1831
+ * Get all span output processors
1832
+ */
1833
+ getSpanOutputProcessors() {
1834
+ return [...this.spanOutputProcessors];
1835
+ }
1836
+ /**
1837
+ * Get the bridge instance if configured
1838
+ */
1839
+ getBridge() {
1840
+ return this.config.bridge;
1841
+ }
1842
+ /**
1843
+ * Get the logger instance (for exporters and other components)
1844
+ */
1845
+ getLogger() {
1846
+ return this.logger;
1847
+ }
1848
+ // ============================================================================
1849
+ // Span Lifecycle Management
1850
+ // ============================================================================
1851
+ /**
1852
+ * Automatically wires up Observability lifecycle events for any span
1853
+ * This ensures all spans emit events regardless of implementation
1854
+ */
1855
+ wireSpanLifecycle(span) {
1856
+ if (!this.config.includeInternalSpans && span.isInternal) {
1857
+ return;
1858
+ }
1859
+ const originalEnd = span.end.bind(span);
1860
+ const originalUpdate = span.update.bind(span);
1861
+ span.end = (options) => {
1862
+ if (span.isEvent) {
1863
+ this.logger.warn(`End event is not available on event spans`);
1864
+ return;
1865
+ }
1866
+ originalEnd(options);
1867
+ this.emitSpanEnded(span);
1868
+ };
1869
+ span.update = (options) => {
1870
+ if (span.isEvent) {
1871
+ this.logger.warn(`Update() is not available on event spans`);
1872
+ return;
1873
+ }
1874
+ originalUpdate(options);
1875
+ this.emitSpanUpdated(span);
1876
+ };
1877
+ }
1878
+ // ============================================================================
1879
+ // Utility Methods
1880
+ // ============================================================================
1881
+ /**
1882
+ * Check if a trace should be sampled
1883
+ */
1884
+ shouldSample(options) {
1885
+ const { sampling } = this.config;
1886
+ switch (sampling?.type) {
1887
+ case void 0:
1888
+ return true;
1889
+ case "always" /* ALWAYS */:
1890
+ return true;
1891
+ case "never" /* NEVER */:
1892
+ return false;
1893
+ case "ratio" /* RATIO */:
1894
+ if (sampling.probability === void 0 || sampling.probability < 0 || sampling.probability > 1) {
1895
+ this.logger.warn(
1896
+ `Invalid sampling probability: ${sampling.probability}. Expected value between 0 and 1. Defaulting to no sampling.`
1897
+ );
1898
+ return false;
1899
+ }
1900
+ return Math.random() < sampling.probability;
1901
+ case "custom" /* CUSTOM */:
1902
+ return sampling.sampler(options);
1903
+ default:
1904
+ throw new Error(`Sampling strategy type not implemented: ${sampling.type}`);
1905
+ }
1906
+ }
1907
+ /**
1908
+ * Compute TraceState for a new trace based on configured and per-request keys
1909
+ */
1910
+ computeTraceState(tracingOptions) {
1911
+ const configuredKeys = this.config.requestContextKeys ?? [];
1912
+ const additionalKeys = tracingOptions?.requestContextKeys ?? [];
1913
+ const allKeys = [...configuredKeys, ...additionalKeys];
1914
+ if (allKeys.length === 0) {
1915
+ return void 0;
1916
+ }
1917
+ return {
1918
+ requestContextKeys: allKeys
1919
+ };
1920
+ }
1921
+ /**
1922
+ * Extract metadata from RequestContext using TraceState
1923
+ */
1924
+ extractMetadataFromRequestContext(requestContext, explicitMetadata, traceState) {
1925
+ if (!requestContext || !traceState || traceState.requestContextKeys.length === 0) {
1926
+ return explicitMetadata;
1927
+ }
1928
+ const extracted = this.extractKeys(requestContext, traceState.requestContextKeys);
1929
+ if (Object.keys(extracted).length === 0 && !explicitMetadata) {
1930
+ return void 0;
1931
+ }
1932
+ return {
1933
+ ...extracted,
1934
+ ...explicitMetadata
1935
+ // Explicit metadata always wins
1936
+ };
1937
+ }
1938
+ /**
1939
+ * Extract specific keys from RequestContext
1940
+ */
1941
+ extractKeys(requestContext, keys) {
1942
+ const result = {};
1943
+ for (const key of keys) {
1944
+ const parts = key.split(".");
1945
+ const rootKey = parts[0];
1946
+ const value = requestContext.get(rootKey);
1947
+ if (value !== void 0) {
1948
+ if (parts.length > 1) {
1949
+ const nestedPath = parts.slice(1).join(".");
1950
+ const nestedValue = utils.getNestedValue(value, nestedPath);
1951
+ if (nestedValue !== void 0) {
1952
+ utils.setNestedValue(result, key, nestedValue);
1953
+ }
1954
+ } else {
1955
+ utils.setNestedValue(result, key, value);
1956
+ }
1957
+ }
1958
+ }
1959
+ return result;
1960
+ }
1961
+ /**
1962
+ * Process a span through all output processors
1963
+ */
1964
+ processSpan(span) {
1965
+ for (const processor of this.spanOutputProcessors) {
1966
+ if (!span) {
1967
+ break;
1968
+ }
1969
+ try {
1970
+ span = processor.process(span);
1971
+ } catch (error) {
1972
+ this.logger.error(`[Observability] Processor error [name=${processor.name}]`, error);
1973
+ }
1974
+ }
1975
+ return span;
1976
+ }
1977
+ // ============================================================================
1978
+ // Event-driven Export Methods
1979
+ // ============================================================================
1980
+ getSpanForExport(span) {
1981
+ if (!span.isValid) return void 0;
1982
+ if (span.isInternal && !this.config.includeInternalSpans) return void 0;
1983
+ const processedSpan = this.processSpan(span);
1984
+ return processedSpan?.exportSpan(this.config.includeInternalSpans);
1985
+ }
1986
+ /**
1987
+ * Emit a span started event
1988
+ */
1989
+ emitSpanStarted(span) {
1990
+ const exportedSpan = this.getSpanForExport(span);
1991
+ if (exportedSpan) {
1992
+ this.exportTracingEvent({ type: observability.TracingEventType.SPAN_STARTED, exportedSpan }).catch((error) => {
1993
+ this.logger.error("[Observability] Failed to export span_started event", error);
1994
+ });
1995
+ }
1996
+ }
1997
+ /**
1998
+ * Emit a span ended event (called automatically when spans end)
1999
+ */
2000
+ emitSpanEnded(span) {
2001
+ const exportedSpan = this.getSpanForExport(span);
2002
+ if (exportedSpan) {
2003
+ this.exportTracingEvent({ type: observability.TracingEventType.SPAN_ENDED, exportedSpan }).catch((error) => {
2004
+ this.logger.error("[Observability] Failed to export span_ended event", error);
2005
+ });
2006
+ }
2007
+ }
2008
+ /**
2009
+ * Emit a span updated event
2010
+ */
2011
+ emitSpanUpdated(span) {
2012
+ const exportedSpan = this.getSpanForExport(span);
2013
+ if (exportedSpan) {
2014
+ this.exportTracingEvent({ type: observability.TracingEventType.SPAN_UPDATED, exportedSpan }).catch((error) => {
2015
+ this.logger.error("[Observability] Failed to export span_updated event", error);
2016
+ });
2017
+ }
2018
+ }
2019
+ /**
2020
+ * Export tracing event through all exporters and bridge (realtime mode)
2021
+ */
2022
+ async exportTracingEvent(event) {
2023
+ const targets = [
2024
+ ...this.exporters
2025
+ ];
2026
+ if (this.config.bridge) {
2027
+ targets.push(this.config.bridge);
2028
+ }
2029
+ const exportPromises = targets.map(async (target) => {
2030
+ try {
2031
+ await target.exportTracingEvent(event);
2032
+ this.logger.debug(`[Observability] Event exported [target=${target.name}] [type=${event.type}]`);
2033
+ } catch (error) {
2034
+ this.logger.error(`[Observability] Export error [target=${target.name}]`, error);
2035
+ }
2036
+ });
2037
+ await Promise.allSettled(exportPromises);
2038
+ }
2039
+ // ============================================================================
2040
+ // Lifecycle Management
2041
+ // ============================================================================
2042
+ /**
2043
+ * Initialize Observability (called by Mastra during component registration)
2044
+ */
2045
+ init() {
2046
+ this.logger.debug(`[Observability] Initialization started [name=${this.name}]`);
2047
+ this.logger.info(`[Observability] Initialized successfully [name=${this.name}]`);
2048
+ }
2049
+ /**
2050
+ * Shutdown Observability and clean up resources
2051
+ */
2052
+ async shutdown() {
2053
+ this.logger.debug(`[Observability] Shutdown started [name=${this.name}]`);
2054
+ const shutdownPromises = [
2055
+ ...this.exporters.map((e) => e.shutdown()),
2056
+ ...this.spanOutputProcessors.map((p) => p.shutdown())
2057
+ ];
2058
+ if (this.config.bridge) {
2059
+ shutdownPromises.push(this.config.bridge.shutdown());
2060
+ }
2061
+ await Promise.allSettled(shutdownPromises);
2062
+ this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
2063
+ }
2064
+ };
2065
+
2066
+ // src/instances/default.ts
2067
+ var DefaultObservabilityInstance = class extends BaseObservabilityInstance {
2068
+ constructor(config) {
2069
+ super(config);
2070
+ }
2071
+ createSpan(options) {
2072
+ return new DefaultSpan(options, this);
2073
+ }
2074
+ };
2075
+
2076
+ // src/registry.ts
2077
+ var ObservabilityRegistry = class {
2078
+ #instances = /* @__PURE__ */ new Map();
2079
+ #defaultInstance;
2080
+ #configSelector;
2081
+ /**
2082
+ * Register a tracing instance
2083
+ */
2084
+ register(name, instance, isDefault = false) {
2085
+ if (this.#instances.has(name)) {
2086
+ throw new Error(`Tracing instance '${name}' already registered`);
2087
+ }
2088
+ this.#instances.set(name, instance);
2089
+ if (isDefault || !this.#defaultInstance) {
2090
+ this.#defaultInstance = instance;
2091
+ }
2092
+ }
2093
+ /**
2094
+ * Get a tracing instance by name
2095
+ */
2096
+ get(name) {
2097
+ return this.#instances.get(name);
2098
+ }
2099
+ /**
2100
+ * Get the default tracing instance
2101
+ */
2102
+ getDefault() {
2103
+ return this.#defaultInstance;
2104
+ }
2105
+ /**
2106
+ * Set the tracing selector function
2107
+ */
2108
+ setSelector(selector) {
2109
+ this.#configSelector = selector;
2110
+ }
2111
+ /**
2112
+ * Get the selected tracing instance based on context
2113
+ */
2114
+ getSelected(options) {
2115
+ if (this.#configSelector) {
2116
+ const selected = this.#configSelector(options, this.#instances);
2117
+ if (selected && this.#instances.has(selected)) {
2118
+ return this.#instances.get(selected);
2119
+ }
2120
+ }
2121
+ return this.#defaultInstance;
2122
+ }
2123
+ /**
2124
+ * Unregister a tracing instance
2125
+ */
2126
+ unregister(name) {
2127
+ const instance = this.#instances.get(name);
2128
+ const deleted = this.#instances.delete(name);
2129
+ if (deleted && instance === this.#defaultInstance) {
2130
+ const next = this.#instances.values().next();
2131
+ this.#defaultInstance = next.done ? void 0 : next.value;
2132
+ }
2133
+ return deleted;
2134
+ }
2135
+ /**
2136
+ * Shutdown all instances and clear the registry
2137
+ */
2138
+ async shutdown() {
2139
+ const shutdownPromises = Array.from(this.#instances.values()).map((instance) => instance.shutdown());
2140
+ await Promise.allSettled(shutdownPromises);
2141
+ this.#instances.clear();
2142
+ this.#instances.clear();
2143
+ this.#defaultInstance = void 0;
2144
+ this.#configSelector = void 0;
2145
+ }
2146
+ /**
2147
+ * Clear all instances without shutdown
2148
+ */
2149
+ clear() {
2150
+ this.#instances.clear();
2151
+ this.#defaultInstance = void 0;
2152
+ this.#configSelector = void 0;
2153
+ }
2154
+ /**
2155
+ * list all registered instances
2156
+ */
2157
+ list() {
2158
+ return new Map(this.#instances);
2159
+ }
2160
+ };
2161
+
2162
+ // src/span_processors/sensitive-data-filter.ts
2163
+ var SensitiveDataFilter = class {
2164
+ name = "sensitive-data-filter";
2165
+ sensitiveFields;
2166
+ redactionToken;
2167
+ redactionStyle;
2168
+ constructor(options = {}) {
2169
+ this.sensitiveFields = (options.sensitiveFields || [
2170
+ "password",
2171
+ "token",
2172
+ "secret",
2173
+ "key",
2174
+ "apikey",
2175
+ "auth",
2176
+ "authorization",
2177
+ "bearer",
2178
+ "bearertoken",
2179
+ "jwt",
2180
+ "credential",
2181
+ "clientsecret",
2182
+ "privatekey",
2183
+ "refresh",
2184
+ "ssn"
2185
+ ]).map((f) => this.normalizeKey(f));
2186
+ this.redactionToken = options.redactionToken ?? "[REDACTED]";
2187
+ this.redactionStyle = options.redactionStyle ?? "full";
2188
+ }
2189
+ /**
2190
+ * Process a span by filtering sensitive data across its key fields.
2191
+ * Fields processed: attributes, metadata, input, output, errorInfo.
2192
+ *
2193
+ * @param span - The input span to filter
2194
+ * @returns A new span with sensitive values redacted
2195
+ */
2196
+ process(span) {
2197
+ span.attributes = this.tryFilter(span.attributes);
2198
+ span.metadata = this.tryFilter(span.metadata);
2199
+ span.input = this.tryFilter(span.input);
2200
+ span.output = this.tryFilter(span.output);
2201
+ span.errorInfo = this.tryFilter(span.errorInfo);
2202
+ return span;
2203
+ }
2204
+ /**
2205
+ * Recursively filter objects/arrays for sensitive keys.
2206
+ * Handles circular references by replacing with a marker.
2207
+ * Also attempts to parse and redact JSON strings.
2208
+ */
2209
+ deepFilter(obj, seen = /* @__PURE__ */ new WeakSet()) {
2210
+ if (obj === null || typeof obj !== "object") {
2211
+ if (typeof obj === "string") {
2212
+ const trimmed = obj.trim();
2213
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2214
+ return this.redactJsonString(obj);
2215
+ }
2216
+ }
2217
+ return obj;
2218
+ }
2219
+ if (seen.has(obj)) {
2220
+ return "[Circular Reference]";
2221
+ }
2222
+ seen.add(obj);
2223
+ if (Array.isArray(obj)) {
2224
+ return obj.map((item) => this.deepFilter(item, seen));
2225
+ }
2226
+ const filtered = {};
2227
+ for (const key of Object.keys(obj)) {
2228
+ const normKey = this.normalizeKey(key);
2229
+ if (this.isSensitive(normKey)) {
2230
+ if (obj[key] && typeof obj[key] === "object") {
2231
+ filtered[key] = this.deepFilter(obj[key], seen);
2232
+ } else {
2233
+ filtered[key] = this.redactValue(obj[key]);
2234
+ }
2235
+ } else {
2236
+ filtered[key] = this.deepFilter(obj[key], seen);
2237
+ }
2238
+ }
2239
+ return filtered;
2240
+ }
2241
+ tryFilter(value) {
2242
+ try {
2243
+ return this.deepFilter(value);
2244
+ } catch {
2245
+ return { error: { processor: this.name } };
2246
+ }
2247
+ }
2248
+ /**
2249
+ * Normalize keys by lowercasing and stripping non-alphanumeric characters.
2250
+ * Ensures consistent matching for variants like "api-key", "api_key", "Api Key".
2251
+ */
2252
+ normalizeKey(key) {
2253
+ return key.toLowerCase().replace(/[^a-z0-9]/g, "");
2254
+ }
2255
+ /**
2256
+ * Check whether a normalized key exactly matches any sensitive field.
2257
+ * Both key and sensitive fields are normalized by removing all non-alphanumeric
2258
+ * characters and converting to lowercase before comparison.
2259
+ *
2260
+ * Examples:
2261
+ * - "api_key", "api-key", "ApiKey" all normalize to "apikey" → MATCHES "apikey"
2262
+ * - "promptTokens", "prompt_tokens" normalize to "prompttokens" → DOES NOT MATCH "token"
2263
+ */
2264
+ isSensitive(normalizedKey) {
2265
+ return this.sensitiveFields.some((sensitiveField) => {
2266
+ return normalizedKey === sensitiveField;
2267
+ });
2268
+ }
2269
+ /**
2270
+ * Attempt to parse a string as JSON and redact sensitive fields within it.
2271
+ * If parsing fails or no sensitive data is found, returns the original string.
2272
+ */
2273
+ redactJsonString(str) {
2274
+ try {
2275
+ const parsed = JSON.parse(str);
2276
+ if (parsed && typeof parsed === "object") {
2277
+ const filtered = this.deepFilter(parsed, /* @__PURE__ */ new WeakSet());
2278
+ return JSON.stringify(filtered);
2279
+ }
2280
+ return str;
2281
+ } catch {
2282
+ return str;
2283
+ }
2284
+ }
2285
+ /**
2286
+ * Redact a sensitive value.
2287
+ * - Full style: replaces with a fixed token.
2288
+ * - Partial style: shows 3 chars at start and end, hides the middle.
2289
+ *
2290
+ * Non-string values are converted to strings before partial redaction.
2291
+ */
2292
+ redactValue(value) {
2293
+ if (this.redactionStyle === "full") {
2294
+ return this.redactionToken;
2295
+ }
2296
+ const str = String(value);
2297
+ const len = str.length;
2298
+ if (len <= 6) {
2299
+ return this.redactionToken;
2300
+ }
2301
+ return str.slice(0, 3) + "\u2026" + str.slice(len - 3);
2302
+ }
2303
+ async shutdown() {
2304
+ }
2305
+ };
2306
+
2307
+ // src/default.ts
2308
+ function isInstance(obj) {
2309
+ return obj instanceof BaseObservabilityInstance;
2310
+ }
2311
+ var Observability = class extends base.MastraBase {
2312
+ #registry = new ObservabilityRegistry();
2313
+ constructor(config) {
2314
+ super({
2315
+ component: logger.RegisteredLogger.OBSERVABILITY,
2316
+ name: "Observability"
2317
+ });
2318
+ if (config === void 0) {
2319
+ config = {};
2320
+ }
2321
+ const validationResult = observabilityRegistryConfigSchema.safeParse(config);
2322
+ if (!validationResult.success) {
2323
+ const errorMessages = validationResult.error.errors.map((err) => `${err.path.join(".") || "config"}: ${err.message}`).join("; ");
2324
+ throw new error.MastraError({
2325
+ id: "OBSERVABILITY_INVALID_CONFIG",
2326
+ text: `Invalid observability configuration: ${errorMessages}`,
2327
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
2328
+ category: error.ErrorCategory.USER,
2329
+ details: {
2330
+ validationErrors: errorMessages
2331
+ }
2332
+ });
2333
+ }
2334
+ if (config.configs) {
2335
+ for (const [name, configValue] of Object.entries(config.configs)) {
2336
+ if (!isInstance(configValue)) {
2337
+ const configValidation = observabilityConfigValueSchema.safeParse(configValue);
2338
+ if (!configValidation.success) {
2339
+ const errorMessages = configValidation.error.errors.map((err) => `${err.path.join(".")}: ${err.message}`).join("; ");
2340
+ throw new error.MastraError({
2341
+ id: "OBSERVABILITY_INVALID_INSTANCE_CONFIG",
2342
+ text: `Invalid configuration for observability instance '${name}': ${errorMessages}`,
2343
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
2344
+ category: error.ErrorCategory.USER,
2345
+ details: {
2346
+ instanceName: name,
2347
+ validationErrors: errorMessages
2348
+ }
2349
+ });
2350
+ }
2351
+ }
2352
+ }
2353
+ }
2354
+ if (config.default?.enabled) {
2355
+ const defaultInstance = new DefaultObservabilityInstance({
2356
+ serviceName: "mastra",
2357
+ name: "default",
2358
+ sampling: { type: "always" /* ALWAYS */ },
2359
+ exporters: [new DefaultExporter(), new CloudExporter()],
2360
+ spanOutputProcessors: [new SensitiveDataFilter()]
2361
+ });
2362
+ this.#registry.register("default", defaultInstance, true);
2363
+ }
2364
+ if (config.configs) {
2365
+ const instances = Object.entries(config.configs);
2366
+ instances.forEach(([name, tracingDef], index) => {
2367
+ const instance = isInstance(tracingDef) ? tracingDef : new DefaultObservabilityInstance({ ...tracingDef, name });
2368
+ const isDefault = !config.default?.enabled && index === 0;
2369
+ this.#registry.register(name, instance, isDefault);
2370
+ });
2371
+ }
2372
+ if (config.configSelector) {
2373
+ this.#registry.setSelector(config.configSelector);
2374
+ }
2375
+ }
2376
+ setMastraContext(options) {
2377
+ const instances = this.listInstances();
2378
+ const { mastra } = options;
2379
+ instances.forEach((instance) => {
2380
+ const config = instance.getConfig();
2381
+ const exporters = instance.getExporters();
2382
+ exporters.forEach((exporter) => {
2383
+ if ("init" in exporter && typeof exporter.init === "function") {
2384
+ try {
2385
+ exporter.init({ mastra, config });
2386
+ } catch (error) {
2387
+ this.logger?.warn("Failed to initialize observability exporter", {
2388
+ exporterName: exporter.name,
2389
+ error: error instanceof Error ? error.message : String(error)
2390
+ });
2391
+ }
2392
+ }
2393
+ });
2394
+ });
2395
+ }
2396
+ setLogger(options) {
2397
+ super.__setLogger(options.logger);
2398
+ this.listInstances().forEach((instance) => {
2399
+ instance.__setLogger(options.logger);
2400
+ });
2401
+ }
2402
+ getSelectedInstance(options) {
2403
+ return this.#registry.getSelected(options);
2404
+ }
2405
+ /**
2406
+ * Registry management methods
2407
+ */
2408
+ registerInstance(name, instance, isDefault = false) {
2409
+ this.#registry.register(name, instance, isDefault);
2410
+ }
2411
+ getInstance(name) {
2412
+ return this.#registry.get(name);
2413
+ }
2414
+ getDefaultInstance() {
2415
+ return this.#registry.getDefault();
2416
+ }
2417
+ listInstances() {
2418
+ return this.#registry.list();
2419
+ }
2420
+ unregisterInstance(name) {
2421
+ return this.#registry.unregister(name);
2422
+ }
2423
+ hasInstance(name) {
2424
+ return !!this.#registry.get(name);
2425
+ }
2426
+ setConfigSelector(selector) {
2427
+ this.#registry.setSelector(selector);
2428
+ }
2429
+ clear() {
2430
+ this.#registry.clear();
2431
+ }
2432
+ async shutdown() {
2433
+ await this.#registry.shutdown();
2434
+ }
2435
+ };
2436
+
2437
+ // src/tracing-options.ts
2438
+ function buildTracingOptions(...updaters) {
2439
+ return updaters.reduce((opts, updater) => updater(opts), {});
2440
+ }
2441
+
2442
+ exports.BaseExporter = BaseExporter;
2443
+ exports.BaseObservabilityInstance = BaseObservabilityInstance;
2444
+ exports.BaseSpan = BaseSpan;
2445
+ exports.CloudExporter = CloudExporter;
2446
+ exports.ConsoleExporter = ConsoleExporter;
2447
+ exports.DefaultExporter = DefaultExporter;
2448
+ exports.DefaultObservabilityInstance = DefaultObservabilityInstance;
2449
+ exports.DefaultSpan = DefaultSpan;
2450
+ exports.ModelSpanTracker = ModelSpanTracker;
2451
+ exports.NoOpSpan = NoOpSpan;
2452
+ exports.Observability = Observability;
2453
+ exports.SamplingStrategyType = SamplingStrategyType;
2454
+ exports.SensitiveDataFilter = SensitiveDataFilter;
2455
+ exports.TestExporter = TestExporter;
2456
+ exports.buildTracingOptions = buildTracingOptions;
2457
+ exports.deepClean = deepClean;
2458
+ exports.getExternalParentId = getExternalParentId;
2459
+ exports.observabilityConfigValueSchema = observabilityConfigValueSchema;
2460
+ exports.observabilityInstanceConfigSchema = observabilityInstanceConfigSchema;
2461
+ exports.observabilityRegistryConfigSchema = observabilityRegistryConfigSchema;
2462
+ exports.samplingStrategySchema = samplingStrategySchema;
3
2463
  //# sourceMappingURL=index.cjs.map
4
2464
  //# sourceMappingURL=index.cjs.map