@mastra/observability 1.0.0-beta.8 → 1.0.0
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 +683 -0
- package/README.md +34 -79
- package/dist/config.d.ts +110 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/default.d.ts.map +1 -1
- package/dist/exporters/base.d.ts +55 -5
- 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 +3 -2
- 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 +470 -0
- package/dist/exporters/tracking.d.ts.map +1 -0
- package/dist/index.cjs +1266 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1262 -98
- package/dist/index.js.map +1 -1
- package/dist/instances/base.d.ts +33 -1
- package/dist/instances/base.d.ts.map +1 -1
- package/dist/model-tracing.d.ts.map +1 -1
- package/dist/span_processors/sensitive-data-filter.d.ts.map +1 -1
- package/dist/spans/base.d.ts +3 -0
- package/dist/spans/base.d.ts.map +1 -1
- package/dist/spans/default.d.ts.map +1 -1
- package/dist/spans/serialization.d.ts +10 -0
- package/dist/spans/serialization.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -3837,7 +3837,7 @@ ZodNaN.create = (params) => {
|
|
|
3837
3837
|
...processCreateParams(params)
|
|
3838
3838
|
});
|
|
3839
3839
|
};
|
|
3840
|
-
var BRAND = Symbol("zod_brand");
|
|
3840
|
+
var BRAND = /* @__PURE__ */ Symbol("zod_brand");
|
|
3841
3841
|
var ZodBranded = class extends ZodType {
|
|
3842
3842
|
_parse(input) {
|
|
3843
3843
|
const { ctx } = this._processInputParams(input);
|
|
@@ -4074,6 +4074,12 @@ var samplingStrategySchema = external_exports.discriminatedUnion("type", [
|
|
|
4074
4074
|
sampler: external_exports.function().args(external_exports.any().optional()).returns(external_exports.boolean())
|
|
4075
4075
|
})
|
|
4076
4076
|
]);
|
|
4077
|
+
var serializationOptionsSchema = external_exports.object({
|
|
4078
|
+
maxStringLength: external_exports.number().int().positive().optional(),
|
|
4079
|
+
maxDepth: external_exports.number().int().positive().optional(),
|
|
4080
|
+
maxArrayLength: external_exports.number().int().positive().optional(),
|
|
4081
|
+
maxObjectKeys: external_exports.number().int().positive().optional()
|
|
4082
|
+
}).optional();
|
|
4077
4083
|
var observabilityInstanceConfigSchema = external_exports.object({
|
|
4078
4084
|
name: external_exports.string().min(1, "Name is required"),
|
|
4079
4085
|
serviceName: external_exports.string().min(1, "Service name is required"),
|
|
@@ -4082,7 +4088,8 @@ var observabilityInstanceConfigSchema = external_exports.object({
|
|
|
4082
4088
|
bridge: external_exports.any().optional(),
|
|
4083
4089
|
spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
|
|
4084
4090
|
includeInternalSpans: external_exports.boolean().optional(),
|
|
4085
|
-
requestContextKeys: external_exports.array(external_exports.string()).optional()
|
|
4091
|
+
requestContextKeys: external_exports.array(external_exports.string()).optional(),
|
|
4092
|
+
serializationOptions: serializationOptionsSchema
|
|
4086
4093
|
}).refine(
|
|
4087
4094
|
(data) => {
|
|
4088
4095
|
const hasExporters = data.exporters && data.exporters.length > 0;
|
|
@@ -4100,7 +4107,8 @@ var observabilityConfigValueSchema = external_exports.object({
|
|
|
4100
4107
|
bridge: external_exports.any().optional(),
|
|
4101
4108
|
spanOutputProcessors: external_exports.array(external_exports.any()).optional(),
|
|
4102
4109
|
includeInternalSpans: external_exports.boolean().optional(),
|
|
4103
|
-
requestContextKeys: external_exports.array(external_exports.string()).optional()
|
|
4110
|
+
requestContextKeys: external_exports.array(external_exports.string()).optional(),
|
|
4111
|
+
serializationOptions: serializationOptionsSchema
|
|
4104
4112
|
}).refine(
|
|
4105
4113
|
(data) => {
|
|
4106
4114
|
const hasExporters = data.exporters && data.exporters.length > 0;
|
|
@@ -4153,12 +4161,19 @@ var observabilityRegistryConfigSchema = external_exports.object({
|
|
|
4153
4161
|
var BaseExporter = class {
|
|
4154
4162
|
/** Mastra logger instance */
|
|
4155
4163
|
logger;
|
|
4164
|
+
/** Base configuration (accessible by subclasses) */
|
|
4165
|
+
baseConfig;
|
|
4156
4166
|
/** Whether this exporter is disabled */
|
|
4157
|
-
|
|
4167
|
+
#disabled = false;
|
|
4168
|
+
/** Public getter for disabled state */
|
|
4169
|
+
get isDisabled() {
|
|
4170
|
+
return this.#disabled;
|
|
4171
|
+
}
|
|
4158
4172
|
/**
|
|
4159
4173
|
* Initialize the base exporter with logger
|
|
4160
4174
|
*/
|
|
4161
4175
|
constructor(config = {}) {
|
|
4176
|
+
this.baseConfig = config;
|
|
4162
4177
|
const logLevel = this.resolveLogLevel(config.logLevel);
|
|
4163
4178
|
this.logger = config.logger ?? new ConsoleLogger({ level: logLevel, name: this.constructor.name });
|
|
4164
4179
|
}
|
|
@@ -4193,20 +4208,62 @@ var BaseExporter = class {
|
|
|
4193
4208
|
* @param reason - Reason why the exporter is disabled
|
|
4194
4209
|
*/
|
|
4195
4210
|
setDisabled(reason) {
|
|
4196
|
-
this
|
|
4211
|
+
this.#disabled = true;
|
|
4197
4212
|
this.logger.warn(`${this.name} disabled: ${reason}`);
|
|
4198
4213
|
}
|
|
4214
|
+
/**
|
|
4215
|
+
* Apply the customSpanFormatter if configured.
|
|
4216
|
+
* This is called automatically by exportTracingEvent before _exportTracingEvent.
|
|
4217
|
+
*
|
|
4218
|
+
* Supports both synchronous and asynchronous formatters. If the formatter
|
|
4219
|
+
* returns a Promise, it will be awaited.
|
|
4220
|
+
*
|
|
4221
|
+
* @param event - The incoming tracing event
|
|
4222
|
+
* @returns The (possibly modified) event to process
|
|
4223
|
+
*/
|
|
4224
|
+
async applySpanFormatter(event) {
|
|
4225
|
+
if (this.baseConfig.customSpanFormatter) {
|
|
4226
|
+
try {
|
|
4227
|
+
const formattedSpan = await this.baseConfig.customSpanFormatter(event.exportedSpan);
|
|
4228
|
+
return {
|
|
4229
|
+
...event,
|
|
4230
|
+
exportedSpan: formattedSpan
|
|
4231
|
+
};
|
|
4232
|
+
} catch (error) {
|
|
4233
|
+
this.logger.error(`${this.name}: Error in customSpanFormatter`, {
|
|
4234
|
+
error,
|
|
4235
|
+
spanId: event.exportedSpan.id,
|
|
4236
|
+
traceId: event.exportedSpan.traceId
|
|
4237
|
+
});
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
return event;
|
|
4241
|
+
}
|
|
4199
4242
|
/**
|
|
4200
4243
|
* Export a tracing event
|
|
4201
4244
|
*
|
|
4202
|
-
* This method checks if the exporter is disabled
|
|
4203
|
-
*
|
|
4245
|
+
* This method checks if the exporter is disabled, applies the customSpanFormatter,
|
|
4246
|
+
* then calls _exportTracingEvent.
|
|
4247
|
+
* Subclasses should implement _exportTracingEvent instead of overriding this method.
|
|
4204
4248
|
*/
|
|
4205
4249
|
async exportTracingEvent(event) {
|
|
4206
4250
|
if (this.isDisabled) {
|
|
4207
4251
|
return;
|
|
4208
4252
|
}
|
|
4209
|
-
await this.
|
|
4253
|
+
const processedEvent = await this.applySpanFormatter(event);
|
|
4254
|
+
await this._exportTracingEvent(processedEvent);
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Force flush any buffered/queued spans without shutting down the exporter.
|
|
4258
|
+
*
|
|
4259
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
4260
|
+
* are exported before the runtime instance is terminated, while keeping
|
|
4261
|
+
* the exporter active for future requests.
|
|
4262
|
+
*
|
|
4263
|
+
* Default implementation is a no-op. Override to add flush logic.
|
|
4264
|
+
*/
|
|
4265
|
+
async flush() {
|
|
4266
|
+
this.logger.debug(`${this.name} flush called (no-op in base class)`);
|
|
4210
4267
|
}
|
|
4211
4268
|
/**
|
|
4212
4269
|
* Shutdown the exporter and clean up resources
|
|
@@ -4217,21 +4274,990 @@ var BaseExporter = class {
|
|
|
4217
4274
|
this.logger.info(`${this.name} shutdown complete`);
|
|
4218
4275
|
}
|
|
4219
4276
|
};
|
|
4277
|
+
var TraceData = class {
|
|
4278
|
+
/** The vendor-specific root/trace object */
|
|
4279
|
+
#rootSpan;
|
|
4280
|
+
/** The span ID of the root span */
|
|
4281
|
+
#rootSpanId;
|
|
4282
|
+
/** Whether a span with isRootSpan=true has been successfully processed */
|
|
4283
|
+
#rootSpanProcessed;
|
|
4284
|
+
/** Maps eventId to vendor-specific event objects */
|
|
4285
|
+
#events;
|
|
4286
|
+
/** Maps spanId to vendor-specific span objects */
|
|
4287
|
+
#spans;
|
|
4288
|
+
/** Maps spanId to parentSpanId, representing the span hierarchy */
|
|
4289
|
+
#tree;
|
|
4290
|
+
/** Set of span IDs that have started but not yet ended */
|
|
4291
|
+
#activeSpanIds;
|
|
4292
|
+
/** Maps spanId to vendor-specific metadata */
|
|
4293
|
+
#metadata;
|
|
4294
|
+
/** Arbitrary key-value storage for per-trace data */
|
|
4295
|
+
#extraData;
|
|
4296
|
+
/** Events waiting for the root span to be processed */
|
|
4297
|
+
#waitingForRoot;
|
|
4298
|
+
/** Events waiting for specific parent spans, keyed by parentSpanId */
|
|
4299
|
+
#waitingForParent;
|
|
4300
|
+
/** When this trace data was created, used for cap enforcement */
|
|
4301
|
+
createdAt;
|
|
4302
|
+
constructor() {
|
|
4303
|
+
this.#events = /* @__PURE__ */ new Map();
|
|
4304
|
+
this.#spans = /* @__PURE__ */ new Map();
|
|
4305
|
+
this.#activeSpanIds = /* @__PURE__ */ new Set();
|
|
4306
|
+
this.#tree = /* @__PURE__ */ new Map();
|
|
4307
|
+
this.#metadata = /* @__PURE__ */ new Map();
|
|
4308
|
+
this.#extraData = /* @__PURE__ */ new Map();
|
|
4309
|
+
this.#rootSpanProcessed = false;
|
|
4310
|
+
this.#waitingForRoot = [];
|
|
4311
|
+
this.#waitingForParent = /* @__PURE__ */ new Map();
|
|
4312
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
4313
|
+
}
|
|
4314
|
+
/**
|
|
4315
|
+
* Check if this trace has a root span registered.
|
|
4316
|
+
* @returns True if addRoot() has been called
|
|
4317
|
+
*/
|
|
4318
|
+
hasRoot() {
|
|
4319
|
+
return !!this.#rootSpanId;
|
|
4320
|
+
}
|
|
4321
|
+
/**
|
|
4322
|
+
* Register the root span for this trace.
|
|
4323
|
+
* @param args.rootId - The span ID of the root span
|
|
4324
|
+
* @param args.rootData - The vendor-specific root object
|
|
4325
|
+
*/
|
|
4326
|
+
addRoot(args) {
|
|
4327
|
+
this.#rootSpanId = args.rootId;
|
|
4328
|
+
this.#rootSpan = args.rootData;
|
|
4329
|
+
this.#rootSpanProcessed = true;
|
|
4330
|
+
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Get the vendor-specific root object.
|
|
4333
|
+
* @returns The root object, or undefined if not yet set
|
|
4334
|
+
*/
|
|
4335
|
+
getRoot() {
|
|
4336
|
+
return this.#rootSpan;
|
|
4337
|
+
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Check if a span with isRootSpan=true has been successfully processed.
|
|
4340
|
+
* Set via addRoot() or markRootSpanProcessed().
|
|
4341
|
+
* @returns True if the root span has been processed
|
|
4342
|
+
*/
|
|
4343
|
+
isRootProcessed() {
|
|
4344
|
+
return this.#rootSpanProcessed;
|
|
4345
|
+
}
|
|
4346
|
+
/**
|
|
4347
|
+
* Mark that the root span has been processed.
|
|
4348
|
+
* Used by exporters with skipBuildRootTask=true where root goes through _buildSpan
|
|
4349
|
+
* instead of _buildRoot.
|
|
4350
|
+
*/
|
|
4351
|
+
markRootSpanProcessed() {
|
|
4352
|
+
this.#rootSpanProcessed = true;
|
|
4353
|
+
}
|
|
4354
|
+
/**
|
|
4355
|
+
* Store an arbitrary value in per-trace storage.
|
|
4356
|
+
* @param key - Storage key
|
|
4357
|
+
* @param value - Value to store
|
|
4358
|
+
*/
|
|
4359
|
+
setExtraValue(key, value) {
|
|
4360
|
+
this.#extraData.set(key, value);
|
|
4361
|
+
}
|
|
4362
|
+
/**
|
|
4363
|
+
* Check if a key exists in per-trace storage.
|
|
4364
|
+
* @param key - Storage key
|
|
4365
|
+
* @returns True if the key exists
|
|
4366
|
+
*/
|
|
4367
|
+
hasExtraValue(key) {
|
|
4368
|
+
return this.#extraData.has(key);
|
|
4369
|
+
}
|
|
4370
|
+
/**
|
|
4371
|
+
* Get a value from per-trace storage.
|
|
4372
|
+
* @param key - Storage key
|
|
4373
|
+
* @returns The stored value, or undefined if not found
|
|
4374
|
+
*/
|
|
4375
|
+
getExtraValue(key) {
|
|
4376
|
+
return this.#extraData.get(key);
|
|
4377
|
+
}
|
|
4378
|
+
// ============================================================================
|
|
4379
|
+
// Early Queue Methods
|
|
4380
|
+
// ============================================================================
|
|
4381
|
+
/**
|
|
4382
|
+
* Add an event to the waiting queue.
|
|
4383
|
+
* @param args.event - The tracing event to queue
|
|
4384
|
+
* @param args.waitingFor - 'root' or a specific parentSpanId
|
|
4385
|
+
* @param args.attempts - Optional: preserve attempts count when re-queuing
|
|
4386
|
+
* @param args.queuedAt - Optional: preserve original queue time when re-queuing
|
|
4387
|
+
*/
|
|
4388
|
+
addToWaitingQueue(args) {
|
|
4389
|
+
const queuedEvent = {
|
|
4390
|
+
event: args.event,
|
|
4391
|
+
waitingFor: args.waitingFor,
|
|
4392
|
+
attempts: args.attempts ?? 0,
|
|
4393
|
+
queuedAt: args.queuedAt ?? /* @__PURE__ */ new Date()
|
|
4394
|
+
};
|
|
4395
|
+
if (args.waitingFor === "root") {
|
|
4396
|
+
this.#waitingForRoot.push(queuedEvent);
|
|
4397
|
+
} else {
|
|
4398
|
+
const queue = this.#waitingForParent.get(args.waitingFor) ?? [];
|
|
4399
|
+
queue.push(queuedEvent);
|
|
4400
|
+
this.#waitingForParent.set(args.waitingFor, queue);
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
/**
|
|
4404
|
+
* Get all events waiting for the root span.
|
|
4405
|
+
* Returns a copy of the internal array.
|
|
4406
|
+
*/
|
|
4407
|
+
getEventsWaitingForRoot() {
|
|
4408
|
+
return [...this.#waitingForRoot];
|
|
4409
|
+
}
|
|
4410
|
+
/**
|
|
4411
|
+
* Get all events waiting for a specific parent span.
|
|
4412
|
+
* Returns a copy of the internal array.
|
|
4413
|
+
*/
|
|
4414
|
+
getEventsWaitingFor(args) {
|
|
4415
|
+
return [...this.#waitingForParent.get(args.spanId) ?? []];
|
|
4416
|
+
}
|
|
4417
|
+
/**
|
|
4418
|
+
* Clear the waiting-for-root queue.
|
|
4419
|
+
*/
|
|
4420
|
+
clearWaitingForRoot() {
|
|
4421
|
+
this.#waitingForRoot = [];
|
|
4422
|
+
}
|
|
4423
|
+
/**
|
|
4424
|
+
* Clear the waiting queue for a specific parent span.
|
|
4425
|
+
*/
|
|
4426
|
+
clearWaitingFor(args) {
|
|
4427
|
+
this.#waitingForParent.delete(args.spanId);
|
|
4428
|
+
}
|
|
4429
|
+
/**
|
|
4430
|
+
* Get total count of events in all waiting queues.
|
|
4431
|
+
*/
|
|
4432
|
+
waitingQueueSize() {
|
|
4433
|
+
let count = this.#waitingForRoot.length;
|
|
4434
|
+
for (const queue of this.#waitingForParent.values()) {
|
|
4435
|
+
count += queue.length;
|
|
4436
|
+
}
|
|
4437
|
+
return count;
|
|
4438
|
+
}
|
|
4439
|
+
/**
|
|
4440
|
+
* Get all queued events across all waiting queues.
|
|
4441
|
+
* Used for cleanup and logging orphaned events.
|
|
4442
|
+
* @returns Array of all queued events
|
|
4443
|
+
*/
|
|
4444
|
+
getAllQueuedEvents() {
|
|
4445
|
+
const all = [...this.#waitingForRoot];
|
|
4446
|
+
for (const queue of this.#waitingForParent.values()) {
|
|
4447
|
+
all.push(...queue);
|
|
4448
|
+
}
|
|
4449
|
+
return all;
|
|
4450
|
+
}
|
|
4451
|
+
// ============================================================================
|
|
4452
|
+
// Span Tree Methods
|
|
4453
|
+
// ============================================================================
|
|
4454
|
+
/**
|
|
4455
|
+
* Record the parent-child relationship for a span.
|
|
4456
|
+
* @param args.spanId - The child span ID
|
|
4457
|
+
* @param args.parentSpanId - The parent span ID, or undefined for root spans
|
|
4458
|
+
*/
|
|
4459
|
+
addBranch(args) {
|
|
4460
|
+
this.#tree.set(args.spanId, args.parentSpanId);
|
|
4461
|
+
}
|
|
4462
|
+
/**
|
|
4463
|
+
* Get the parent span ID for a given span.
|
|
4464
|
+
* @param args.spanId - The span ID to look up
|
|
4465
|
+
* @returns The parent span ID, or undefined if root or not found
|
|
4466
|
+
*/
|
|
4467
|
+
getParentId(args) {
|
|
4468
|
+
return this.#tree.get(args.spanId);
|
|
4469
|
+
}
|
|
4470
|
+
// ============================================================================
|
|
4471
|
+
// Span Management Methods
|
|
4472
|
+
// ============================================================================
|
|
4473
|
+
/**
|
|
4474
|
+
* Register a span and mark it as active.
|
|
4475
|
+
* @param args.spanId - The span ID
|
|
4476
|
+
* @param args.spanData - The vendor-specific span object
|
|
4477
|
+
*/
|
|
4478
|
+
addSpan(args) {
|
|
4479
|
+
this.#spans.set(args.spanId, args.spanData);
|
|
4480
|
+
this.#activeSpanIds.add(args.spanId);
|
|
4481
|
+
}
|
|
4482
|
+
/**
|
|
4483
|
+
* Check if a span exists (regardless of active state).
|
|
4484
|
+
* @param args.spanId - The span ID to check
|
|
4485
|
+
* @returns True if the span exists
|
|
4486
|
+
*/
|
|
4487
|
+
hasSpan(args) {
|
|
4488
|
+
return this.#spans.has(args.spanId);
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
* Get a span by ID.
|
|
4492
|
+
* @param args.spanId - The span ID to look up
|
|
4493
|
+
* @returns The vendor-specific span object, or undefined if not found
|
|
4494
|
+
*/
|
|
4495
|
+
getSpan(args) {
|
|
4496
|
+
return this.#spans.get(args.spanId);
|
|
4497
|
+
}
|
|
4498
|
+
/**
|
|
4499
|
+
* Mark a span as ended (no longer active).
|
|
4500
|
+
* @param args.spanId - The span ID to mark as ended
|
|
4501
|
+
*/
|
|
4502
|
+
endSpan(args) {
|
|
4503
|
+
this.#activeSpanIds.delete(args.spanId);
|
|
4504
|
+
}
|
|
4505
|
+
/**
|
|
4506
|
+
* Check if a span is currently active (started but not ended).
|
|
4507
|
+
* @param args.spanId - The span ID to check
|
|
4508
|
+
* @returns True if the span is active
|
|
4509
|
+
*/
|
|
4510
|
+
isActiveSpan(args) {
|
|
4511
|
+
return this.#activeSpanIds.has(args.spanId);
|
|
4512
|
+
}
|
|
4513
|
+
/**
|
|
4514
|
+
* Get the count of currently active spans.
|
|
4515
|
+
* @returns Number of active spans
|
|
4516
|
+
*/
|
|
4517
|
+
activeSpanCount() {
|
|
4518
|
+
return this.#activeSpanIds.size;
|
|
4519
|
+
}
|
|
4520
|
+
/**
|
|
4521
|
+
* Get all active span IDs.
|
|
4522
|
+
* @returns Array of active span IDs
|
|
4523
|
+
*/
|
|
4524
|
+
get activeSpanIds() {
|
|
4525
|
+
return [...this.#activeSpanIds];
|
|
4526
|
+
}
|
|
4527
|
+
// ============================================================================
|
|
4528
|
+
// Event Management Methods
|
|
4529
|
+
// ============================================================================
|
|
4530
|
+
/**
|
|
4531
|
+
* Register an event.
|
|
4532
|
+
* @param args.eventId - The event ID
|
|
4533
|
+
* @param args.eventData - The vendor-specific event object
|
|
4534
|
+
*/
|
|
4535
|
+
addEvent(args) {
|
|
4536
|
+
this.#events.set(args.eventId, args.eventData);
|
|
4537
|
+
}
|
|
4538
|
+
// ============================================================================
|
|
4539
|
+
// Metadata Methods
|
|
4540
|
+
// ============================================================================
|
|
4541
|
+
/**
|
|
4542
|
+
* Store vendor-specific metadata for a span.
|
|
4543
|
+
* Note: This overwrites any existing metadata for the span.
|
|
4544
|
+
* @param args.spanId - The span ID
|
|
4545
|
+
* @param args.metadata - The vendor-specific metadata
|
|
4546
|
+
*/
|
|
4547
|
+
addMetadata(args) {
|
|
4548
|
+
this.#metadata.set(args.spanId, args.metadata);
|
|
4549
|
+
}
|
|
4550
|
+
/**
|
|
4551
|
+
* Get vendor-specific metadata for a span.
|
|
4552
|
+
* @param args.spanId - The span ID
|
|
4553
|
+
* @returns The metadata, or undefined if not found
|
|
4554
|
+
*/
|
|
4555
|
+
getMetadata(args) {
|
|
4556
|
+
return this.#metadata.get(args.spanId);
|
|
4557
|
+
}
|
|
4558
|
+
// ============================================================================
|
|
4559
|
+
// Parent Lookup Methods
|
|
4560
|
+
// ============================================================================
|
|
4561
|
+
/**
|
|
4562
|
+
* Get the parent span or event for a given span.
|
|
4563
|
+
* Looks up in both spans and events maps.
|
|
4564
|
+
* @param args.span - The span to find the parent for
|
|
4565
|
+
* @returns The parent span/event object, or undefined if root or not found
|
|
4566
|
+
*/
|
|
4567
|
+
getParent(args) {
|
|
4568
|
+
const parentId = args.span.parentSpanId;
|
|
4569
|
+
if (parentId) {
|
|
4570
|
+
if (this.#spans.has(parentId)) {
|
|
4571
|
+
return this.#spans.get(parentId);
|
|
4572
|
+
}
|
|
4573
|
+
if (this.#events.has(parentId)) {
|
|
4574
|
+
return this.#events.get(parentId);
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
return void 0;
|
|
4578
|
+
}
|
|
4579
|
+
/**
|
|
4580
|
+
* Get the parent span/event or fall back to the root object.
|
|
4581
|
+
* Useful for vendors that attach child spans to either parent spans or the trace root.
|
|
4582
|
+
* @param args.span - The span to find the parent for
|
|
4583
|
+
* @returns The parent span/event, the root object, or undefined
|
|
4584
|
+
*/
|
|
4585
|
+
getParentOrRoot(args) {
|
|
4586
|
+
return this.getParent(args) ?? this.getRoot();
|
|
4587
|
+
}
|
|
4588
|
+
};
|
|
4589
|
+
var DEFAULT_EARLY_QUEUE_MAX_ATTEMPTS = 5;
|
|
4590
|
+
var DEFAULT_EARLY_QUEUE_TTL_MS = 3e4;
|
|
4591
|
+
var DEFAULT_TRACE_CLEANUP_DELAY_MS = 3e4;
|
|
4592
|
+
var DEFAULT_MAX_PENDING_CLEANUP_TRACES = 100;
|
|
4593
|
+
var DEFAULT_MAX_TOTAL_TRACES = 500;
|
|
4594
|
+
var TrackingExporter = class extends BaseExporter {
|
|
4595
|
+
/** Map of traceId to per-trace data container */
|
|
4596
|
+
#traceMap = /* @__PURE__ */ new Map();
|
|
4597
|
+
/** Flag to prevent processing during shutdown */
|
|
4598
|
+
#shutdownStarted = false;
|
|
4599
|
+
/** Flag to prevent concurrent hard cap enforcement */
|
|
4600
|
+
#hardCapEnforcementInProgress = false;
|
|
4601
|
+
/** Map of traceId to scheduled cleanup timeout */
|
|
4602
|
+
#pendingCleanups = /* @__PURE__ */ new Map();
|
|
4603
|
+
// Note: #traceMap maintains insertion order (JS Map spec), so we use
|
|
4604
|
+
// #traceMap.keys() to iterate traces oldest-first for cap enforcement.
|
|
4605
|
+
/** Subclass configuration with resolved values */
|
|
4606
|
+
config;
|
|
4607
|
+
/** Maximum attempts to process a queued event before dropping */
|
|
4608
|
+
#earlyQueueMaxAttempts;
|
|
4609
|
+
/** TTL in milliseconds for queued events */
|
|
4610
|
+
#earlyQueueTTLMs;
|
|
4611
|
+
/** Delay before cleaning up completed traces */
|
|
4612
|
+
#traceCleanupDelayMs;
|
|
4613
|
+
/** Soft cap on traces awaiting cleanup */
|
|
4614
|
+
#maxPendingCleanupTraces;
|
|
4615
|
+
/** Hard cap on total traces (will abort active spans if exceeded) */
|
|
4616
|
+
#maxTotalTraces;
|
|
4617
|
+
constructor(config) {
|
|
4618
|
+
super(config);
|
|
4619
|
+
this.config = config;
|
|
4620
|
+
this.#earlyQueueMaxAttempts = config.earlyQueueMaxAttempts ?? DEFAULT_EARLY_QUEUE_MAX_ATTEMPTS;
|
|
4621
|
+
this.#earlyQueueTTLMs = config.earlyQueueTTLMs ?? DEFAULT_EARLY_QUEUE_TTL_MS;
|
|
4622
|
+
this.#traceCleanupDelayMs = config.traceCleanupDelayMs ?? DEFAULT_TRACE_CLEANUP_DELAY_MS;
|
|
4623
|
+
this.#maxPendingCleanupTraces = config.maxPendingCleanupTraces ?? DEFAULT_MAX_PENDING_CLEANUP_TRACES;
|
|
4624
|
+
this.#maxTotalTraces = config.maxTotalTraces ?? DEFAULT_MAX_TOTAL_TRACES;
|
|
4625
|
+
}
|
|
4626
|
+
// ============================================================================
|
|
4627
|
+
// Early Queue Processing
|
|
4628
|
+
// ============================================================================
|
|
4629
|
+
/**
|
|
4630
|
+
* Schedule async processing of events waiting for root span.
|
|
4631
|
+
* Called after root span is successfully processed.
|
|
4632
|
+
*/
|
|
4633
|
+
#scheduleProcessWaitingForRoot(traceId) {
|
|
4634
|
+
setImmediate(() => {
|
|
4635
|
+
this.#processWaitingForRoot(traceId).catch((error) => {
|
|
4636
|
+
this.logger.error(`${this.name}: Error processing waiting-for-root queue`, { error, traceId });
|
|
4637
|
+
});
|
|
4638
|
+
});
|
|
4639
|
+
}
|
|
4640
|
+
/**
|
|
4641
|
+
* Schedule async processing of events waiting for a specific parent span.
|
|
4642
|
+
* Called after a span/event is successfully created.
|
|
4643
|
+
*/
|
|
4644
|
+
#scheduleProcessWaitingFor(traceId, spanId) {
|
|
4645
|
+
setImmediate(() => {
|
|
4646
|
+
this.#processWaitingFor(traceId, spanId).catch((error) => {
|
|
4647
|
+
this.logger.error(`${this.name}: Error processing waiting queue`, { error, traceId, spanId });
|
|
4648
|
+
});
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4651
|
+
/**
|
|
4652
|
+
* Process all events waiting for root span.
|
|
4653
|
+
*/
|
|
4654
|
+
async #processWaitingForRoot(traceId) {
|
|
4655
|
+
if (this.#shutdownStarted) return;
|
|
4656
|
+
const traceData = this.#traceMap.get(traceId);
|
|
4657
|
+
if (!traceData) return;
|
|
4658
|
+
const queue = traceData.getEventsWaitingForRoot();
|
|
4659
|
+
if (queue.length === 0) return;
|
|
4660
|
+
this.logger.debug(`${this.name}: Processing ${queue.length} events waiting for root`, { traceId });
|
|
4661
|
+
const toKeep = [];
|
|
4662
|
+
const now = Date.now();
|
|
4663
|
+
for (const queuedEvent of queue) {
|
|
4664
|
+
if (now - queuedEvent.queuedAt.getTime() > this.#earlyQueueTTLMs) {
|
|
4665
|
+
this.logger.warn(`${this.name}: Dropping event due to TTL expiry`, {
|
|
4666
|
+
traceId,
|
|
4667
|
+
spanId: queuedEvent.event.exportedSpan.id,
|
|
4668
|
+
waitingFor: queuedEvent.waitingFor,
|
|
4669
|
+
queuedAt: queuedEvent.queuedAt,
|
|
4670
|
+
attempts: queuedEvent.attempts
|
|
4671
|
+
});
|
|
4672
|
+
continue;
|
|
4673
|
+
}
|
|
4674
|
+
if (queuedEvent.attempts >= this.#earlyQueueMaxAttempts) {
|
|
4675
|
+
this.logger.warn(`${this.name}: Dropping event due to max attempts`, {
|
|
4676
|
+
traceId,
|
|
4677
|
+
spanId: queuedEvent.event.exportedSpan.id,
|
|
4678
|
+
waitingFor: queuedEvent.waitingFor,
|
|
4679
|
+
attempts: queuedEvent.attempts
|
|
4680
|
+
});
|
|
4681
|
+
continue;
|
|
4682
|
+
}
|
|
4683
|
+
queuedEvent.attempts++;
|
|
4684
|
+
const processed = await this.#tryProcessQueuedEvent(queuedEvent, traceData);
|
|
4685
|
+
if (!processed) {
|
|
4686
|
+
const parentId = queuedEvent.event.exportedSpan.parentSpanId;
|
|
4687
|
+
if (parentId && traceData.isRootProcessed()) {
|
|
4688
|
+
traceData.addToWaitingQueue({
|
|
4689
|
+
event: queuedEvent.event,
|
|
4690
|
+
waitingFor: parentId,
|
|
4691
|
+
attempts: queuedEvent.attempts,
|
|
4692
|
+
queuedAt: queuedEvent.queuedAt
|
|
4693
|
+
});
|
|
4694
|
+
} else {
|
|
4695
|
+
toKeep.push(queuedEvent);
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
traceData.clearWaitingForRoot();
|
|
4700
|
+
for (const event of toKeep) {
|
|
4701
|
+
traceData.addToWaitingQueue({
|
|
4702
|
+
event: event.event,
|
|
4703
|
+
waitingFor: "root",
|
|
4704
|
+
attempts: event.attempts,
|
|
4705
|
+
queuedAt: event.queuedAt
|
|
4706
|
+
});
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
/**
|
|
4710
|
+
* Process events waiting for a specific parent span.
|
|
4711
|
+
*/
|
|
4712
|
+
async #processWaitingFor(traceId, spanId) {
|
|
4713
|
+
if (this.#shutdownStarted) return;
|
|
4714
|
+
const traceData = this.#traceMap.get(traceId);
|
|
4715
|
+
if (!traceData) return;
|
|
4716
|
+
const queue = traceData.getEventsWaitingFor({ spanId });
|
|
4717
|
+
if (queue.length === 0) return;
|
|
4718
|
+
this.logger.debug(`${this.name}: Processing ${queue.length} events waiting for span`, { traceId, spanId });
|
|
4719
|
+
const toKeep = [];
|
|
4720
|
+
const now = Date.now();
|
|
4721
|
+
for (const queuedEvent of queue) {
|
|
4722
|
+
if (now - queuedEvent.queuedAt.getTime() > this.#earlyQueueTTLMs) {
|
|
4723
|
+
this.logger.warn(`${this.name}: Dropping event due to TTL expiry`, {
|
|
4724
|
+
traceId,
|
|
4725
|
+
spanId: queuedEvent.event.exportedSpan.id,
|
|
4726
|
+
waitingFor: queuedEvent.waitingFor,
|
|
4727
|
+
queuedAt: queuedEvent.queuedAt,
|
|
4728
|
+
attempts: queuedEvent.attempts
|
|
4729
|
+
});
|
|
4730
|
+
continue;
|
|
4731
|
+
}
|
|
4732
|
+
if (queuedEvent.attempts >= this.#earlyQueueMaxAttempts) {
|
|
4733
|
+
this.logger.warn(`${this.name}: Dropping event due to max attempts`, {
|
|
4734
|
+
traceId,
|
|
4735
|
+
spanId: queuedEvent.event.exportedSpan.id,
|
|
4736
|
+
waitingFor: queuedEvent.waitingFor,
|
|
4737
|
+
attempts: queuedEvent.attempts
|
|
4738
|
+
});
|
|
4739
|
+
continue;
|
|
4740
|
+
}
|
|
4741
|
+
queuedEvent.attempts++;
|
|
4742
|
+
const processed = await this.#tryProcessQueuedEvent(queuedEvent, traceData);
|
|
4743
|
+
if (!processed) {
|
|
4744
|
+
toKeep.push(queuedEvent);
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
traceData.clearWaitingFor({ spanId });
|
|
4748
|
+
for (const event of toKeep) {
|
|
4749
|
+
traceData.addToWaitingQueue({
|
|
4750
|
+
event: event.event,
|
|
4751
|
+
waitingFor: spanId,
|
|
4752
|
+
attempts: event.attempts,
|
|
4753
|
+
queuedAt: event.queuedAt
|
|
4754
|
+
});
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
/**
|
|
4758
|
+
* Try to process a queued event.
|
|
4759
|
+
* Returns true if successfully processed, false if still waiting for dependencies.
|
|
4760
|
+
*/
|
|
4761
|
+
async #tryProcessQueuedEvent(queuedEvent, traceData) {
|
|
4762
|
+
const { event } = queuedEvent;
|
|
4763
|
+
const { exportedSpan } = event;
|
|
4764
|
+
const method = this.getMethod(event);
|
|
4765
|
+
try {
|
|
4766
|
+
switch (method) {
|
|
4767
|
+
case "handleEventSpan": {
|
|
4768
|
+
traceData.addBranch({ spanId: exportedSpan.id, parentSpanId: exportedSpan.parentSpanId });
|
|
4769
|
+
const eventData = await this._buildEvent({ span: exportedSpan, traceData });
|
|
4770
|
+
if (eventData) {
|
|
4771
|
+
if (!this.skipCachingEventSpans) {
|
|
4772
|
+
traceData.addEvent({ eventId: exportedSpan.id, eventData });
|
|
4773
|
+
}
|
|
4774
|
+
this.#scheduleProcessWaitingFor(exportedSpan.traceId, exportedSpan.id);
|
|
4775
|
+
return true;
|
|
4776
|
+
}
|
|
4777
|
+
return false;
|
|
4778
|
+
}
|
|
4779
|
+
case "handleSpanStart": {
|
|
4780
|
+
traceData.addBranch({ spanId: exportedSpan.id, parentSpanId: exportedSpan.parentSpanId });
|
|
4781
|
+
const spanData = await this._buildSpan({ span: exportedSpan, traceData });
|
|
4782
|
+
if (spanData) {
|
|
4783
|
+
traceData.addSpan({ spanId: exportedSpan.id, spanData });
|
|
4784
|
+
if (exportedSpan.isRootSpan) {
|
|
4785
|
+
traceData.markRootSpanProcessed();
|
|
4786
|
+
}
|
|
4787
|
+
this.#scheduleProcessWaitingFor(exportedSpan.traceId, exportedSpan.id);
|
|
4788
|
+
return true;
|
|
4789
|
+
}
|
|
4790
|
+
return false;
|
|
4791
|
+
}
|
|
4792
|
+
case "handleSpanUpdate": {
|
|
4793
|
+
await this._updateSpan({ span: exportedSpan, traceData });
|
|
4794
|
+
return true;
|
|
4795
|
+
}
|
|
4796
|
+
case "handleSpanEnd": {
|
|
4797
|
+
traceData.endSpan({ spanId: exportedSpan.id });
|
|
4798
|
+
await this._finishSpan({ span: exportedSpan, traceData });
|
|
4799
|
+
if (traceData.activeSpanCount() === 0) {
|
|
4800
|
+
this.#scheduleCleanup(exportedSpan.traceId);
|
|
4801
|
+
}
|
|
4802
|
+
return true;
|
|
4803
|
+
}
|
|
4804
|
+
default:
|
|
4805
|
+
return false;
|
|
4806
|
+
}
|
|
4807
|
+
} catch (error) {
|
|
4808
|
+
this.logger.error(`${this.name}: Error processing queued event`, { error, event, method });
|
|
4809
|
+
return false;
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
// ============================================================================
|
|
4813
|
+
// Delayed Cleanup
|
|
4814
|
+
// ============================================================================
|
|
4815
|
+
/**
|
|
4816
|
+
* Schedule cleanup of trace data after a delay.
|
|
4817
|
+
* Allows late-arriving data to still be processed.
|
|
4818
|
+
*/
|
|
4819
|
+
#scheduleCleanup(traceId) {
|
|
4820
|
+
this.#cancelScheduledCleanup(traceId);
|
|
4821
|
+
this.logger.debug(`${this.name}: Scheduling cleanup in ${this.#traceCleanupDelayMs}ms`, { traceId });
|
|
4822
|
+
const timeout = setTimeout(() => {
|
|
4823
|
+
this.#pendingCleanups.delete(traceId);
|
|
4824
|
+
this.#performCleanup(traceId);
|
|
4825
|
+
}, this.#traceCleanupDelayMs);
|
|
4826
|
+
this.#pendingCleanups.set(traceId, timeout);
|
|
4827
|
+
this.#enforcePendingCleanupCap();
|
|
4828
|
+
}
|
|
4829
|
+
/**
|
|
4830
|
+
* Cancel a scheduled cleanup for a trace.
|
|
4831
|
+
*/
|
|
4832
|
+
#cancelScheduledCleanup(traceId) {
|
|
4833
|
+
const existingTimeout = this.#pendingCleanups.get(traceId);
|
|
4834
|
+
if (existingTimeout) {
|
|
4835
|
+
clearTimeout(existingTimeout);
|
|
4836
|
+
this.#pendingCleanups.delete(traceId);
|
|
4837
|
+
this.logger.debug(`${this.name}: Cancelled scheduled cleanup`, { traceId });
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
/**
|
|
4841
|
+
* Perform the actual cleanup of trace data.
|
|
4842
|
+
*/
|
|
4843
|
+
#performCleanup(traceId) {
|
|
4844
|
+
const traceData = this.#traceMap.get(traceId);
|
|
4845
|
+
if (!traceData) return;
|
|
4846
|
+
const orphanedEvents = traceData.getAllQueuedEvents();
|
|
4847
|
+
if (orphanedEvents.length > 0) {
|
|
4848
|
+
this.logger.warn(`${this.name}: Dropping ${orphanedEvents.length} orphaned events on cleanup`, {
|
|
4849
|
+
traceId,
|
|
4850
|
+
orphanedEvents: orphanedEvents.map((e) => ({
|
|
4851
|
+
spanId: e.event.exportedSpan.id,
|
|
4852
|
+
waitingFor: e.waitingFor,
|
|
4853
|
+
attempts: e.attempts,
|
|
4854
|
+
queuedAt: e.queuedAt
|
|
4855
|
+
}))
|
|
4856
|
+
});
|
|
4857
|
+
}
|
|
4858
|
+
this.#traceMap.delete(traceId);
|
|
4859
|
+
this.logger.debug(`${this.name}: Cleaned up trace data`, { traceId });
|
|
4860
|
+
}
|
|
4861
|
+
// ============================================================================
|
|
4862
|
+
// Cap Enforcement
|
|
4863
|
+
// ============================================================================
|
|
4864
|
+
/**
|
|
4865
|
+
* Enforce soft cap on pending cleanup traces.
|
|
4866
|
+
* Only removes traces with activeSpanCount == 0.
|
|
4867
|
+
*/
|
|
4868
|
+
#enforcePendingCleanupCap() {
|
|
4869
|
+
if (this.#pendingCleanups.size <= this.#maxPendingCleanupTraces) {
|
|
4870
|
+
return;
|
|
4871
|
+
}
|
|
4872
|
+
const toRemove = this.#pendingCleanups.size - this.#maxPendingCleanupTraces;
|
|
4873
|
+
this.logger.warn(`${this.name}: Pending cleanup cap exceeded, force-cleaning ${toRemove} traces`, {
|
|
4874
|
+
pendingCount: this.#pendingCleanups.size,
|
|
4875
|
+
cap: this.#maxPendingCleanupTraces
|
|
4876
|
+
});
|
|
4877
|
+
let removed = 0;
|
|
4878
|
+
for (const traceId of this.#traceMap.keys()) {
|
|
4879
|
+
if (removed >= toRemove) break;
|
|
4880
|
+
if (this.#pendingCleanups.has(traceId)) {
|
|
4881
|
+
this.#cancelScheduledCleanup(traceId);
|
|
4882
|
+
this.#performCleanup(traceId);
|
|
4883
|
+
removed++;
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
/**
|
|
4888
|
+
* Enforce hard cap on total traces.
|
|
4889
|
+
* Will kill even active traces if necessary.
|
|
4890
|
+
* Uses a flag to prevent concurrent executions when called fire-and-forget.
|
|
4891
|
+
*/
|
|
4892
|
+
async #enforceHardCap() {
|
|
4893
|
+
if (this.#traceMap.size <= this.#maxTotalTraces || this.#hardCapEnforcementInProgress) {
|
|
4894
|
+
return;
|
|
4895
|
+
}
|
|
4896
|
+
this.#hardCapEnforcementInProgress = true;
|
|
4897
|
+
try {
|
|
4898
|
+
if (this.#traceMap.size <= this.#maxTotalTraces) {
|
|
4899
|
+
return;
|
|
4900
|
+
}
|
|
4901
|
+
const toRemove = this.#traceMap.size - this.#maxTotalTraces;
|
|
4902
|
+
this.logger.warn(`${this.name}: Total trace cap exceeded, killing ${toRemove} oldest traces`, {
|
|
4903
|
+
traceCount: this.#traceMap.size,
|
|
4904
|
+
cap: this.#maxTotalTraces
|
|
4905
|
+
});
|
|
4906
|
+
const reason = {
|
|
4907
|
+
id: "TRACE_CAP_EXCEEDED",
|
|
4908
|
+
message: "Trace killed due to memory cap enforcement.",
|
|
4909
|
+
domain: "MASTRA_OBSERVABILITY",
|
|
4910
|
+
category: "SYSTEM"
|
|
4911
|
+
};
|
|
4912
|
+
let removed = 0;
|
|
4913
|
+
for (const traceId of [...this.#traceMap.keys()]) {
|
|
4914
|
+
if (removed >= toRemove) break;
|
|
4915
|
+
const traceData = this.#traceMap.get(traceId);
|
|
4916
|
+
if (traceData) {
|
|
4917
|
+
for (const spanId of traceData.activeSpanIds) {
|
|
4918
|
+
const span = traceData.getSpan({ spanId });
|
|
4919
|
+
if (span) {
|
|
4920
|
+
await this._abortSpan({ span, traceData, reason });
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
this.#cancelScheduledCleanup(traceId);
|
|
4924
|
+
this.#performCleanup(traceId);
|
|
4925
|
+
removed++;
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
} finally {
|
|
4929
|
+
this.#hardCapEnforcementInProgress = false;
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
// ============================================================================
|
|
4933
|
+
// Lifecycle Hooks (Override in subclass)
|
|
4934
|
+
// ============================================================================
|
|
4935
|
+
/**
|
|
4936
|
+
* Hook called before processing each tracing event.
|
|
4937
|
+
* Override to transform or enrich the event before processing.
|
|
4938
|
+
*
|
|
4939
|
+
* Note: The customSpanFormatter is applied at the BaseExporter level before this hook.
|
|
4940
|
+
* Subclasses can override this to add additional pre-processing logic.
|
|
4941
|
+
*
|
|
4942
|
+
* @param event - The incoming tracing event
|
|
4943
|
+
* @returns The (possibly modified) event to process
|
|
4944
|
+
*/
|
|
4945
|
+
async _preExportTracingEvent(event) {
|
|
4946
|
+
return event;
|
|
4947
|
+
}
|
|
4948
|
+
/**
|
|
4949
|
+
* Hook called after processing each tracing event.
|
|
4950
|
+
* Override to perform post-processing actions like flushing.
|
|
4951
|
+
*/
|
|
4952
|
+
async _postExportTracingEvent() {
|
|
4953
|
+
}
|
|
4954
|
+
// ============================================================================
|
|
4955
|
+
// Behavior Flags (Override in subclass as needed)
|
|
4956
|
+
// ============================================================================
|
|
4957
|
+
/**
|
|
4958
|
+
* If true, skip calling _buildRoot and let root spans go through _buildSpan.
|
|
4959
|
+
* Use when the vendor doesn't have a separate trace/root concept.
|
|
4960
|
+
* @default false
|
|
4961
|
+
*/
|
|
4962
|
+
skipBuildRootTask = false;
|
|
4963
|
+
/**
|
|
4964
|
+
* If true, skip processing span_updated events entirely.
|
|
4965
|
+
* Use when the vendor doesn't support incremental span updates.
|
|
4966
|
+
* @default false
|
|
4967
|
+
*/
|
|
4968
|
+
skipSpanUpdateEvents = false;
|
|
4969
|
+
/**
|
|
4970
|
+
* If true, don't cache event spans in TraceData.
|
|
4971
|
+
* Use when events can't be parents of other spans.
|
|
4972
|
+
* @default false
|
|
4973
|
+
*/
|
|
4974
|
+
skipCachingEventSpans = false;
|
|
4975
|
+
getMethod(event) {
|
|
4976
|
+
if (event.exportedSpan.isEvent) {
|
|
4977
|
+
return "handleEventSpan";
|
|
4978
|
+
}
|
|
4979
|
+
const eventType = event.type;
|
|
4980
|
+
switch (eventType) {
|
|
4981
|
+
case TracingEventType.SPAN_STARTED:
|
|
4982
|
+
return "handleSpanStart";
|
|
4983
|
+
case TracingEventType.SPAN_UPDATED:
|
|
4984
|
+
return "handleSpanUpdate";
|
|
4985
|
+
case TracingEventType.SPAN_ENDED:
|
|
4986
|
+
return "handleSpanEnd";
|
|
4987
|
+
default: {
|
|
4988
|
+
const _exhaustiveCheck = eventType;
|
|
4989
|
+
throw new Error(`Unhandled event type: ${_exhaustiveCheck}`);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
async _exportTracingEvent(event) {
|
|
4994
|
+
if (this.#shutdownStarted) {
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
const method = this.getMethod(event);
|
|
4998
|
+
if (method == "handleSpanUpdate" && this.skipSpanUpdateEvents) {
|
|
4999
|
+
return;
|
|
5000
|
+
}
|
|
5001
|
+
const traceId = event.exportedSpan.traceId;
|
|
5002
|
+
const traceData = this.getTraceData({ traceId, method });
|
|
5003
|
+
const { exportedSpan } = await this._preExportTracingEvent(event);
|
|
5004
|
+
if (!this.skipBuildRootTask && !traceData.hasRoot()) {
|
|
5005
|
+
if (exportedSpan.isRootSpan) {
|
|
5006
|
+
this.logger.debug(`${this.name}: Building root`, {
|
|
5007
|
+
traceId: exportedSpan.traceId,
|
|
5008
|
+
spanId: exportedSpan.id
|
|
5009
|
+
});
|
|
5010
|
+
const rootData = await this._buildRoot({ span: exportedSpan, traceData });
|
|
5011
|
+
if (rootData) {
|
|
5012
|
+
this.logger.debug(`${this.name}: Adding root`, {
|
|
5013
|
+
traceId: exportedSpan.traceId,
|
|
5014
|
+
spanId: exportedSpan.id
|
|
5015
|
+
});
|
|
5016
|
+
traceData.addRoot({ rootId: exportedSpan.id, rootData });
|
|
5017
|
+
this.#scheduleProcessWaitingForRoot(traceId);
|
|
5018
|
+
}
|
|
5019
|
+
} else {
|
|
5020
|
+
this.logger.debug(`${this.name}: Root does not exist, adding span to waiting queue.`, {
|
|
5021
|
+
traceId: exportedSpan.traceId,
|
|
5022
|
+
spanId: exportedSpan.id
|
|
5023
|
+
});
|
|
5024
|
+
traceData.addToWaitingQueue({ event, waitingFor: "root" });
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
if (exportedSpan.metadata && this.name in exportedSpan.metadata) {
|
|
5029
|
+
const metadata = exportedSpan.metadata[this.name];
|
|
5030
|
+
this.logger.debug(`${this.name}: Found provider metadata in span`, {
|
|
5031
|
+
traceId: exportedSpan.traceId,
|
|
5032
|
+
spanId: exportedSpan.id,
|
|
5033
|
+
metadata
|
|
5034
|
+
});
|
|
5035
|
+
traceData.addMetadata({ spanId: exportedSpan.id, metadata });
|
|
5036
|
+
}
|
|
5037
|
+
try {
|
|
5038
|
+
switch (method) {
|
|
5039
|
+
case "handleEventSpan": {
|
|
5040
|
+
this.logger.debug(`${this.name}: handling event`, {
|
|
5041
|
+
traceId: exportedSpan.traceId,
|
|
5042
|
+
spanId: exportedSpan.id
|
|
5043
|
+
});
|
|
5044
|
+
traceData.addBranch({ spanId: exportedSpan.id, parentSpanId: exportedSpan.parentSpanId });
|
|
5045
|
+
const eventData = await this._buildEvent({ span: exportedSpan, traceData });
|
|
5046
|
+
if (eventData) {
|
|
5047
|
+
if (!this.skipCachingEventSpans) {
|
|
5048
|
+
this.logger.debug(`${this.name}: adding event to traceData`, {
|
|
5049
|
+
traceId: exportedSpan.traceId,
|
|
5050
|
+
spanId: exportedSpan.id
|
|
5051
|
+
});
|
|
5052
|
+
traceData.addEvent({ eventId: exportedSpan.id, eventData });
|
|
5053
|
+
}
|
|
5054
|
+
this.#scheduleProcessWaitingFor(traceId, exportedSpan.id);
|
|
5055
|
+
} else {
|
|
5056
|
+
const parentId = exportedSpan.parentSpanId;
|
|
5057
|
+
this.logger.debug(`${this.name}: adding event to waiting queue`, {
|
|
5058
|
+
traceId: exportedSpan.traceId,
|
|
5059
|
+
spanId: exportedSpan.id,
|
|
5060
|
+
waitingFor: parentId ?? "root"
|
|
5061
|
+
});
|
|
5062
|
+
traceData.addToWaitingQueue({ event, waitingFor: parentId ?? "root" });
|
|
5063
|
+
}
|
|
5064
|
+
break;
|
|
5065
|
+
}
|
|
5066
|
+
case "handleSpanStart": {
|
|
5067
|
+
this.logger.debug(`${this.name}: handling span start`, {
|
|
5068
|
+
traceId: exportedSpan.traceId,
|
|
5069
|
+
spanId: exportedSpan.id
|
|
5070
|
+
});
|
|
5071
|
+
traceData.addBranch({ spanId: exportedSpan.id, parentSpanId: exportedSpan.parentSpanId });
|
|
5072
|
+
const spanData = await this._buildSpan({ span: exportedSpan, traceData });
|
|
5073
|
+
if (spanData) {
|
|
5074
|
+
this.logger.debug(`${this.name}: adding span to traceData`, {
|
|
5075
|
+
traceId: exportedSpan.traceId,
|
|
5076
|
+
spanId: exportedSpan.id
|
|
5077
|
+
});
|
|
5078
|
+
traceData.addSpan({ spanId: exportedSpan.id, spanData });
|
|
5079
|
+
if (exportedSpan.isRootSpan) {
|
|
5080
|
+
traceData.markRootSpanProcessed();
|
|
5081
|
+
this.#scheduleProcessWaitingForRoot(traceId);
|
|
5082
|
+
}
|
|
5083
|
+
this.#scheduleProcessWaitingFor(traceId, exportedSpan.id);
|
|
5084
|
+
} else {
|
|
5085
|
+
const parentId = exportedSpan.parentSpanId;
|
|
5086
|
+
this.logger.debug(`${this.name}: adding span to waiting queue`, {
|
|
5087
|
+
traceId: exportedSpan.traceId,
|
|
5088
|
+
waitingFor: parentId ?? "root"
|
|
5089
|
+
});
|
|
5090
|
+
traceData.addToWaitingQueue({ event, waitingFor: parentId ?? "root" });
|
|
5091
|
+
}
|
|
5092
|
+
break;
|
|
5093
|
+
}
|
|
5094
|
+
case "handleSpanUpdate":
|
|
5095
|
+
this.logger.debug(`${this.name}: handling span update`, {
|
|
5096
|
+
traceId: exportedSpan.traceId,
|
|
5097
|
+
spanId: exportedSpan.id
|
|
5098
|
+
});
|
|
5099
|
+
await this._updateSpan({ span: exportedSpan, traceData });
|
|
5100
|
+
break;
|
|
5101
|
+
case "handleSpanEnd":
|
|
5102
|
+
this.logger.debug(`${this.name}: handling span end`, {
|
|
5103
|
+
traceId: exportedSpan.traceId,
|
|
5104
|
+
spanId: exportedSpan.id
|
|
5105
|
+
});
|
|
5106
|
+
traceData.endSpan({ spanId: exportedSpan.id });
|
|
5107
|
+
await this._finishSpan({ span: exportedSpan, traceData });
|
|
5108
|
+
if (traceData.activeSpanCount() === 0) {
|
|
5109
|
+
this.#scheduleCleanup(traceId);
|
|
5110
|
+
}
|
|
5111
|
+
break;
|
|
5112
|
+
}
|
|
5113
|
+
} catch (error) {
|
|
5114
|
+
this.logger.error(`${this.name}: exporter error`, { error, event, method });
|
|
5115
|
+
}
|
|
5116
|
+
if (traceData.activeSpanCount() === 0) {
|
|
5117
|
+
this.#scheduleCleanup(traceId);
|
|
5118
|
+
}
|
|
5119
|
+
await this._postExportTracingEvent();
|
|
5120
|
+
}
|
|
5121
|
+
// ============================================================================
|
|
5122
|
+
// Protected Helpers
|
|
5123
|
+
// ============================================================================
|
|
5124
|
+
/**
|
|
5125
|
+
* Get or create the TraceData container for a trace.
|
|
5126
|
+
* Also cancels any pending cleanup since new data has arrived.
|
|
5127
|
+
*
|
|
5128
|
+
* @param args.traceId - The trace ID
|
|
5129
|
+
* @param args.method - The calling method name (for logging)
|
|
5130
|
+
* @returns The TraceData container for this trace
|
|
5131
|
+
*/
|
|
5132
|
+
getTraceData(args) {
|
|
5133
|
+
const { traceId, method } = args;
|
|
5134
|
+
this.#cancelScheduledCleanup(traceId);
|
|
5135
|
+
if (!this.#traceMap.has(traceId)) {
|
|
5136
|
+
this.#traceMap.set(traceId, new TraceData());
|
|
5137
|
+
this.logger.debug(`${this.name}: Created new trace data cache`, {
|
|
5138
|
+
traceId,
|
|
5139
|
+
method
|
|
5140
|
+
});
|
|
5141
|
+
this.#enforceHardCap().catch((error) => {
|
|
5142
|
+
this.logger.error(`${this.name}: Error enforcing hard cap`, { error });
|
|
5143
|
+
});
|
|
5144
|
+
}
|
|
5145
|
+
return this.#traceMap.get(traceId);
|
|
5146
|
+
}
|
|
5147
|
+
/**
|
|
5148
|
+
* Get the current number of traces being tracked.
|
|
5149
|
+
* @returns The trace count
|
|
5150
|
+
*/
|
|
5151
|
+
traceMapSize() {
|
|
5152
|
+
return this.#traceMap.size;
|
|
5153
|
+
}
|
|
5154
|
+
// ============================================================================
|
|
5155
|
+
// Flush and Shutdown Hooks (Override in subclass as needed)
|
|
5156
|
+
// ============================================================================
|
|
5157
|
+
/**
|
|
5158
|
+
* Hook called by flush() to perform vendor-specific flush logic.
|
|
5159
|
+
* Override to send buffered data to the vendor's API.
|
|
5160
|
+
*
|
|
5161
|
+
* Unlike _postShutdown(), this method should NOT release resources,
|
|
5162
|
+
* as the exporter will continue to be used after flushing.
|
|
5163
|
+
*/
|
|
5164
|
+
async _flush() {
|
|
5165
|
+
}
|
|
5166
|
+
/**
|
|
5167
|
+
* Force flush any buffered data without shutting down the exporter.
|
|
5168
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
5169
|
+
* are exported before the runtime instance is terminated.
|
|
5170
|
+
*
|
|
5171
|
+
* Subclasses should override _flush() to implement vendor-specific flush logic.
|
|
5172
|
+
*/
|
|
5173
|
+
async flush() {
|
|
5174
|
+
if (this.isDisabled) {
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
this.logger.debug(`${this.name}: Flushing`);
|
|
5178
|
+
await this._flush();
|
|
5179
|
+
}
|
|
5180
|
+
/**
|
|
5181
|
+
* Hook called at the start of shutdown, before cancelling timers and aborting spans.
|
|
5182
|
+
* Override to perform vendor-specific pre-shutdown tasks.
|
|
5183
|
+
*/
|
|
5184
|
+
async _preShutdown() {
|
|
5185
|
+
}
|
|
5186
|
+
/**
|
|
5187
|
+
* Hook called at the end of shutdown, after all spans are aborted.
|
|
5188
|
+
* Override to perform vendor-specific cleanup (e.g., flushing).
|
|
5189
|
+
*/
|
|
5190
|
+
async _postShutdown() {
|
|
5191
|
+
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Gracefully shut down the exporter.
|
|
5194
|
+
* Cancels all pending cleanup timers, aborts all active spans, and clears state.
|
|
5195
|
+
*/
|
|
5196
|
+
async shutdown() {
|
|
5197
|
+
if (this.isDisabled) {
|
|
5198
|
+
return;
|
|
5199
|
+
}
|
|
5200
|
+
this.#shutdownStarted = true;
|
|
5201
|
+
await this._preShutdown();
|
|
5202
|
+
for (const [traceId, timeout] of this.#pendingCleanups) {
|
|
5203
|
+
clearTimeout(timeout);
|
|
5204
|
+
this.logger.debug(`${this.name}: Cancelled pending cleanup on shutdown`, { traceId });
|
|
5205
|
+
}
|
|
5206
|
+
this.#pendingCleanups.clear();
|
|
5207
|
+
const reason = {
|
|
5208
|
+
id: "SHUTDOWN",
|
|
5209
|
+
message: "Observability is shutting down.",
|
|
5210
|
+
domain: "MASTRA_OBSERVABILITY",
|
|
5211
|
+
category: "SYSTEM"
|
|
5212
|
+
};
|
|
5213
|
+
for (const [traceId, traceData] of this.#traceMap) {
|
|
5214
|
+
const orphanedEvents = traceData.getAllQueuedEvents();
|
|
5215
|
+
if (orphanedEvents.length > 0) {
|
|
5216
|
+
this.logger.warn(`${this.name}: Dropping ${orphanedEvents.length} orphaned events on shutdown`, {
|
|
5217
|
+
traceId,
|
|
5218
|
+
orphanedEvents: orphanedEvents.map((e) => ({
|
|
5219
|
+
spanId: e.event.exportedSpan.id,
|
|
5220
|
+
waitingFor: e.waitingFor,
|
|
5221
|
+
attempts: e.attempts
|
|
5222
|
+
}))
|
|
5223
|
+
});
|
|
5224
|
+
}
|
|
5225
|
+
for (const spanId of traceData.activeSpanIds) {
|
|
5226
|
+
const span = traceData.getSpan({ spanId });
|
|
5227
|
+
if (span) {
|
|
5228
|
+
await this._abortSpan({ span, traceData, reason });
|
|
5229
|
+
}
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5232
|
+
this.#traceMap.clear();
|
|
5233
|
+
await this._postShutdown();
|
|
5234
|
+
await super.shutdown();
|
|
5235
|
+
}
|
|
5236
|
+
};
|
|
5237
|
+
|
|
5238
|
+
// src/exporters/span-formatters.ts
|
|
5239
|
+
function chainFormatters(formatters) {
|
|
5240
|
+
return async (span) => {
|
|
5241
|
+
let currentSpan = span;
|
|
5242
|
+
for (const formatter of formatters) {
|
|
5243
|
+
currentSpan = await formatter(currentSpan);
|
|
5244
|
+
}
|
|
5245
|
+
return currentSpan;
|
|
5246
|
+
};
|
|
5247
|
+
}
|
|
4220
5248
|
var CloudExporter = class extends BaseExporter {
|
|
4221
5249
|
name = "mastra-cloud-observability-exporter";
|
|
4222
|
-
|
|
5250
|
+
cloudConfig;
|
|
4223
5251
|
buffer;
|
|
4224
5252
|
flushTimer = null;
|
|
4225
5253
|
constructor(config = {}) {
|
|
4226
5254
|
super(config);
|
|
4227
5255
|
const accessToken = config.accessToken ?? process.env.MASTRA_CLOUD_ACCESS_TOKEN;
|
|
4228
5256
|
if (!accessToken) {
|
|
4229
|
-
this.setDisabled(
|
|
4230
|
-
"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."
|
|
4231
|
-
);
|
|
5257
|
+
this.setDisabled("MASTRA_CLOUD_ACCESS_TOKEN environment variable not set.");
|
|
4232
5258
|
}
|
|
4233
5259
|
const endpoint = config.endpoint ?? process.env.MASTRA_CLOUD_TRACES_ENDPOINT ?? "https://api.mastra.ai/ai/spans/publish";
|
|
4234
|
-
this.
|
|
5260
|
+
this.cloudConfig = {
|
|
4235
5261
|
logger: this.logger,
|
|
4236
5262
|
logLevel: config.logLevel ?? LogLevel.INFO,
|
|
4237
5263
|
maxBatchSize: config.maxBatchSize ?? 1e3,
|
|
@@ -4289,12 +5315,12 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4289
5315
|
return spanRecord;
|
|
4290
5316
|
}
|
|
4291
5317
|
shouldFlush() {
|
|
4292
|
-
if (this.buffer.totalSize >= this.
|
|
5318
|
+
if (this.buffer.totalSize >= this.cloudConfig.maxBatchSize) {
|
|
4293
5319
|
return true;
|
|
4294
5320
|
}
|
|
4295
5321
|
if (this.buffer.firstEventTime && this.buffer.totalSize > 0) {
|
|
4296
5322
|
const elapsed = Date.now() - this.buffer.firstEventTime.getTime();
|
|
4297
|
-
if (elapsed >= this.
|
|
5323
|
+
if (elapsed >= this.cloudConfig.maxBatchWaitMs) {
|
|
4298
5324
|
return true;
|
|
4299
5325
|
}
|
|
4300
5326
|
}
|
|
@@ -4317,9 +5343,9 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4317
5343
|
this.logger.trackException(mastraError);
|
|
4318
5344
|
this.logger.error("Scheduled flush failed", mastraError);
|
|
4319
5345
|
});
|
|
4320
|
-
}, this.
|
|
5346
|
+
}, this.cloudConfig.maxBatchWaitMs);
|
|
4321
5347
|
}
|
|
4322
|
-
async
|
|
5348
|
+
async flushBuffer() {
|
|
4323
5349
|
if (this.flushTimer) {
|
|
4324
5350
|
clearTimeout(this.flushTimer);
|
|
4325
5351
|
this.flushTimer = null;
|
|
@@ -4329,7 +5355,7 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4329
5355
|
}
|
|
4330
5356
|
const startTime = Date.now();
|
|
4331
5357
|
const spansCopy = [...this.buffer.spans];
|
|
4332
|
-
const flushReason = this.buffer.totalSize >= this.
|
|
5358
|
+
const flushReason = this.buffer.totalSize >= this.cloudConfig.maxBatchSize ? "size" : "time";
|
|
4333
5359
|
this.resetBuffer();
|
|
4334
5360
|
try {
|
|
4335
5361
|
await this.batchUpload(spansCopy);
|
|
@@ -4360,7 +5386,7 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4360
5386
|
*/
|
|
4361
5387
|
async batchUpload(spans) {
|
|
4362
5388
|
const headers = {
|
|
4363
|
-
Authorization: `Bearer ${this.
|
|
5389
|
+
Authorization: `Bearer ${this.cloudConfig.accessToken}`,
|
|
4364
5390
|
"Content-Type": "application/json"
|
|
4365
5391
|
};
|
|
4366
5392
|
const options = {
|
|
@@ -4368,13 +5394,29 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4368
5394
|
headers,
|
|
4369
5395
|
body: JSON.stringify({ spans })
|
|
4370
5396
|
};
|
|
4371
|
-
await fetchWithRetry(this.
|
|
5397
|
+
await fetchWithRetry(this.cloudConfig.endpoint, options, this.cloudConfig.maxRetries);
|
|
4372
5398
|
}
|
|
4373
5399
|
resetBuffer() {
|
|
4374
5400
|
this.buffer.spans = [];
|
|
4375
5401
|
this.buffer.firstEventTime = void 0;
|
|
4376
5402
|
this.buffer.totalSize = 0;
|
|
4377
5403
|
}
|
|
5404
|
+
/**
|
|
5405
|
+
* Force flush any buffered spans without shutting down the exporter.
|
|
5406
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
5407
|
+
* are exported before the runtime instance is terminated.
|
|
5408
|
+
*/
|
|
5409
|
+
async flush() {
|
|
5410
|
+
if (this.isDisabled) {
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
if (this.buffer.totalSize > 0) {
|
|
5414
|
+
this.logger.debug("Flushing buffered events", {
|
|
5415
|
+
bufferedEvents: this.buffer.totalSize
|
|
5416
|
+
});
|
|
5417
|
+
await this.flushBuffer();
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
4378
5420
|
async shutdown() {
|
|
4379
5421
|
if (this.isDisabled) {
|
|
4380
5422
|
return;
|
|
@@ -4383,27 +5425,22 @@ var CloudExporter = class extends BaseExporter {
|
|
|
4383
5425
|
clearTimeout(this.flushTimer);
|
|
4384
5426
|
this.flushTimer = null;
|
|
4385
5427
|
}
|
|
4386
|
-
|
|
4387
|
-
this.
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
{
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
error
|
|
4403
|
-
);
|
|
4404
|
-
this.logger.trackException(mastraError);
|
|
4405
|
-
this.logger.error("Failed to flush remaining events during shutdown", mastraError);
|
|
4406
|
-
}
|
|
5428
|
+
try {
|
|
5429
|
+
await this.flush();
|
|
5430
|
+
} catch (error) {
|
|
5431
|
+
const mastraError = new MastraError(
|
|
5432
|
+
{
|
|
5433
|
+
id: `CLOUD_EXPORTER_FAILED_TO_FLUSH_REMAINING_EVENTS_DURING_SHUTDOWN`,
|
|
5434
|
+
domain: ErrorDomain.MASTRA_OBSERVABILITY,
|
|
5435
|
+
category: ErrorCategory.USER,
|
|
5436
|
+
details: {
|
|
5437
|
+
remainingEvents: this.buffer.totalSize
|
|
5438
|
+
}
|
|
5439
|
+
},
|
|
5440
|
+
error
|
|
5441
|
+
);
|
|
5442
|
+
this.logger.trackException(mastraError);
|
|
5443
|
+
this.logger.error("Failed to flush remaining events during shutdown", mastraError);
|
|
4407
5444
|
}
|
|
4408
5445
|
this.logger.info("CloudExporter shutdown complete");
|
|
4409
5446
|
}
|
|
@@ -4707,7 +5744,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4707
5744
|
clearTimeout(this.#flushTimer);
|
|
4708
5745
|
}
|
|
4709
5746
|
this.#flushTimer = setTimeout(() => {
|
|
4710
|
-
this.
|
|
5747
|
+
this.flushBuffer().catch((error) => {
|
|
4711
5748
|
this.logger.error("Scheduled flush failed", {
|
|
4712
5749
|
error: error instanceof Error ? error.message : String(error)
|
|
4713
5750
|
});
|
|
@@ -4841,7 +5878,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4841
5878
|
handleBatchWithUpdatesEvent(event) {
|
|
4842
5879
|
this.addToBuffer(event);
|
|
4843
5880
|
if (this.shouldFlush()) {
|
|
4844
|
-
this.
|
|
5881
|
+
this.flushBuffer().catch((error) => {
|
|
4845
5882
|
this.logger.error("Batch flush failed", {
|
|
4846
5883
|
error: error instanceof Error ? error.message : String(error)
|
|
4847
5884
|
});
|
|
@@ -4857,7 +5894,7 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4857
5894
|
if (event.type === TracingEventType.SPAN_ENDED) {
|
|
4858
5895
|
this.addToBuffer(event);
|
|
4859
5896
|
if (this.shouldFlush()) {
|
|
4860
|
-
this.
|
|
5897
|
+
this.flushBuffer().catch((error) => {
|
|
4861
5898
|
this.logger.error("Batch flush failed", {
|
|
4862
5899
|
error: error instanceof Error ? error.message : String(error)
|
|
4863
5900
|
});
|
|
@@ -4874,9 +5911,9 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4874
5911
|
return this.#config.retryDelayMs * Math.pow(2, attempt);
|
|
4875
5912
|
}
|
|
4876
5913
|
/**
|
|
4877
|
-
* Flushes the current buffer to storage with retry logic
|
|
5914
|
+
* Flushes the current buffer to storage with retry logic (internal implementation)
|
|
4878
5915
|
*/
|
|
4879
|
-
async
|
|
5916
|
+
async flushBuffer() {
|
|
4880
5917
|
if (!this.#observability) {
|
|
4881
5918
|
this.logger.debug("Cannot flush traces. Observability storage is not initialized");
|
|
4882
5919
|
return;
|
|
@@ -4983,23 +6020,25 @@ var DefaultExporter = class extends BaseExporter {
|
|
|
4983
6020
|
break;
|
|
4984
6021
|
}
|
|
4985
6022
|
}
|
|
6023
|
+
/**
|
|
6024
|
+
* Force flush any buffered spans without shutting down the exporter.
|
|
6025
|
+
* This is useful in serverless environments where you need to ensure spans
|
|
6026
|
+
* are exported before the runtime instance is terminated.
|
|
6027
|
+
*/
|
|
6028
|
+
async flush() {
|
|
6029
|
+
if (this.buffer.totalSize > 0) {
|
|
6030
|
+
this.logger.debug("Flushing buffered events", {
|
|
6031
|
+
bufferedEvents: this.buffer.totalSize
|
|
6032
|
+
});
|
|
6033
|
+
await this.flushBuffer();
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
4986
6036
|
async shutdown() {
|
|
4987
6037
|
if (this.#flushTimer) {
|
|
4988
6038
|
clearTimeout(this.#flushTimer);
|
|
4989
6039
|
this.#flushTimer = null;
|
|
4990
6040
|
}
|
|
4991
|
-
|
|
4992
|
-
this.logger.info("Flushing remaining events on shutdown", {
|
|
4993
|
-
remainingEvents: this.buffer.totalSize
|
|
4994
|
-
});
|
|
4995
|
-
try {
|
|
4996
|
-
await this.flush();
|
|
4997
|
-
} catch (error) {
|
|
4998
|
-
this.logger.error("Failed to flush remaining events during shutdown", {
|
|
4999
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5000
|
-
});
|
|
5001
|
-
}
|
|
5002
|
-
}
|
|
6041
|
+
await this.flush();
|
|
5003
6042
|
this.logger.info("DefaultExporter shutdown complete");
|
|
5004
6043
|
}
|
|
5005
6044
|
};
|
|
@@ -5472,10 +6511,39 @@ var ModelSpanTracker = class {
|
|
|
5472
6511
|
case "step-finish":
|
|
5473
6512
|
this.#endStepSpan(chunk.payload);
|
|
5474
6513
|
break;
|
|
6514
|
+
// Infrastructure chunks - skip creating spans for these
|
|
6515
|
+
// They are either redundant, metadata-only, or error/control flow
|
|
5475
6516
|
case "raw":
|
|
5476
|
-
//
|
|
6517
|
+
// Redundant raw data
|
|
5477
6518
|
case "start":
|
|
6519
|
+
// Stream start marker
|
|
5478
6520
|
case "finish":
|
|
6521
|
+
// Stream finish marker (step-finish already captures this)
|
|
6522
|
+
case "response-metadata":
|
|
6523
|
+
// Response metadata (not semantic content)
|
|
6524
|
+
case "source":
|
|
6525
|
+
// Source references (metadata)
|
|
6526
|
+
case "file":
|
|
6527
|
+
// Binary file data (too large/not semantic)
|
|
6528
|
+
case "error":
|
|
6529
|
+
// Error handling
|
|
6530
|
+
case "abort":
|
|
6531
|
+
// Abort signal
|
|
6532
|
+
case "tripwire":
|
|
6533
|
+
// Processor rejection
|
|
6534
|
+
case "watch":
|
|
6535
|
+
// Internal watch event
|
|
6536
|
+
case "tool-error":
|
|
6537
|
+
// Tool error handling
|
|
6538
|
+
case "tool-call-approval":
|
|
6539
|
+
// Approval request (not content)
|
|
6540
|
+
case "tool-call-suspended":
|
|
6541
|
+
// Suspension (not content)
|
|
6542
|
+
case "reasoning-signature":
|
|
6543
|
+
// Signature metadata
|
|
6544
|
+
case "redacted-reasoning":
|
|
6545
|
+
// Redacted content metadata
|
|
6546
|
+
case "step-output":
|
|
5479
6547
|
break;
|
|
5480
6548
|
case "tool-output":
|
|
5481
6549
|
this.#handleToolOutputChunk(chunk);
|
|
@@ -5490,20 +6558,6 @@ var ModelSpanTracker = class {
|
|
|
5490
6558
|
this.#createEventSpan(chunk.type, cleanPayload);
|
|
5491
6559
|
break;
|
|
5492
6560
|
}
|
|
5493
|
-
// Default: auto-create event span for all other chunk types
|
|
5494
|
-
default: {
|
|
5495
|
-
let outputPayload = chunk.payload;
|
|
5496
|
-
if (outputPayload && typeof outputPayload === "object" && "data" in outputPayload) {
|
|
5497
|
-
const typedPayload = outputPayload;
|
|
5498
|
-
outputPayload = { ...typedPayload };
|
|
5499
|
-
if (typedPayload.data) {
|
|
5500
|
-
outputPayload.size = typeof typedPayload.data === "string" ? typedPayload.data.length : typedPayload.data instanceof Uint8Array ? typedPayload.data.length : void 0;
|
|
5501
|
-
delete outputPayload.data;
|
|
5502
|
-
}
|
|
5503
|
-
}
|
|
5504
|
-
this.#createEventSpan(chunk.type, outputPayload);
|
|
5505
|
-
break;
|
|
5506
|
-
}
|
|
5507
6561
|
}
|
|
5508
6562
|
}
|
|
5509
6563
|
})
|
|
@@ -5526,6 +6580,18 @@ var DEFAULT_DEEP_CLEAN_OPTIONS = Object.freeze({
|
|
|
5526
6580
|
maxArrayLength: 50,
|
|
5527
6581
|
maxObjectKeys: 50
|
|
5528
6582
|
});
|
|
6583
|
+
function mergeSerializationOptions(userOptions) {
|
|
6584
|
+
if (!userOptions) {
|
|
6585
|
+
return DEFAULT_DEEP_CLEAN_OPTIONS;
|
|
6586
|
+
}
|
|
6587
|
+
return {
|
|
6588
|
+
keysToStrip: DEFAULT_KEYS_TO_STRIP,
|
|
6589
|
+
maxDepth: userOptions.maxDepth ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxDepth,
|
|
6590
|
+
maxStringLength: userOptions.maxStringLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxStringLength,
|
|
6591
|
+
maxArrayLength: userOptions.maxArrayLength ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxArrayLength,
|
|
6592
|
+
maxObjectKeys: userOptions.maxObjectKeys ?? DEFAULT_DEEP_CLEAN_OPTIONS.maxObjectKeys
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
5529
6595
|
function truncateString(s, maxChars) {
|
|
5530
6596
|
if (s.length <= maxChars) {
|
|
5531
6597
|
return s;
|
|
@@ -5682,25 +6748,29 @@ var BaseSpan = class {
|
|
|
5682
6748
|
entityName;
|
|
5683
6749
|
/** Parent span ID (for root spans that are children of external spans) */
|
|
5684
6750
|
parentSpanId;
|
|
6751
|
+
/** Deep clean options for serialization */
|
|
6752
|
+
deepCleanOptions;
|
|
5685
6753
|
constructor(options, observabilityInstance) {
|
|
6754
|
+
const serializationOptions = observabilityInstance.getConfig().serializationOptions;
|
|
6755
|
+
this.deepCleanOptions = mergeSerializationOptions(serializationOptions);
|
|
5686
6756
|
this.name = options.name;
|
|
5687
6757
|
this.type = options.type;
|
|
5688
|
-
this.attributes = deepClean(options.attributes) || {};
|
|
5689
|
-
this.metadata = deepClean(options.metadata);
|
|
6758
|
+
this.attributes = deepClean(options.attributes, this.deepCleanOptions) || {};
|
|
6759
|
+
this.metadata = deepClean(options.metadata, this.deepCleanOptions);
|
|
5690
6760
|
this.parent = options.parent;
|
|
5691
|
-
this.startTime = /* @__PURE__ */ new Date();
|
|
6761
|
+
this.startTime = options.startTime ?? /* @__PURE__ */ new Date();
|
|
5692
6762
|
this.observabilityInstance = observabilityInstance;
|
|
5693
6763
|
this.isEvent = options.isEvent ?? false;
|
|
5694
6764
|
this.isInternal = isSpanInternal(this.type, options.tracingPolicy?.internal);
|
|
5695
6765
|
this.traceState = options.traceState;
|
|
5696
6766
|
this.tags = !options.parent && options.tags?.length ? options.tags : void 0;
|
|
5697
|
-
this.entityType = options.entityType;
|
|
5698
|
-
this.entityId = options.entityId;
|
|
5699
|
-
this.entityName = options.entityName;
|
|
6767
|
+
this.entityType = options.entityType ?? options.parent?.entityType;
|
|
6768
|
+
this.entityId = options.entityId ?? options.parent?.entityId;
|
|
6769
|
+
this.entityName = options.entityName ?? options.parent?.entityName;
|
|
5700
6770
|
if (this.isEvent) {
|
|
5701
|
-
this.output = deepClean(options.output);
|
|
6771
|
+
this.output = deepClean(options.output, this.deepCleanOptions);
|
|
5702
6772
|
} else {
|
|
5703
|
-
this.input = deepClean(options.input);
|
|
6773
|
+
this.input = deepClean(options.input, this.deepCleanOptions);
|
|
5704
6774
|
}
|
|
5705
6775
|
}
|
|
5706
6776
|
createChildSpan(options) {
|
|
@@ -5745,6 +6815,8 @@ var BaseSpan = class {
|
|
|
5745
6815
|
}
|
|
5746
6816
|
/** Returns a lightweight span ready for export */
|
|
5747
6817
|
exportSpan(includeInternalSpans) {
|
|
6818
|
+
const hideInput = this.traceState?.hideInput ?? false;
|
|
6819
|
+
const hideOutput = this.traceState?.hideOutput ?? false;
|
|
5748
6820
|
return {
|
|
5749
6821
|
id: this.id,
|
|
5750
6822
|
traceId: this.traceId,
|
|
@@ -5757,8 +6829,8 @@ var BaseSpan = class {
|
|
|
5757
6829
|
metadata: this.metadata,
|
|
5758
6830
|
startTime: this.startTime,
|
|
5759
6831
|
endTime: this.endTime,
|
|
5760
|
-
input: this.input,
|
|
5761
|
-
output: this.output,
|
|
6832
|
+
input: hideInput ? void 0 : this.input,
|
|
6833
|
+
output: hideOutput ? void 0 : this.output,
|
|
5762
6834
|
errorInfo: this.errorInfo,
|
|
5763
6835
|
isEvent: this.isEvent,
|
|
5764
6836
|
isRootSpan: this.isRootSpan,
|
|
@@ -5798,6 +6870,14 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
5798
6870
|
traceId;
|
|
5799
6871
|
constructor(options, observabilityInstance) {
|
|
5800
6872
|
super(options, observabilityInstance);
|
|
6873
|
+
if (options.spanId && options.traceId) {
|
|
6874
|
+
this.id = options.spanId;
|
|
6875
|
+
this.traceId = options.traceId;
|
|
6876
|
+
if (options.parentSpanId) {
|
|
6877
|
+
this.parentSpanId = options.parentSpanId;
|
|
6878
|
+
}
|
|
6879
|
+
return;
|
|
6880
|
+
}
|
|
5801
6881
|
const bridge = observabilityInstance.getBridge();
|
|
5802
6882
|
if (bridge && !this.isInternal) {
|
|
5803
6883
|
const bridgeIds = bridge.createSpan(options);
|
|
@@ -5832,13 +6912,13 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
5832
6912
|
}
|
|
5833
6913
|
this.endTime = /* @__PURE__ */ new Date();
|
|
5834
6914
|
if (options?.output !== void 0) {
|
|
5835
|
-
this.output = deepClean(options.output);
|
|
6915
|
+
this.output = deepClean(options.output, this.deepCleanOptions);
|
|
5836
6916
|
}
|
|
5837
6917
|
if (options?.attributes) {
|
|
5838
|
-
this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
|
|
6918
|
+
this.attributes = { ...this.attributes, ...deepClean(options.attributes, this.deepCleanOptions) };
|
|
5839
6919
|
}
|
|
5840
6920
|
if (options?.metadata) {
|
|
5841
|
-
this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
|
|
6921
|
+
this.metadata = { ...this.metadata, ...deepClean(options.metadata, this.deepCleanOptions) };
|
|
5842
6922
|
}
|
|
5843
6923
|
}
|
|
5844
6924
|
error(options) {
|
|
@@ -5856,10 +6936,10 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
5856
6936
|
message: error.message
|
|
5857
6937
|
};
|
|
5858
6938
|
if (attributes) {
|
|
5859
|
-
this.attributes = { ...this.attributes, ...deepClean(attributes) };
|
|
6939
|
+
this.attributes = { ...this.attributes, ...deepClean(attributes, this.deepCleanOptions) };
|
|
5860
6940
|
}
|
|
5861
6941
|
if (metadata) {
|
|
5862
|
-
this.metadata = { ...this.metadata, ...deepClean(metadata) };
|
|
6942
|
+
this.metadata = { ...this.metadata, ...deepClean(metadata, this.deepCleanOptions) };
|
|
5863
6943
|
}
|
|
5864
6944
|
if (endSpan) {
|
|
5865
6945
|
this.end();
|
|
@@ -5872,16 +6952,16 @@ var DefaultSpan = class extends BaseSpan {
|
|
|
5872
6952
|
return;
|
|
5873
6953
|
}
|
|
5874
6954
|
if (options.input !== void 0) {
|
|
5875
|
-
this.input = deepClean(options.input);
|
|
6955
|
+
this.input = deepClean(options.input, this.deepCleanOptions);
|
|
5876
6956
|
}
|
|
5877
6957
|
if (options.output !== void 0) {
|
|
5878
|
-
this.output = deepClean(options.output);
|
|
6958
|
+
this.output = deepClean(options.output, this.deepCleanOptions);
|
|
5879
6959
|
}
|
|
5880
6960
|
if (options.attributes) {
|
|
5881
|
-
this.attributes = { ...this.attributes, ...deepClean(options.attributes) };
|
|
6961
|
+
this.attributes = { ...this.attributes, ...deepClean(options.attributes, this.deepCleanOptions) };
|
|
5882
6962
|
}
|
|
5883
6963
|
if (options.metadata) {
|
|
5884
|
-
this.metadata = { ...this.metadata, ...deepClean(options.metadata) };
|
|
6964
|
+
this.metadata = { ...this.metadata, ...deepClean(options.metadata, this.deepCleanOptions) };
|
|
5885
6965
|
}
|
|
5886
6966
|
}
|
|
5887
6967
|
get isValid() {
|
|
@@ -5972,7 +7052,8 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
5972
7052
|
spanOutputProcessors: config.spanOutputProcessors ?? [],
|
|
5973
7053
|
bridge: config.bridge ?? void 0,
|
|
5974
7054
|
includeInternalSpans: config.includeInternalSpans ?? false,
|
|
5975
|
-
requestContextKeys: config.requestContextKeys ?? []
|
|
7055
|
+
requestContextKeys: config.requestContextKeys ?? [],
|
|
7056
|
+
serializationOptions: config.serializationOptions
|
|
5976
7057
|
};
|
|
5977
7058
|
if (this.config.bridge?.init) {
|
|
5978
7059
|
this.config.bridge.init({ config: this.config });
|
|
@@ -6010,11 +7091,26 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
6010
7091
|
// ============================================================================
|
|
6011
7092
|
/**
|
|
6012
7093
|
* Start a new span of a specific SpanType
|
|
7094
|
+
*
|
|
7095
|
+
* Sampling Decision:
|
|
7096
|
+
* - For root spans (no parent): Perform sampling check using the configured strategy
|
|
7097
|
+
* - For child spans: Inherit the sampling decision from the parent
|
|
7098
|
+
* - If parent is a NoOpSpan (not sampled), child is also a NoOpSpan
|
|
7099
|
+
* - If parent is a valid span (sampled), child is also sampled
|
|
7100
|
+
*
|
|
7101
|
+
* This ensures trace-level sampling: either all spans in a trace are sampled or none are.
|
|
7102
|
+
* See: https://github.com/mastra-ai/mastra/issues/11504
|
|
6013
7103
|
*/
|
|
6014
7104
|
startSpan(options) {
|
|
6015
7105
|
const { customSamplerOptions, requestContext, metadata, tracingOptions, ...rest } = options;
|
|
6016
|
-
if (
|
|
6017
|
-
|
|
7106
|
+
if (options.parent) {
|
|
7107
|
+
if (!options.parent.isValid) {
|
|
7108
|
+
return new NoOpSpan({ ...rest, metadata }, this);
|
|
7109
|
+
}
|
|
7110
|
+
} else {
|
|
7111
|
+
if (!this.shouldSample(customSamplerOptions)) {
|
|
7112
|
+
return new NoOpSpan({ ...rest, metadata }, this);
|
|
7113
|
+
}
|
|
6018
7114
|
}
|
|
6019
7115
|
let traceState;
|
|
6020
7116
|
if (options.parent) {
|
|
@@ -6026,8 +7122,12 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
6026
7122
|
const mergedMetadata = metadata || tracingMetadata ? { ...metadata, ...tracingMetadata } : void 0;
|
|
6027
7123
|
const enrichedMetadata = this.extractMetadataFromRequestContext(requestContext, mergedMetadata, traceState);
|
|
6028
7124
|
const tags = !options.parent ? tracingOptions?.tags : void 0;
|
|
7125
|
+
const traceId = !options.parent ? options.traceId ?? tracingOptions?.traceId : options.traceId;
|
|
7126
|
+
const parentSpanId = !options.parent ? options.parentSpanId ?? tracingOptions?.parentSpanId : options.parentSpanId;
|
|
6029
7127
|
const span = this.createSpan({
|
|
6030
7128
|
...rest,
|
|
7129
|
+
traceId,
|
|
7130
|
+
parentSpanId,
|
|
6031
7131
|
metadata: enrichedMetadata,
|
|
6032
7132
|
traceState,
|
|
6033
7133
|
tags
|
|
@@ -6040,6 +7140,37 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
6040
7140
|
}
|
|
6041
7141
|
return span;
|
|
6042
7142
|
}
|
|
7143
|
+
/**
|
|
7144
|
+
* Rebuild a span from exported data for lifecycle operations.
|
|
7145
|
+
* Used by durable execution engines (e.g., Inngest) to end/update spans
|
|
7146
|
+
* that were created in a previous durable operation.
|
|
7147
|
+
*
|
|
7148
|
+
* The rebuilt span:
|
|
7149
|
+
* - Does NOT emit SPAN_STARTED (assumes original span already did)
|
|
7150
|
+
* - Can have end(), update(), error() called on it
|
|
7151
|
+
* - Will emit SPAN_ENDED or SPAN_UPDATED when those methods are called
|
|
7152
|
+
*
|
|
7153
|
+
* @param cached - The exported span data to rebuild from
|
|
7154
|
+
* @returns A span that can have lifecycle methods called on it
|
|
7155
|
+
*/
|
|
7156
|
+
rebuildSpan(cached) {
|
|
7157
|
+
const span = this.createSpan({
|
|
7158
|
+
name: cached.name,
|
|
7159
|
+
type: cached.type,
|
|
7160
|
+
traceId: cached.traceId,
|
|
7161
|
+
spanId: cached.id,
|
|
7162
|
+
parentSpanId: cached.parentSpanId,
|
|
7163
|
+
startTime: cached.startTime instanceof Date ? cached.startTime : new Date(cached.startTime),
|
|
7164
|
+
input: cached.input,
|
|
7165
|
+
attributes: cached.attributes,
|
|
7166
|
+
metadata: cached.metadata,
|
|
7167
|
+
entityType: cached.entityType,
|
|
7168
|
+
entityId: cached.entityId,
|
|
7169
|
+
entityName: cached.entityName
|
|
7170
|
+
});
|
|
7171
|
+
this.wireSpanLifecycle(span);
|
|
7172
|
+
return span;
|
|
7173
|
+
}
|
|
6043
7174
|
// ============================================================================
|
|
6044
7175
|
// Configuration Management
|
|
6045
7176
|
// ============================================================================
|
|
@@ -6142,11 +7273,15 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
6142
7273
|
const configuredKeys = this.config.requestContextKeys ?? [];
|
|
6143
7274
|
const additionalKeys = tracingOptions?.requestContextKeys ?? [];
|
|
6144
7275
|
const allKeys = [...configuredKeys, ...additionalKeys];
|
|
6145
|
-
|
|
7276
|
+
const hideInput = tracingOptions?.hideInput;
|
|
7277
|
+
const hideOutput = tracingOptions?.hideOutput;
|
|
7278
|
+
if (allKeys.length === 0 && !hideInput && !hideOutput) {
|
|
6146
7279
|
return void 0;
|
|
6147
7280
|
}
|
|
6148
7281
|
return {
|
|
6149
|
-
requestContextKeys: allKeys
|
|
7282
|
+
requestContextKeys: allKeys,
|
|
7283
|
+
...hideInput !== void 0 && { hideInput },
|
|
7284
|
+
...hideOutput !== void 0 && { hideOutput }
|
|
6150
7285
|
};
|
|
6151
7286
|
}
|
|
6152
7287
|
/**
|
|
@@ -6277,6 +7412,29 @@ var BaseObservabilityInstance = class extends MastraBase {
|
|
|
6277
7412
|
this.logger.debug(`[Observability] Initialization started [name=${this.name}]`);
|
|
6278
7413
|
this.logger.info(`[Observability] Initialized successfully [name=${this.name}]`);
|
|
6279
7414
|
}
|
|
7415
|
+
/**
|
|
7416
|
+
* Force flush any buffered/queued spans from all exporters and the bridge
|
|
7417
|
+
* without shutting down the observability instance.
|
|
7418
|
+
*
|
|
7419
|
+
* This is useful in serverless environments (like Vercel's fluid compute) where
|
|
7420
|
+
* you need to ensure all spans are exported before the runtime instance is
|
|
7421
|
+
* terminated, while keeping the observability system active for future requests.
|
|
7422
|
+
*/
|
|
7423
|
+
async flush() {
|
|
7424
|
+
this.logger.debug(`[Observability] Flush started [name=${this.name}]`);
|
|
7425
|
+
const flushPromises = [...this.exporters.map((e) => e.flush())];
|
|
7426
|
+
if (this.config.bridge) {
|
|
7427
|
+
flushPromises.push(this.config.bridge.flush());
|
|
7428
|
+
}
|
|
7429
|
+
const results = await Promise.allSettled(flushPromises);
|
|
7430
|
+
results.forEach((result, index) => {
|
|
7431
|
+
if (result.status === "rejected") {
|
|
7432
|
+
const targetName = index < this.exporters.length ? this.exporters[index]?.name : "bridge";
|
|
7433
|
+
this.logger.error(`[Observability] Flush error [target=${targetName}]`, result.reason);
|
|
7434
|
+
}
|
|
7435
|
+
});
|
|
7436
|
+
this.logger.debug(`[Observability] Flush completed [name=${this.name}]`);
|
|
7437
|
+
}
|
|
6280
7438
|
/**
|
|
6281
7439
|
* Shutdown Observability and clean up resources
|
|
6282
7440
|
*/
|
|
@@ -6451,6 +7609,9 @@ var SensitiveDataFilter = class {
|
|
|
6451
7609
|
return "[Circular Reference]";
|
|
6452
7610
|
}
|
|
6453
7611
|
seen.add(obj);
|
|
7612
|
+
if (obj instanceof Date) {
|
|
7613
|
+
return obj;
|
|
7614
|
+
}
|
|
6454
7615
|
if (Array.isArray(obj)) {
|
|
6455
7616
|
return obj.map((item) => this.deepFilter(item, seen));
|
|
6456
7617
|
}
|
|
@@ -6583,6 +7744,9 @@ var Observability = class extends MastraBase {
|
|
|
6583
7744
|
}
|
|
6584
7745
|
}
|
|
6585
7746
|
if (config.default?.enabled) {
|
|
7747
|
+
console.warn(
|
|
7748
|
+
'[Mastra Observability] The "default: { enabled: true }" configuration is deprecated and will be removed in a future version. Please use explicit configs with DefaultExporter, CloudExporter, and SensitiveDataFilter instead. See https://mastra.ai/docs/observability/tracing/overview for the recommended configuration.'
|
|
7749
|
+
);
|
|
6586
7750
|
const defaultInstance = new DefaultObservabilityInstance({
|
|
6587
7751
|
serviceName: "mastra",
|
|
6588
7752
|
name: "default",
|
|
@@ -6670,6 +7834,6 @@ function buildTracingOptions(...updaters) {
|
|
|
6670
7834
|
return updaters.reduce((opts, updater) => updater(opts), {});
|
|
6671
7835
|
}
|
|
6672
7836
|
|
|
6673
|
-
export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, buildTracingOptions, deepClean, getExternalParentId, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema, truncateString };
|
|
7837
|
+
export { BaseExporter, BaseObservabilityInstance, BaseSpan, CloudExporter, ConsoleExporter, DEFAULT_DEEP_CLEAN_OPTIONS, DEFAULT_KEYS_TO_STRIP, DefaultExporter, DefaultObservabilityInstance, DefaultSpan, ModelSpanTracker, NoOpSpan, Observability, SamplingStrategyType, SensitiveDataFilter, TestExporter, TraceData, TrackingExporter, buildTracingOptions, chainFormatters, deepClean, getExternalParentId, mergeSerializationOptions, observabilityConfigValueSchema, observabilityInstanceConfigSchema, observabilityRegistryConfigSchema, samplingStrategySchema, serializationOptionsSchema, truncateString };
|
|
6674
7838
|
//# sourceMappingURL=index.js.map
|
|
6675
7839
|
//# sourceMappingURL=index.js.map
|