@mastra/observability 1.0.0-beta.11 → 1.0.0-beta.13
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 +75 -0
- package/dist/exporters/base.d.ts +52 -3
- package/dist/exporters/base.d.ts.map +1 -1
- package/dist/exporters/cloud.d.ts +8 -2
- package/dist/exporters/cloud.d.ts.map +1 -1
- package/dist/exporters/default.d.ts +8 -2
- package/dist/exporters/default.d.ts.map +1 -1
- package/dist/exporters/index.d.ts +1 -0
- package/dist/exporters/index.d.ts.map +1 -1
- package/dist/exporters/span-formatters.d.ts +35 -0
- package/dist/exporters/span-formatters.d.ts.map +1 -0
- package/dist/exporters/tracking.d.ts +20 -0
- package/dist/exporters/tracking.d.ts.map +1 -1
- package/dist/index.cjs +171 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +171 -52
- package/dist/index.js.map +1 -1
- package/dist/instances/base.d.ts +9 -0
- package/dist/instances/base.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -4163,6 +4163,8 @@ var observabilityRegistryConfigSchema = external_exports.object({
|
|
|
4163
4163
|
var BaseExporter = class {
|
|
4164
4164
|
/** Mastra logger instance */
|
|
4165
4165
|
logger;
|
|
4166
|
+
/** Base configuration (accessible by subclasses) */
|
|
4167
|
+
baseConfig;
|
|
4166
4168
|
/** Whether this exporter is disabled */
|
|
4167
4169
|
#disabled = false;
|
|
4168
4170
|
/** Public getter for disabled state */
|
|
@@ -4173,6 +4175,7 @@ var BaseExporter = class {
|
|
|
4173
4175
|
* Initialize the base exporter with logger
|
|
4174
4176
|
*/
|
|
4175
4177
|
constructor(config = {}) {
|
|
4178
|
+
this.baseConfig = config;
|
|
4176
4179
|
const logLevel = this.resolveLogLevel(config.logLevel);
|
|
4177
4180
|
this.logger = config.logger ?? new logger.ConsoleLogger({ level: logLevel, name: this.constructor.name });
|
|
4178
4181
|
}
|
|
@@ -4210,17 +4213,59 @@ var BaseExporter = class {
|
|
|
4210
4213
|
this.#disabled = true;
|
|
4211
4214
|
this.logger.warn(`${this.name} disabled: ${reason}`);
|
|
4212
4215
|
}
|
|
4216
|
+
/**
|
|
4217
|
+
* Apply the customSpanFormatter if configured.
|
|
4218
|
+
* This is called automatically by exportTracingEvent before _exportTracingEvent.
|
|
4219
|
+
*
|
|
4220
|
+
* Supports both synchronous and asynchronous formatters. If the formatter
|
|
4221
|
+
* returns a Promise, it will be awaited.
|
|
4222
|
+
*
|
|
4223
|
+
* @param event - The incoming tracing event
|
|
4224
|
+
* @returns The (possibly modified) event to process
|
|
4225
|
+
*/
|
|
4226
|
+
async applySpanFormatter(event) {
|
|
4227
|
+
if (this.baseConfig.customSpanFormatter) {
|
|
4228
|
+
try {
|
|
4229
|
+
const formattedSpan = await this.baseConfig.customSpanFormatter(event.exportedSpan);
|
|
4230
|
+
return {
|
|
4231
|
+
...event,
|
|
4232
|
+
exportedSpan: formattedSpan
|
|
4233
|
+
};
|
|
4234
|
+
} catch (error) {
|
|
4235
|
+
this.logger.error(`${this.name}: Error in customSpanFormatter`, {
|
|
4236
|
+
error,
|
|
4237
|
+
spanId: event.exportedSpan.id,
|
|
4238
|
+
traceId: event.exportedSpan.traceId
|
|
4239
|
+
});
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
return event;
|
|
4243
|
+
}
|
|
4213
4244
|
/**
|
|
4214
4245
|
* Export a tracing event
|
|
4215
4246
|
*
|
|
4216
|
-
* This method checks if the exporter is disabled
|
|
4217
|
-
*
|
|
4247
|
+
* This method checks if the exporter is disabled, applies the customSpanFormatter,
|
|
4248
|
+
* then calls _exportTracingEvent.
|
|
4249
|
+
* Subclasses should implement _exportTracingEvent instead of overriding this method.
|
|
4218
4250
|
*/
|
|
4219
4251
|
async exportTracingEvent(event) {
|
|
4220
4252
|
if (this.isDisabled) {
|
|
4221
4253
|
return;
|
|
4222
4254
|
}
|
|
4223
|
-
await this.
|
|
4255
|
+
const processedEvent = await this.applySpanFormatter(event);
|
|
4256
|
+
await this._exportTracingEvent(processedEvent);
|
|
4257
|
+
}
|
|
4258
|
+
/**
|
|
4259
|
+
* Force flush any buffered/queued spans without shutting down the exporter.
|
|
4260
|
+
*
|
|
4261
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
4262
|
+
* are exported before the runtime instance is terminated, while keeping
|
|
4263
|
+
* the exporter active for future requests.
|
|
4264
|
+
*
|
|
4265
|
+
* Default implementation is a no-op. Override to add flush logic.
|
|
4266
|
+
*/
|
|
4267
|
+
async flush() {
|
|
4268
|
+
this.logger.debug(`${this.name} flush called (no-op in base class)`);
|
|
4224
4269
|
}
|
|
4225
4270
|
/**
|
|
4226
4271
|
* Shutdown the exporter and clean up resources
|
|
@@ -4892,6 +4937,10 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
4892
4937
|
/**
|
|
4893
4938
|
* Hook called before processing each tracing event.
|
|
4894
4939
|
* Override to transform or enrich the event before processing.
|
|
4940
|
+
*
|
|
4941
|
+
* Note: The customSpanFormatter is applied at the BaseExporter level before this hook.
|
|
4942
|
+
* Subclasses can override this to add additional pre-processing logic.
|
|
4943
|
+
*
|
|
4895
4944
|
* @param event - The incoming tracing event
|
|
4896
4945
|
* @returns The (possibly modified) event to process
|
|
4897
4946
|
*/
|
|
@@ -5105,8 +5154,31 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
5105
5154
|
return this.#traceMap.size;
|
|
5106
5155
|
}
|
|
5107
5156
|
// ============================================================================
|
|
5108
|
-
// Shutdown Hooks (Override in subclass as needed)
|
|
5157
|
+
// Flush and Shutdown Hooks (Override in subclass as needed)
|
|
5109
5158
|
// ============================================================================
|
|
5159
|
+
/**
|
|
5160
|
+
* Hook called by flush() to perform vendor-specific flush logic.
|
|
5161
|
+
* Override to send buffered data to the vendor's API.
|
|
5162
|
+
*
|
|
5163
|
+
* Unlike _postShutdown(), this method should NOT release resources,
|
|
5164
|
+
* as the exporter will continue to be used after flushing.
|
|
5165
|
+
*/
|
|
5166
|
+
async _flush() {
|
|
5167
|
+
}
|
|
5168
|
+
/**
|
|
5169
|
+
* Force flush any buffered data without shutting down the exporter.
|
|
5170
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
5171
|
+
* are exported before the runtime instance is terminated.
|
|
5172
|
+
*
|
|
5173
|
+
* Subclasses should override _flush() to implement vendor-specific flush logic.
|
|
5174
|
+
*/
|
|
5175
|
+
async flush() {
|
|
5176
|
+
if (this.isDisabled) {
|
|
5177
|
+
return;
|
|
5178
|
+
}
|
|
5179
|
+
this.logger.debug(`${this.name}: Flushing`);
|
|
5180
|
+
await this._flush();
|
|
5181
|
+
}
|
|
5110
5182
|
/**
|
|
5111
5183
|
* Hook called at the start of shutdown, before cancelling timers and aborting spans.
|
|
5112
5184
|
* Override to perform vendor-specific pre-shutdown tasks.
|
|
@@ -5164,9 +5236,20 @@ var TrackingExporter = class extends BaseExporter {
|
|
|
5164
5236
|
await super.shutdown();
|
|
5165
5237
|
}
|
|
5166
5238
|
};
|
|
5239
|
+
|
|
5240
|
+
// src/exporters/span-formatters.ts
|
|
5241
|
+
function chainFormatters(formatters) {
|
|
5242
|
+
return async (span) => {
|
|
5243
|
+
let currentSpan = span;
|
|
5244
|
+
for (const formatter of formatters) {
|
|
5245
|
+
currentSpan = await formatter(currentSpan);
|
|
5246
|
+
}
|
|
5247
|
+
return currentSpan;
|
|
5248
|
+
};
|
|
5249
|
+
}
|
|
5167
5250
|
var CloudExporter = class extends BaseExporter {
|
|
5168
5251
|
name = "mastra-cloud-observability-exporter";
|
|
5169
|
-
|
|
5252
|
+
cloudConfig;
|
|
5170
5253
|
buffer;
|
|
5171
5254
|
flushTimer = null;
|
|
5172
5255
|
constructor(config = {}) {
|
|
@@ -5176,7 +5259,7 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5176
5259
|
this.setDisabled("MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.");
|
|
5177
5260
|
}
|
|
5178
5261
|
const endpoint = config.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
|
|
5179
|
-
this.
|
|
5262
|
+
this.cloudConfig = {
|
|
5180
5263
|
logger: this.logger,
|
|
5181
5264
|
logLevel: config.logLevel ?? logger.LogLevel.INFO,
|
|
5182
5265
|
maxBatchSize: config.maxBatchSize ?? 1e3,
|
|
@@ -5234,12 +5317,12 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5234
5317
|
return spanRecord;
|
|
5235
5318
|
}
|
|
5236
5319
|
shouldFlush() {
|
|
5237
|
-
if (this.buffer.totalSize >= this.
|
|
5320
|
+
if (this.buffer.totalSize >= this.cloudConfig.maxBatchSize) {
|
|
5238
5321
|
return true;
|
|
5239
5322
|
}
|
|
5240
5323
|
if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
|
|
5241
5324
|
const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
|
|
5242
|
-
if (elapsed >= this.
|
|
5325
|
+
if (elapsed >= this.cloudConfig.maxBatchWaitMs) {
|
|
5243
5326
|
return true;
|
|
5244
5327
|
}
|
|
5245
5328
|
}
|
|
@@ -5262,9 +5345,9 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5262
5345
|
this.logger.trackException(mastraError);
|
|
5263
5346
|
this.logger.error("Scheduled flush failed", mastraError);
|
|
5264
5347
|
});
|
|
5265
|
-
}, this.
|
|
5348
|
+
}, this.cloudConfig.maxBatchWaitMs);
|
|
5266
5349
|
}
|
|
5267
|
-
async
|
|
5350
|
+
async flushBuffer() {
|
|
5268
5351
|
if (this.flushTimer) {
|
|
5269
5352
|
clearTimeout(this.flushTimer);
|
|
5270
5353
|
this.flushTimer = null;
|
|
@@ -5274,7 +5357,7 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5274
5357
|
}
|
|
5275
5358
|
const startTime = Date.now();
|
|
5276
5359
|
const spansCopy = [...this.buffer.spans];
|
|
5277
|
-
const flushReason = this.buffer.totalSize >= this.
|
|
5360
|
+
const flushReason = this.buffer.totalSize >= this.cloudConfig.maxBatchSize ? "size" : "time";
|
|
5278
5361
|
this.resetBuffer();
|
|
5279
5362
|
try {
|
|
5280
5363
|
await this.batchUpload(spansCopy);
|
|
@@ -5305,7 +5388,7 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5305
5388
|
*/
|
|
5306
5389
|
async batchUpload(spans) {
|
|
5307
5390
|
const headers = {
|
|
5308
|
-
Authorization: `Bearer ${this.
|
|
5391
|
+
Authorization: `Bearer ${this.cloudConfig.accessToken}`,
|
|
5309
5392
|
"Content-Type": "application/json"
|
|
5310
5393
|
};
|
|
5311
5394
|
const options = {
|
|
@@ -5313,13 +5396,29 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5313
5396
|
headers,
|
|
5314
5397
|
body: JSON.stringify({ spans })
|
|
5315
5398
|
};
|
|
5316
|
-
await utils.fetchWithRetry(this.
|
|
5399
|
+
await utils.fetchWithRetry(this.cloudConfig.endpoint, options, this.cloudConfig.maxRetries);
|
|
5317
5400
|
}
|
|
5318
5401
|
resetBuffer() {
|
|
5319
5402
|
this.buffer.spans = [];
|
|
5320
5403
|
this.buffer.firstEventTime = void 0;
|
|
5321
5404
|
this.buffer.totalSize = 0;
|
|
5322
5405
|
}
|
|
5406
|
+
/**
|
|
5407
|
+
* Force flush any buffered spans without shutting down the exporter.
|
|
5408
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
5409
|
+
* are exported before the runtime instance is terminated.
|
|
5410
|
+
*/
|
|
5411
|
+
async flush() {
|
|
5412
|
+
if (this.isDisabled) {
|
|
5413
|
+
return;
|
|
5414
|
+
}
|
|
5415
|
+
if (this.buffer.totalSize > 0) {
|
|
5416
|
+
this.logger.debug("Flushing buffered events", {
|
|
5417
|
+
bufferedEvents: this.buffer.totalSize
|
|
5418
|
+
});
|
|
5419
|
+
await this.flushBuffer();
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5323
5422
|
async shutdown() {
|
|
5324
5423
|
if (this.isDisabled) {
|
|
5325
5424
|
return;
|
|
@@ -5328,27 +5427,22 @@ var CloudExporter = class extends BaseExporter {
|
|
|
5328
5427
|
clearTimeout(this.flushTimer);
|
|
5329
5428
|
this.flushTimer = null;
|
|
5330
5429
|
}
|
|
5331
|
-
|
|
5332
|
-
this.
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
{
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
error$1
|
|
5348
|
-
);
|
|
5349
|
-
this.logger.trackException(mastraError);
|
|
5350
|
-
this.logger.error("Failed to flush remaining events during shutdown", mastraError);
|
|
5351
|
-
}
|
|
5430
|
+
try {
|
|
5431
|
+
await this.flush();
|
|
5432
|
+
} catch (error$1) {
|
|
5433
|
+
const mastraError = new error.MastraError(
|
|
5434
|
+
{
|
|
5435
|
+
id: `CLOUD_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
|
|
5436
|
+
domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
|
|
5437
|
+
category: error.ErrorCategory.USER,
|
|
5438
|
+
details: {
|
|
5439
|
+
remainingEvents: this.buffer.totalSize
|
|
5440
|
+
}
|
|
5441
|
+
},
|
|
5442
|
+
error$1
|
|
5443
|
+
);
|
|
5444
|
+
this.logger.trackException(mastraError);
|
|
5445
|
+
this.logger.error("Failed to flush remaining events during shutdown", mastraError);
|
|
5352
5446
|
}
|
|
5353
5447
|
this.logger.info("CloudExporter shutdown complete");
|
|
5354
5448
|
}
|
|
@@ -5652,7 +5746,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
5652
5746
|
clearTimeout(this.#flushTimer);
|
|
5653
5747
|
}
|
|
5654
5748
|
this.#flushTimer = setTimeout(() => {
|
|
5655
|
-
this.
|
|
5749
|
+
this.flushBuffer().catch((error) => {
|
|
5656
5750
|
this.logger.error("Scheduled flush failed", {
|
|
5657
5751
|
error: error instanceof Error ? error.message : String(error)
|
|
5658
5752
|
});
|
|
@@ -5786,7 +5880,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
5786
5880
|
handleBatchWithUpdatesEvent(event) {
|
|
5787
5881
|
this.addToBuffer(event);
|
|
5788
5882
|
if (this.shouldFlush()) {
|
|
5789
|
-
this.
|
|
5883
|
+
this.flushBuffer().catch((error) => {
|
|
5790
5884
|
this.logger.error("Batch flush failed", {
|
|
5791
5885
|
error: error instanceof Error ? error.message : String(error)
|
|
5792
5886
|
});
|
|
@@ -5802,7 +5896,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
5802
5896
|
if (event.type === observability.TracingEventType.SPAN_ENDED) {
|
|
5803
5897
|
this.addToBuffer(event);
|
|
5804
5898
|
if (this.shouldFlush()) {
|
|
5805
|
-
this.
|
|
5899
|
+
this.flushBuffer().catch((error) => {
|
|
5806
5900
|
this.logger.error("Batch flush failed", {
|
|
5807
5901
|
error: error instanceof Error ? error.message : String(error)
|
|
5808
5902
|
});
|
|
@@ -5819,9 +5913,9 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
5819
5913
|
return this.#config.retryDelayMs * Math.pow(2, attempt);
|
|
5820
5914
|
}
|
|
5821
5915
|
/**
|
|
5822
|
-
* Flushes the current buffer to storage with retry logic
|
|
5916
|
+
* Flushes the current buffer to storage with retry logic (internal implementation)
|
|
5823
5917
|
*/
|
|
5824
|
-
async
|
|
5918
|
+
async flushBuffer() {
|
|
5825
5919
|
if (!this.#observability) {
|
|
5826
5920
|
this.logger.debug("Cannot flush traces. Observability storage is not initialized");
|
|
5827
5921
|
return;
|
|
@@ -5928,23 +6022,25 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
5928
6022
|
break;
|
|
5929
6023
|
}
|
|
5930
6024
|
}
|
|
6025
|
+
/**
|
|
6026
|
+
* Force flush any buffered spans without shutting down the exporter.
|
|
6027
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
6028
|
+
* are exported before the runtime instance is terminated.
|
|
6029
|
+
*/
|
|
6030
|
+
async flush() {
|
|
6031
|
+
if (this.buffer.totalSize > 0) {
|
|
6032
|
+
this.logger.debug("Flushing buffered events", {
|
|
6033
|
+
bufferedEvents: this.buffer.totalSize
|
|
6034
|
+
});
|
|
6035
|
+
await this.flushBuffer();
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
5931
6038
|
async shutdown() {
|
|
5932
6039
|
if (this.#flushTimer) {
|
|
5933
6040
|
clearTimeout(this.#flushTimer);
|
|
5934
6041
|
this.#flushTimer = null;
|
|
5935
6042
|
}
|
|
5936
|
-
|
|
5937
|
-
this.logger.info("Flushing remaining events on shutdown", {
|
|
5938
|
-
remainingEvents: this.buffer.totalSize
|
|
5939
|
-
});
|
|
5940
|
-
try {
|
|
5941
|
-
await this.flush();
|
|
5942
|
-
} catch (error) {
|
|
5943
|
-
this.logger.error("Failed to flush remaining events during shutdown", {
|
|
5944
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5945
|
-
});
|
|
5946
|
-
}
|
|
5947
|
-
}
|
|
6043
|
+
await this.flush();
|
|
5948
6044
|
this.logger.info("DefaultExporter shutdown complete");
|
|
5949
6045
|
}
|
|
5950
6046
|
};
|
|
@@ -7318,6 +7414,29 @@ var BaseObservabilityInstance = class extends base.MastraBase {
|
|
|
7318
7414
|
this.logger.debug(`[Observability] Initialization started [name=${this.name}]`);
|
|
7319
7415
|
this.logger.info(`[Observability] Initialized successfully [name=${this.name}]`);
|
|
7320
7416
|
}
|
|
7417
|
+
/**
|
|
7418
|
+
* Force flush any buffered/queued spans from all exporters and the bridge
|
|
7419
|
+
* without shutting down the observability instance.
|
|
7420
|
+
*
|
|
7421
|
+
* This is useful in serverless environments (like Vercel's fluid compute) where
|
|
7422
|
+
* you need to ensure all spans are exported before the runtime instance is
|
|
7423
|
+
* terminated, while keeping the observability system active for future requests.
|
|
7424
|
+
*/
|
|
7425
|
+
async flush() {
|
|
7426
|
+
this.logger.debug(`[Observability] Flush started [name=${this.name}]`);
|
|
7427
|
+
const flushPromises = [...this.exporters.map((e) => e.flush())];
|
|
7428
|
+
if (this.config.bridge) {
|
|
7429
|
+
flushPromises.push(this.config.bridge.flush());
|
|
7430
|
+
}
|
|
7431
|
+
const results = await Promise.allSettled(flushPromises);
|
|
7432
|
+
results.forEach((result, index) => {
|
|
7433
|
+
if (result.status === "rejected") {
|
|
7434
|
+
const targetName = index < this.exporters.length ? this.exporters[index]?.name : "bridge";
|
|
7435
|
+
this.logger.error(`[Observability] Flush error [target=${targetName}]`, result.reason);
|
|
7436
|
+
}
|
|
7437
|
+
});
|
|
7438
|
+
this.logger.debug(`[Observability] Flush completed [name=${this.name}]`);
|
|
7439
|
+
}
|
|
7321
7440
|
/**
|
|
7322
7441
|
* Shutdown Observability and clean up resources
|
|
7323
7442
|
*/
|
|
@@ -7736,6 +7855,7 @@ exports.TestExporter = TestExporter;
|
|
|
7736
7855
|
exports.TraceData = TraceData;
|
|
7737
7856
|
exports.TrackingExporter = TrackingExporter;
|
|
7738
7857
|
exports.buildTracingOptions = buildTracingOptions;
|
|
7858
|
+
exports.chainFormatters = chainFormatters;
|
|
7739
7859
|
exports.deepClean = deepClean;
|
|
7740
7860
|
exports.getExternalParentId = getExternalParentId;
|
|
7741
7861
|
exports.mergeSerializationOptions = mergeSerializationOptions;
|