@mastra/observability 1.17.1 → 1.17.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/exporters/mastra-storage.d.ts +3 -6
- package/dist/exporters/mastra-storage.d.ts.map +1 -1
- package/dist/index.cjs +64 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -52
- package/dist/index.js.map +1 -1
- package/dist/spans/serialization.d.ts +6 -0
- package/dist/spans/serialization.d.ts.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @mastra/observability
|
|
2
2
|
|
|
3
|
+
## 1.17.2-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix PostgreSQL observability writes failing on NUL characters and unpaired Unicode surrogates ([#21728](https://github.com/mastra-ai/mastra/pull/21728))
|
|
8
|
+
|
|
9
|
+
Span serialization truncated strings by UTF-16 code unit, so a cut inside an emoji left a lone surrogate that PostgreSQL rejected on the jsonb cast (`22P02`). NUL characters were rejected as well (`22P05`). Because observability events are inserted as a single multi-row statement, one malformed field discarded the entire batch.
|
|
10
|
+
|
|
11
|
+
Truncation now preserves complete surrogate pairs, and the v-next PostgreSQL observability encoder sanitizes NUL and unpaired surrogates before the jsonb cast, using the same sanitizer that workflow snapshots already rely on. Valid Unicode, including complete emoji, is preserved.
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`2c85f42`](https://github.com/mastra-ai/mastra/commit/2c85f428e04ccd63ea31a7ec80b5b327afdad555), [`11bbeb9`](https://github.com/mastra-ai/mastra/commit/11bbeb9b108ef2264e05acefc6dafb9cbb342921), [`1a485f3`](https://github.com/mastra-ai/mastra/commit/1a485f3538f5ec64d58bd8b5e1e99de0c695c87b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`8661d7d`](https://github.com/mastra-ai/mastra/commit/8661d7d7179f0a024456aabdd8679bcecd09ac28), [`575e343`](https://github.com/mastra-ai/mastra/commit/575e343900451021d96110916497d334af7bc252), [`cacb839`](https://github.com/mastra-ai/mastra/commit/cacb8392d9e74189b56d857290b0615f98a2683d), [`b47b26e`](https://github.com/mastra-ai/mastra/commit/b47b26e6fe95cb8a3482be2c5e52de157fe59d0b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`c46eb09`](https://github.com/mastra-ai/mastra/commit/c46eb09ce4987509af57a0ac582c61241a6dd2f1), [`30ed33e`](https://github.com/mastra-ai/mastra/commit/30ed33ee14084a26019aba15fceadda6d6ddefaf), [`91ad69d`](https://github.com/mastra-ai/mastra/commit/91ad69d64994c89199b0c55399e64ed91c61df2f), [`8dc408d`](https://github.com/mastra-ai/mastra/commit/8dc408d34438f9e13297f792c11a5cfd6cf952e1), [`c92def1`](https://github.com/mastra-ai/mastra/commit/c92def10a13c822972c96f0a4ca6ffc1f4258aed), [`c5eaec5`](https://github.com/mastra-ai/mastra/commit/c5eaec5a860d80d0e3805e67db0414b87ac8cbed), [`e66b2ba`](https://github.com/mastra-ai/mastra/commit/e66b2ba100db63eaeab6e21e1ea34b113f2ec781)]:
|
|
14
|
+
- @mastra/core@1.62.0-alpha.3
|
|
15
|
+
|
|
16
|
+
## 1.17.2-alpha.0
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Fixed `MastraStorageExporter` staying disabled when a configured observability store is temporarily unavailable during startup. The exporter now recovers on a later event, so traces resume without restarting the process. (#21943) ([#21957](https://github.com/mastra-ai/mastra/pull/21957))
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`f95f468`](https://github.com/mastra-ai/mastra/commit/f95f468cf1e7c2b924a13826494f98b8f2ccd581)]:
|
|
23
|
+
- @mastra/core@1.61.1-alpha.1
|
|
24
|
+
|
|
3
25
|
## 1.17.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -23,6 +23,9 @@ export declare class MastraStorageExporter extends BaseExporter {
|
|
|
23
23
|
* Initialize the exporter (called after all dependencies are ready)
|
|
24
24
|
*/
|
|
25
25
|
init(options: InitExporterOptions): Promise<void>;
|
|
26
|
+
private warnUnavailable;
|
|
27
|
+
private initializeStorage;
|
|
28
|
+
private ensureInitialized;
|
|
26
29
|
/**
|
|
27
30
|
* Checks if buffer should be flushed based on size or time triggers
|
|
28
31
|
*/
|
|
@@ -63,12 +66,6 @@ export declare class MastraStorageExporter extends BaseExporter {
|
|
|
63
66
|
*/
|
|
64
67
|
private flushBuffer;
|
|
65
68
|
_exportTracingEvent(event: TracingEvent): Promise<void>;
|
|
66
|
-
/**
|
|
67
|
-
* Resolves when an ongoing init call is finished
|
|
68
|
-
* Doesn't wait for the caller to call init
|
|
69
|
-
* @returns
|
|
70
|
-
*/
|
|
71
|
-
private waitForInit;
|
|
72
69
|
/**
|
|
73
70
|
* Handle metric events — buffer for batch flush.
|
|
74
71
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mastra-storage.d.ts","sourceRoot":"","sources":["../../src/exporters/mastra-storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,UAAU,EACV,aAAa,EAId,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAwB,sBAAsB,EAAwB,MAAM,sBAAsB,CAAC;AAS/G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,4FAA4F;AAC5F,MAAM,WAAW,2BAA4B,SAAQ,kBAAkB;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;CAC5C;
|
|
1
|
+
{"version":3,"file":"mastra-storage.d.ts","sourceRoot":"","sources":["../../src/exporters/mastra-storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,UAAU,EACV,aAAa,EAId,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAwB,sBAAsB,EAAwB,MAAM,sBAAsB,CAAC;AAS/G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,4FAA4F;AAC5F,MAAM,WAAW,2BAA4B,SAAQ,kBAAkB;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;CAC5C;AAkCD;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;;IACrD,IAAI,SAA6B;gBAgBrB,MAAM,GAAE,2BAAgC;IAiBpD;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD,OAAO,CAAC,eAAe;YAUT,iBAAiB;YA6CjB,iBAAiB;IAS/B;;OAEG;IACH,OAAO,CAAC,WAAW;IAyBnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,iBAAiB;IAiBzB;;;;OAIG;YACW,kBAAkB;IAQhC,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,QAAQ;IAsBhB;;;;OAIG;YACW,YAAY;IAkC1B;;;OAGG;YACW,gBAAgB;IA+D9B;;;;;;;OAOG;YACW,WAAW;IA+HnB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtD;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhD;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpD;;OAEG;IACG,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAiBhC"}
|
package/dist/index.cjs
CHANGED
|
@@ -246,10 +246,18 @@ function mergeSerializationOptions(userOptions) {
|
|
|
246
246
|
}
|
|
247
247
|
/**
|
|
248
248
|
* Hard-cap any string to prevent unbounded growth.
|
|
249
|
+
*
|
|
250
|
+
* The cut never splits a UTF-16 surrogate pair: if it would land immediately
|
|
251
|
+
* after a lone high surrogate (U+D800..U+DBFF), it backs off one code unit so
|
|
252
|
+
* the pair is dropped as a whole. `JSON.stringify` emits a lone `\ud83d` for a
|
|
253
|
+
* split pair, which PostgreSQL rejects on a jsonb cast with 22P02
|
|
254
|
+
* ("Unicode low surrogate must follow a high surrogate").
|
|
249
255
|
*/
|
|
250
256
|
function truncateString(s, maxChars) {
|
|
251
257
|
if (s.length <= maxChars) return s;
|
|
252
|
-
|
|
258
|
+
const code = s.charCodeAt(maxChars - 1);
|
|
259
|
+
const safeEnd = code >= 55296 && code <= 56319 ? maxChars - 1 : maxChars;
|
|
260
|
+
return s.slice(0, safeEnd) + "…[truncated]";
|
|
253
261
|
}
|
|
254
262
|
function formatSerializationError(error) {
|
|
255
263
|
return `[${error instanceof Error ? truncateString(error.message, 256) : "unknown error"}]`;
|
|
@@ -7123,8 +7131,8 @@ const FLUSH_SUCCEEDED = { failed: false };
|
|
|
7123
7131
|
var MastraStorageExporter = class extends BaseExporter {
|
|
7124
7132
|
name = "mastra-storage-exporter";
|
|
7125
7133
|
#config;
|
|
7126
|
-
#
|
|
7127
|
-
#
|
|
7134
|
+
#initializationPromise;
|
|
7135
|
+
#wasUnavailable = false;
|
|
7128
7136
|
#eventBuffer;
|
|
7129
7137
|
#storage;
|
|
7130
7138
|
#observabilityStorage;
|
|
@@ -7149,41 +7157,56 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7149
7157
|
* Initialize the exporter (called after all dependencies are ready)
|
|
7150
7158
|
*/
|
|
7151
7159
|
async init(options) {
|
|
7160
|
+
this.#emitDropEvent = options.emitDropEvent;
|
|
7161
|
+
this.#storage = options.mastra?.getStorage();
|
|
7162
|
+
if (!this.#storage) {
|
|
7163
|
+
this.logger.warn("MastraStorageExporter disabled: Storage not available. Traces will not be persisted.");
|
|
7164
|
+
return;
|
|
7165
|
+
}
|
|
7166
|
+
await this.ensureInitialized();
|
|
7167
|
+
}
|
|
7168
|
+
warnUnavailable(message, error) {
|
|
7169
|
+
if (this.#wasUnavailable) return;
|
|
7170
|
+
this.#wasUnavailable = true;
|
|
7171
|
+
if (error === void 0) {
|
|
7172
|
+
this.logger.warn(message);
|
|
7173
|
+
return;
|
|
7174
|
+
}
|
|
7175
|
+
this.logger.warn(message, { error: error instanceof Error ? error.message : String(error) });
|
|
7176
|
+
}
|
|
7177
|
+
async initializeStorage() {
|
|
7178
|
+
const storage = this.#storage;
|
|
7179
|
+
if (!storage) return;
|
|
7152
7180
|
try {
|
|
7153
|
-
this.#
|
|
7154
|
-
|
|
7155
|
-
this
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
if (!this.#observabilityStorage) {
|
|
7162
|
-
this.logger.warn("MastraStorageExporter disabled: Observability storage not available. Traces will not be persisted.");
|
|
7163
|
-
return;
|
|
7164
|
-
}
|
|
7165
|
-
if (!this.#resolvedStrategy) {
|
|
7166
|
-
this.#resolvedStrategy = resolveTracingStorageStrategy(this.#config, this.#observabilityStorage, this.#storage.constructor.name, this.logger);
|
|
7167
|
-
this.logger.debug("tracing storage exporter initialized", {
|
|
7168
|
-
strategy: this.#resolvedStrategy,
|
|
7169
|
-
source: this.#config.strategy !== "auto" ? "user" : "auto",
|
|
7170
|
-
storageAdapter: this.#storage.constructor.name,
|
|
7171
|
-
maxBatchSize: this.#config.maxBatchSize,
|
|
7172
|
-
maxBatchWaitMs: this.#config.maxBatchWaitMs
|
|
7173
|
-
});
|
|
7174
|
-
}
|
|
7175
|
-
if (this.#resolvedStrategy) this.#eventBuffer.init({ strategy: this.#resolvedStrategy });
|
|
7176
|
-
} finally {
|
|
7177
|
-
this.#isInitializing = false;
|
|
7178
|
-
/**
|
|
7179
|
-
* Assumes caller waits until export of a parent span is completed before calling
|
|
7180
|
-
* export for child spans , order is not relevant for resolve
|
|
7181
|
-
*/
|
|
7182
|
-
this.#initPromises.forEach((resolve) => {
|
|
7183
|
-
resolve();
|
|
7184
|
-
});
|
|
7185
|
-
this.#initPromises.clear();
|
|
7181
|
+
this.#observabilityStorage = await storage.getStore("observability");
|
|
7182
|
+
} catch (error) {
|
|
7183
|
+
this.warnUnavailable("MastraStorageExporter unavailable: Failed to initialize observability storage. Traces will not be persisted until storage becomes available.", error);
|
|
7184
|
+
return;
|
|
7185
|
+
}
|
|
7186
|
+
if (!this.#observabilityStorage) {
|
|
7187
|
+
this.warnUnavailable("MastraStorageExporter unavailable: Observability storage not available. Traces will not be persisted until storage becomes available.");
|
|
7188
|
+
return;
|
|
7186
7189
|
}
|
|
7190
|
+
this.#resolvedStrategy = resolveTracingStorageStrategy(this.#config, this.#observabilityStorage, storage.constructor.name, this.logger);
|
|
7191
|
+
this.#eventBuffer.init({ strategy: this.#resolvedStrategy });
|
|
7192
|
+
this.logger.debug("tracing storage exporter initialized", {
|
|
7193
|
+
strategy: this.#resolvedStrategy,
|
|
7194
|
+
source: this.#config.strategy !== "auto" ? "user" : "auto",
|
|
7195
|
+
storageAdapter: storage.constructor.name,
|
|
7196
|
+
maxBatchSize: this.#config.maxBatchSize,
|
|
7197
|
+
maxBatchWaitMs: this.#config.maxBatchWaitMs
|
|
7198
|
+
});
|
|
7199
|
+
if (this.#wasUnavailable) {
|
|
7200
|
+
this.#wasUnavailable = false;
|
|
7201
|
+
this.logger.info("MastraStorageExporter recovered: Observability storage is available. Traces will be persisted.");
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
async ensureInitialized() {
|
|
7205
|
+
if (this.#observabilityStorage || !this.#storage) return;
|
|
7206
|
+
this.#initializationPromise ??= this.initializeStorage().finally(() => {
|
|
7207
|
+
this.#initializationPromise = void 0;
|
|
7208
|
+
});
|
|
7209
|
+
await this.#initializationPromise;
|
|
7187
7210
|
}
|
|
7188
7211
|
/**
|
|
7189
7212
|
* Checks if buffer should be flushed based on size or time triggers
|
|
@@ -7435,7 +7458,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7435
7458
|
}
|
|
7436
7459
|
}
|
|
7437
7460
|
async _exportTracingEvent(event) {
|
|
7438
|
-
await this.
|
|
7461
|
+
await this.ensureInitialized();
|
|
7439
7462
|
if (!this.#observabilityStorage) {
|
|
7440
7463
|
this.logger.debug("Cannot store traces. Observability storage is not initialized");
|
|
7441
7464
|
return;
|
|
@@ -7444,21 +7467,10 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7444
7467
|
await this.handleBatchedFlush();
|
|
7445
7468
|
}
|
|
7446
7469
|
/**
|
|
7447
|
-
* Resolves when an ongoing init call is finished
|
|
7448
|
-
* Doesn't wait for the caller to call init
|
|
7449
|
-
* @returns
|
|
7450
|
-
*/
|
|
7451
|
-
async waitForInit() {
|
|
7452
|
-
if (!this.#isInitializing) return;
|
|
7453
|
-
return new Promise((resolve) => {
|
|
7454
|
-
this.#initPromises.add(resolve);
|
|
7455
|
-
});
|
|
7456
|
-
}
|
|
7457
|
-
/**
|
|
7458
7470
|
* Handle metric events — buffer for batch flush.
|
|
7459
7471
|
*/
|
|
7460
7472
|
async onMetricEvent(event) {
|
|
7461
|
-
await this.
|
|
7473
|
+
await this.ensureInitialized();
|
|
7462
7474
|
if (!this.#observabilityStorage) return;
|
|
7463
7475
|
this.#eventBuffer.addEvent(event);
|
|
7464
7476
|
await this.handleBatchedFlush();
|
|
@@ -7467,7 +7479,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7467
7479
|
* Handle log events — buffer for batch flush.
|
|
7468
7480
|
*/
|
|
7469
7481
|
async onLogEvent(event) {
|
|
7470
|
-
await this.
|
|
7482
|
+
await this.ensureInitialized();
|
|
7471
7483
|
if (!this.#observabilityStorage) return;
|
|
7472
7484
|
this.#eventBuffer.addEvent(event);
|
|
7473
7485
|
await this.handleBatchedFlush();
|
|
@@ -7476,7 +7488,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7476
7488
|
* Handle score events — buffer for batch flush.
|
|
7477
7489
|
*/
|
|
7478
7490
|
async onScoreEvent(event) {
|
|
7479
|
-
await this.
|
|
7491
|
+
await this.ensureInitialized();
|
|
7480
7492
|
if (!this.#observabilityStorage) return;
|
|
7481
7493
|
this.#eventBuffer.addEvent(event);
|
|
7482
7494
|
await this.handleBatchedFlush();
|
|
@@ -7485,7 +7497,7 @@ var MastraStorageExporter = class extends BaseExporter {
|
|
|
7485
7497
|
* Handle feedback events — buffer for batch flush.
|
|
7486
7498
|
*/
|
|
7487
7499
|
async onFeedbackEvent(event) {
|
|
7488
|
-
await this.
|
|
7500
|
+
await this.ensureInitialized();
|
|
7489
7501
|
if (!this.#observabilityStorage) return;
|
|
7490
7502
|
this.#eventBuffer.addEvent(event);
|
|
7491
7503
|
await this.handleBatchedFlush();
|