@mastra/observability 1.0.0-beta.1 → 1.0.0-beta.2

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
@@ -35,18 +35,38 @@ var observabilityInstanceConfigSchema = z.object({
35
35
  serviceName: z.string().min(1, "Service name is required"),
36
36
  sampling: samplingStrategySchema.optional(),
37
37
  exporters: z.array(z.any()).optional(),
38
+ bridge: z.any().optional(),
38
39
  spanOutputProcessors: z.array(z.any()).optional(),
39
40
  includeInternalSpans: z.boolean().optional(),
40
41
  requestContextKeys: z.array(z.string()).optional()
41
- });
42
+ }).refine(
43
+ (data) => {
44
+ const hasExporters = data.exporters && data.exporters.length > 0;
45
+ const hasBridge = !!data.bridge;
46
+ return hasExporters || hasBridge;
47
+ },
48
+ {
49
+ message: "At least one exporter or a bridge is required"
50
+ }
51
+ );
42
52
  var observabilityConfigValueSchema = z.object({
43
53
  serviceName: z.string().min(1, "Service name is required"),
44
54
  sampling: samplingStrategySchema.optional(),
45
55
  exporters: z.array(z.any()).optional(),
56
+ bridge: z.any().optional(),
46
57
  spanOutputProcessors: z.array(z.any()).optional(),
47
58
  includeInternalSpans: z.boolean().optional(),
48
59
  requestContextKeys: z.array(z.string()).optional()
49
- });
60
+ }).refine(
61
+ (data) => {
62
+ const hasExporters = data.exporters && data.exporters.length > 0;
63
+ const hasBridge = !!data.bridge;
64
+ return hasExporters || hasBridge;
65
+ },
66
+ {
67
+ message: "At least one exporter or a bridge is required"
68
+ }
69
+ );
50
70
  var observabilityRegistryConfigSchema = z.object({
51
71
  default: z.object({
52
72
  enabled: z.boolean().optional()
@@ -73,6 +93,18 @@ var observabilityRegistryConfigSchema = z.object({
73
93
  {
74
94
  message: 'A "configSelector" function is required when multiple configs are specified to determine which config to use.'
75
95
  }
96
+ ).refine(
97
+ (data) => {
98
+ if (data.configSelector) {
99
+ const isDefaultEnabled = data.default?.enabled === true;
100
+ const hasConfigs = data.configs && typeof data.configs === "object" && !Array.isArray(data.configs) ? Object.keys(data.configs).length > 0 : false;
101
+ return isDefaultEnabled || hasConfigs;
102
+ }
103
+ return true;
104
+ },
105
+ {
106
+ message: 'A "configSelector" requires at least one config or default observability to be configured.'
107
+ }
76
108
  );
77
109
  var BaseExporter = class {
78
110
  /** Mastra logger instance */
@@ -893,6 +925,27 @@ var DefaultExporter = class extends BaseExporter {
893
925
  this.logger.info("DefaultExporter shutdown complete");
894
926
  }
895
927
  };
928
+
929
+ // src/exporters/test.ts
930
+ var TestExporter = class extends BaseExporter {
931
+ name = "tracing-test-exporter";
932
+ #events = [];
933
+ constructor(config = {}) {
934
+ super(config);
935
+ }
936
+ async _exportTracingEvent(event) {
937
+ this.#events.push(event);
938
+ }
939
+ clearEvents() {
940
+ this.#events = [];
941
+ }
942
+ get events() {
943
+ return this.#events;
944
+ }
945
+ async shutdown() {
946
+ this.logger.info("TestExporter shutdown");
947
+ }
948
+ };
896
949
  var ModelSpanTracker = class {
897
950
  #modelSpan;
898
951
  #currentStepSpan;
@@ -1224,6 +1277,16 @@ function isSpanInternal(spanType, flags) {
1224
1277
  return false;
1225
1278
  }
1226
1279
  }
1280
+ function getExternalParentId(options) {
1281
+ if (!options.parent) {
1282
+ return void 0;
1283
+ }
1284
+ if (options.parent.isInternal) {
1285
+ return options.parent.getParentSpanId(false);
1286
+ } else {
1287
+ return options.parent.id;
1288
+ }
1289
+ }
1227
1290
  var BaseSpan = class {
1228
1291
  name;
1229
1292
  type;
@@ -1238,6 +1301,7 @@ var BaseSpan = class {
1238
1301
  output;
1239
1302
  errorInfo;
1240
1303
  metadata;
1304
+ tags;
1241
1305
  traceState;
1242
1306
  /** Parent span ID (for root spans that are children of external spans) */
1243
1307
  parentSpanId;
@@ -1252,6 +1316,7 @@ var BaseSpan = class {
1252
1316
  this.isEvent = options.isEvent ?? false;
1253
1317
  this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
1254
1318
  this.traceState = options.traceState;
1319
+ this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
1255
1320
  if (this.isEvent) {
1256
1321
  this.output = deepClean(options.output);
1257
1322
  } else {
@@ -1314,12 +1379,36 @@ var BaseSpan = class {
1314
1379
  errorInfo: this.errorInfo,
1315
1380
  isEvent: this.isEvent,
1316
1381
  isRootSpan: this.isRootSpan,
1317
- parentSpanId: this.getParentSpanId(includeInternalSpans)
1382
+ parentSpanId: this.getParentSpanId(includeInternalSpans),
1383
+ // Tags are only included for root spans
1384
+ ...this.isRootSpan && this.tags?.length ? { tags: this.tags } : {}
1318
1385
  };
1319
1386
  }
1320
1387
  get externalTraceId() {
1321
1388
  return this.isValid ? this.traceId : void 0;
1322
1389
  }
1390
+ /**
1391
+ * Execute an async function within this span's tracing context.
1392
+ * Delegates to the bridge if available.
1393
+ */
1394
+ async executeInContext(fn) {
1395
+ const bridge = this.observabilityInstance.getBridge();
1396
+ if (bridge?.executeInContext) {
1397
+ return bridge.executeInContext(this.id, fn);
1398
+ }
1399
+ return fn();
1400
+ }
1401
+ /**
1402
+ * Execute a synchronous function within this span's tracing context.
1403
+ * Delegates to the bridge if available.
1404
+ */
1405
+ executeInContextSync(fn) {
1406
+ const bridge = this.observabilityInstance.getBridge();
1407
+ if (bridge?.executeInContextSync) {
1408
+ return bridge.executeInContextSync(this.id, fn);
1409
+ }
1410
+ return fn();
1411
+ }
1323
1412
  };
1324
1413
  var DEFAULT_KEYS_TO_STRIP = /* @__PURE__ */ new Set([
1325
1414
  "logger",
@@ -1366,27 +1455,30 @@ var DefaultSpan = class extends BaseSpan {
1366
1455
  traceId;
1367
1456
  constructor(options, observabilityInstance) {
1368
1457
  super(options, observabilityInstance);
1369
- this.id = generateSpanId();
1458
+ const bridge = observabilityInstance.getBridge();
1459
+ if (bridge && !this.isInternal) {
1460
+ const bridgeIds = bridge.createSpan(options);
1461
+ if (bridgeIds) {
1462
+ this.id = bridgeIds.spanId;
1463
+ this.traceId = bridgeIds.traceId;
1464
+ this.parentSpanId = bridgeIds.parentSpanId;
1465
+ return;
1466
+ }
1467
+ }
1370
1468
  if (options.parent) {
1371
1469
  this.traceId = options.parent.traceId;
1372
- } else if (options.traceId) {
1373
- if (isValidTraceId(options.traceId)) {
1374
- this.traceId = options.traceId;
1375
- } else {
1376
- console.error(
1377
- `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1378
- );
1379
- this.traceId = generateTraceId();
1380
- }
1381
- } else {
1382
- this.traceId = generateTraceId();
1470
+ this.parentSpanId = options.parent.id;
1471
+ this.id = generateSpanId();
1472
+ return;
1383
1473
  }
1384
- if (!options.parent && options.parentSpanId) {
1474
+ this.traceId = getOrCreateTraceId(options);
1475
+ this.id = generateSpanId();
1476
+ if (options.parentSpanId) {
1385
1477
  if (isValidSpanId(options.parentSpanId)) {
1386
1478
  this.parentSpanId = options.parentSpanId;
1387
1479
  } else {
1388
1480
  console.error(
1389
- `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring parent span ID.`
1481
+ `[Mastra Tracing] Invalid parentSpanId: must be 1-16 hexadecimal characters, got "${options.parentSpanId}". Ignoring.`
1390
1482
  );
1391
1483
  }
1392
1484
  }
@@ -1491,6 +1583,18 @@ function isValidTraceId(traceId) {
1491
1583
  function isValidSpanId(spanId) {
1492
1584
  return /^[0-9a-f]{1,16}$/i.test(spanId);
1493
1585
  }
1586
+ function getOrCreateTraceId(options) {
1587
+ if (options.traceId) {
1588
+ if (isValidTraceId(options.traceId)) {
1589
+ return options.traceId;
1590
+ } else {
1591
+ console.error(
1592
+ `[Mastra Tracing] Invalid traceId: must be 1-32 hexadecimal characters, got "${options.traceId}". Generating new trace ID.`
1593
+ );
1594
+ }
1595
+ }
1596
+ return generateTraceId();
1597
+ }
1494
1598
 
1495
1599
  // src/spans/no-op.ts
1496
1600
  var NoOpSpan = class extends BaseSpan {
@@ -1523,13 +1627,17 @@ var BaseObservabilityInstance = class extends MastraBase {
1523
1627
  sampling: config.sampling ?? { type: "always" /* ALWAYS */ },
1524
1628
  exporters: config.exporters ?? [],
1525
1629
  spanOutputProcessors: config.spanOutputProcessors ?? [],
1630
+ bridge: config.bridge ?? void 0,
1526
1631
  includeInternalSpans: config.includeInternalSpans ?? false,
1527
1632
  requestContextKeys: config.requestContextKeys ?? []
1528
1633
  };
1634
+ if (this.config.bridge?.init) {
1635
+ this.config.bridge.init({ config: this.config });
1636
+ }
1529
1637
  }
1530
1638
  /**
1531
1639
  * Override setLogger to add Observability specific initialization log
1532
- * and propagate logger to exporters
1640
+ * and propagate logger to exporters and bridge
1533
1641
  */
1534
1642
  __setLogger(logger) {
1535
1643
  super.__setLogger(logger);
@@ -1538,8 +1646,11 @@ var BaseObservabilityInstance = class extends MastraBase {
1538
1646
  exporter.__setLogger(logger);
1539
1647
  }
1540
1648
  });
1649
+ if (this.config.bridge?.__setLogger) {
1650
+ this.config.bridge.__setLogger(logger);
1651
+ }
1541
1652
  this.logger.debug(
1542
- `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling.type}]`
1653
+ `[Observability] Initialized [service=${this.config.serviceName}] [instance=${this.config.name}] [sampling=${this.config.sampling?.type}] [bridge=${!!this.config.bridge}]`
1543
1654
  );
1544
1655
  }
1545
1656
  // ============================================================================
@@ -1568,11 +1679,15 @@ var BaseObservabilityInstance = class extends MastraBase {
1568
1679
  } else {
1569
1680
  traceState = this.computeTraceState(tracingOptions);
1570
1681
  }
1571
- const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, metadata, traceState);
1682
+ const tracingMetadata = !options.parent ? tracingOptions?.metadata : void 0;
1683
+ const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
1684
+ const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
1685
+ const tags = !options.parent ? tracingOptions?.tags : void 0;
1572
1686
  const span = this.createSpan({
1573
1687
  ...rest,
1574
1688
  metadata: enrichedMetadata,
1575
- traceState
1689
+ traceState,
1690
+ tags
1576
1691
  });
1577
1692
  if (span.isEvent) {
1578
1693
  this.emitSpanEnded(span);
@@ -1606,6 +1721,12 @@ var BaseObservabilityInstance = class extends MastraBase {
1606
1721
  getSpanOutputProcessors() {
1607
1722
  return [...this.spanOutputProcessors];
1608
1723
  }
1724
+ /**
1725
+ * Get the bridge instance if configured
1726
+ */
1727
+ getBridge() {
1728
+ return this.config.bridge;
1729
+ }
1609
1730
  /**
1610
1731
  * Get the logger instance (for exporters and other components)
1611
1732
  */
@@ -1650,7 +1771,9 @@ var BaseObservabilityInstance = class extends MastraBase {
1650
1771
  */
1651
1772
  shouldSample(options) {
1652
1773
  const { sampling } = this.config;
1653
- switch (sampling.type) {
1774
+ switch (sampling?.type) {
1775
+ case void 0:
1776
+ return true;
1654
1777
  case "always" /* ALWAYS */:
1655
1778
  return true;
1656
1779
  case "never" /* NEVER */:
@@ -1782,17 +1905,21 @@ var BaseObservabilityInstance = class extends MastraBase {
1782
1905
  }
1783
1906
  }
1784
1907
  /**
1785
- * Export tracing event through all exporters (realtime mode)
1908
+ * Export tracing event through all exporters and bridge (realtime mode)
1786
1909
  */
1787
1910
  async exportTracingEvent(event) {
1788
- const exportPromises = this.exporters.map(async (exporter) => {
1911
+ const targets = [
1912
+ ...this.exporters
1913
+ ];
1914
+ if (this.config.bridge) {
1915
+ targets.push(this.config.bridge);
1916
+ }
1917
+ const exportPromises = targets.map(async (target) => {
1789
1918
  try {
1790
- if (exporter.exportTracingEvent) {
1791
- await exporter.exportTracingEvent(event);
1792
- this.logger.debug(`[Observability] Event exported [exporter=${exporter.name}] [type=${event.type}]`);
1793
- }
1919
+ await target.exportTracingEvent(event);
1920
+ this.logger.debug(`[Observability] Event exported [target=${target.name}] [type=${event.type}]`);
1794
1921
  } catch (error) {
1795
- this.logger.error(`[Observability] Export error [exporter=${exporter.name}]`, error);
1922
+ this.logger.error(`[Observability] Export error [target=${target.name}]`, error);
1796
1923
  }
1797
1924
  });
1798
1925
  await Promise.allSettled(exportPromises);
@@ -1816,6 +1943,9 @@ var BaseObservabilityInstance = class extends MastraBase {
1816
1943
  ...this.exporters.map((e) => e.shutdown()),
1817
1944
  ...this.spanOutputProcessors.map((p) => p.shutdown())
1818
1945
  ];
1946
+ if (this.config.bridge) {
1947
+ shutdownPromises.push(this.config.bridge.shutdown());
1948
+ }
1819
1949
  await Promise.allSettled(shutdownPromises);
1820
1950
  this.logger.info(`[Observability] Shutdown completed [name=${this.name}]`);
1821
1951
  }
@@ -1962,9 +2092,16 @@ var SensitiveDataFilter = class {
1962
2092
  /**
1963
2093
  * Recursively filter objects/arrays for sensitive keys.
1964
2094
  * Handles circular references by replacing with a marker.
2095
+ * Also attempts to parse and redact JSON strings.
1965
2096
  */
1966
2097
  deepFilter(obj, seen = /* @__PURE__ */ new WeakSet()) {
1967
2098
  if (obj === null || typeof obj !== "object") {
2099
+ if (typeof obj === "string") {
2100
+ const trimmed = obj.trim();
2101
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
2102
+ return this.redactJsonString(obj);
2103
+ }
2104
+ }
1968
2105
  return obj;
1969
2106
  }
1970
2107
  if (seen.has(obj)) {
@@ -2017,6 +2154,22 @@ var SensitiveDataFilter = class {
2017
2154
  return normalizedKey === sensitiveField;
2018
2155
  });
2019
2156
  }
2157
+ /**
2158
+ * Attempt to parse a string as JSON and redact sensitive fields within it.
2159
+ * If parsing fails or no sensitive data is found, returns the original string.
2160
+ */
2161
+ redactJsonString(str) {
2162
+ try {
2163
+ const parsed = JSON.parse(str);
2164
+ if (parsed && typeof parsed === "object") {
2165
+ const filtered = this.deepFilter(parsed, /* @__PURE__ */ new WeakSet());
2166
+ return JSON.stringify(filtered);
2167
+ }
2168
+ return str;
2169
+ } catch {
2170
+ return str;
2171
+ }
2172
+ }
2020
2173
  /**
2021
2174
  * Redact a sensitive value.
2022
2175
  * - Full style: replaces with a fixed token.
@@ -2169,6 +2322,6 @@ var Observability = class extends MastraBase {
2169
2322
  }
2170
2323
  };
2171
2324
 
2172
- export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, deepClean, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema };
2325
+ export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, deepClean, getExternalParentId, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema };
2173
2326
  //# sourceMappingURL=index.js.map
2174
2327
  //# sourceMappingURL=index.js.map