@mastra/loggers 1.3.9-alpha.0 → 1.3.9-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-core-mastra-class.md +7 -1
- package/dist/http/index.cjs +8 -0
- package/dist/http/index.cjs.map +1 -1
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +8 -0
- package/dist/http/index.js.map +1 -1
- package/dist/upstash/index.cjs +44 -16
- package/dist/upstash/index.cjs.map +1 -1
- package/dist/upstash/index.d.ts +5 -1
- package/dist/upstash/index.d.ts.map +1 -1
- package/dist/upstash/index.js +44 -16
- package/dist/upstash/index.js.map +1 -1
- package/package.json +5 -6
package/dist/docs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: mastra-loggers
|
|
|
3
3
|
description: Documentation for @mastra/loggers. Use when working with @mastra/loggers APIs, configuration, or implementation.
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@mastra/loggers"
|
|
6
|
-
version: "1.3.9-alpha.
|
|
6
|
+
version: "1.3.9-alpha.2"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
|
@@ -147,6 +147,12 @@ Re-drives every orphaned `running` durable-agent run across all registered durab
|
|
|
147
147
|
|
|
148
148
|
Requires persistent storage. With an in-memory store, there's nothing to recover after a process restart.
|
|
149
149
|
|
|
150
|
+
Agents and their runs are attempted serially. Each built-in `recoverActiveRuns()` attempt waits up to 30 seconds in total for `recover()` setup and settlement of the whole recovered workflow, then traversal can continue to later runs and agents. This isn't an overall recovery deadline: discovery and overridden custom recovery methods aren't bounded by this budget.
|
|
151
|
+
|
|
152
|
+
A timed-out attempt continues in the background. Its outcome is `status: 'skipped'` with `reason: 'recovery-timeout'` and no `retryAt`. It contributes only to `skipped`, not `recovered`, `succeeded`, or `failed`, and Mastra doesn't automatically retry it. Late settlement doesn't change the returned counts.
|
|
153
|
+
|
|
154
|
+
When shutdown begins, recovery stops dispatching new agents and runs. The recovery traversal and original attempts, including timed-out setup or execution, participate in the same shared shutdown drain as active executions. A recovery wait timeout doesn't cancel the attempt or give it a separate shutdown drain window.
|
|
155
|
+
|
|
150
156
|
A run that another execution still drives isn't taken over. It's counted in `skipped` and checked again once that execution's claim could have lapsed, so a run whose process crashed is recovered without another call. `shutdown()` cancels pending re-checks. See [run ownership](https://mastra.ai/docs/harness/durable-agents).
|
|
151
157
|
|
|
152
158
|
```typescript
|
|
@@ -164,4 +170,4 @@ Returns:
|
|
|
164
170
|
|
|
165
171
|
**failed** (`number`): Runs whose restart threw an error.
|
|
166
172
|
|
|
167
|
-
**skipped** (`number`): Runs
|
|
173
|
+
**skipped** (`number`): Runs skipped because they are already active, recovery is already in progress, or the recovery wait timed out. Only ownership skips eligible for re-checks are checked again; recovery timeouts are not automatically retried.
|
package/dist/http/index.cjs
CHANGED
|
@@ -59,6 +59,11 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
59
59
|
this.logBuffer = [];
|
|
60
60
|
this.lastFlush = Date.now();
|
|
61
61
|
this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);
|
|
62
|
+
this.updateIntervalRef();
|
|
63
|
+
}
|
|
64
|
+
updateIntervalRef() {
|
|
65
|
+
if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
|
|
66
|
+
else this.flushIntervalId.unref?.();
|
|
62
67
|
}
|
|
63
68
|
async makeHttpRequest(data) {
|
|
64
69
|
const body = JSON.stringify({ logs: data });
|
|
@@ -114,6 +119,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
114
119
|
this.flushRequested = false;
|
|
115
120
|
const flush = this.flushBatch().finally(() => {
|
|
116
121
|
this.flushPromise = null;
|
|
122
|
+
this.updateIntervalRef();
|
|
117
123
|
});
|
|
118
124
|
this.flushPromise = flush;
|
|
119
125
|
const continueDraining = () => {
|
|
@@ -160,6 +166,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
160
166
|
} : parsed;
|
|
161
167
|
this.logBuffer.push(log);
|
|
162
168
|
this.enforceBufferLimit();
|
|
169
|
+
this.updateIntervalRef();
|
|
163
170
|
if (this.logBuffer.length >= this.batchSize) this.requestFlush();
|
|
164
171
|
cb(null, chunk);
|
|
165
172
|
} catch (error) {
|
|
@@ -204,6 +211,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
204
211
|
}
|
|
205
212
|
clearBuffer() {
|
|
206
213
|
this.logBuffer = [];
|
|
214
|
+
this.updateIntervalRef();
|
|
207
215
|
}
|
|
208
216
|
getDroppedLogCount() {
|
|
209
217
|
return this.droppedLogCount;
|
package/dist/http/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["LoggerTransport"],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["LoggerTransport"],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while logs are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered logs still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef(): void {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n this.updateIntervalRef();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n this.updateIntervalRef();\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
|
package/dist/http/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare class HttpTransport extends LoggerTransport {
|
|
|
31
31
|
private lastFlush;
|
|
32
32
|
private flushIntervalId;
|
|
33
33
|
constructor(options: HttpTransportOptions);
|
|
34
|
+
private updateIntervalRef;
|
|
34
35
|
private makeHttpRequest;
|
|
35
36
|
private sendWithRetry;
|
|
36
37
|
private enforceBufferLimit;
|
package/dist/http/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,UAAU,oBAAoB;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AA0BD,qBAAa,aAAc,SAAQ,eAAe;IAChD,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAiB;IAExC,YAAY,OAAO,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"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"}
|
package/dist/http/index.js
CHANGED
|
@@ -58,6 +58,11 @@ var HttpTransport = class extends LoggerTransport {
|
|
|
58
58
|
this.logBuffer = [];
|
|
59
59
|
this.lastFlush = Date.now();
|
|
60
60
|
this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);
|
|
61
|
+
this.updateIntervalRef();
|
|
62
|
+
}
|
|
63
|
+
updateIntervalRef() {
|
|
64
|
+
if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
|
|
65
|
+
else this.flushIntervalId.unref?.();
|
|
61
66
|
}
|
|
62
67
|
async makeHttpRequest(data) {
|
|
63
68
|
const body = JSON.stringify({ logs: data });
|
|
@@ -113,6 +118,7 @@ var HttpTransport = class extends LoggerTransport {
|
|
|
113
118
|
this.flushRequested = false;
|
|
114
119
|
const flush = this.flushBatch().finally(() => {
|
|
115
120
|
this.flushPromise = null;
|
|
121
|
+
this.updateIntervalRef();
|
|
116
122
|
});
|
|
117
123
|
this.flushPromise = flush;
|
|
118
124
|
const continueDraining = () => {
|
|
@@ -159,6 +165,7 @@ var HttpTransport = class extends LoggerTransport {
|
|
|
159
165
|
} : parsed;
|
|
160
166
|
this.logBuffer.push(log);
|
|
161
167
|
this.enforceBufferLimit();
|
|
168
|
+
this.updateIntervalRef();
|
|
162
169
|
if (this.logBuffer.length >= this.batchSize) this.requestFlush();
|
|
163
170
|
cb(null, chunk);
|
|
164
171
|
} catch (error) {
|
|
@@ -203,6 +210,7 @@ var HttpTransport = class extends LoggerTransport {
|
|
|
203
210
|
}
|
|
204
211
|
clearBuffer() {
|
|
205
212
|
this.logBuffer = [];
|
|
213
|
+
this.updateIntervalRef();
|
|
206
214
|
}
|
|
207
215
|
getDroppedLogCount() {
|
|
208
216
|
return this.droppedLogCount;
|
package/dist/http/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":";;AAoBA,IAAM,oBAAN,cAAgC,MAAM;CAEzB;CAEA;CAHX,YACE,QACA,YACA,cACA;EACA,MAAM,QAAQ,OAAO,IAAI,YAAY;EAJ5B,KAAA,SAAA;EAEA,KAAA,eAAA;CAGX;AACF;AAGA,SAAS,kBAAkB,QAAyB;CAClD,OAAO,WAAW,OAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACzE;AAGA,SAAS,gBAAgB,SAAkD;CACzE,MAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,EAAE,KAAK;CAChD,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,IAAI,QAAQ,KAAK,KAAK,GAAG,OAAO,OAAO,KAAK,IAAI;CAChD,MAAM,OAAO,KAAK,MAAM,KAAK;CAC7B,OAAO,OAAO,MAAM,IAAI,IAAI,KAAA,IAAY,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC;AACvE;AAEA,IAAa,gBAAb,cAAmC,gBAAgB;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,kBAA0B;CAC1B,eAA6C;CAC7C,iBAAyB;CACzB;CACA;CAEA,YAAY,SAA+B;EACzC,MAAM,EAAE,YAAY,KAAK,CAAC;EAE1B,IAAI,CAAC,QAAQ,KACX,MAAM,IAAI,MAAM,sBAAsB;EAGxC,KAAK,MAAM,QAAQ;EACnB,KAAK,SAAS,QAAQ,UAAU;EAChC,KAAK,UAAU;GACb,gBAAgB;GAChB,GAAG,QAAQ;EACb;EACA,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,gBAAgB,QAAQ,iBAAiB;EAC9C,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,eAAe;GAClB,YAAY,QAAQ,cAAc,cAAc;GAChD,YAAY,QAAQ,cAAc,cAAc;GAChD,oBAAoB,QAAQ,cAAc,sBAAsB;EAClE;EAEA,IAAI,CAAC,OAAO,UAAU,KAAK,SAAS,KAAK,KAAK,YAAY,GACxD,MAAM,IAAI,MAAM,oDAAoD;EAGtE,MAAM,gBAAgB,QAAQ,iBAAiB;EAC/C,IAAI,CAAC,OAAO,UAAU,aAAa,KAAK,gBAAgB,GACtD,MAAM,IAAI,MAAM,wDAAwD;EAE1E,KAAK,gBAAgB,KAAK,IAAI,eAAe,KAAK,SAAS;EAC3D,KAAK,YAAY,CAAC;EAClB,KAAK,YAAY,KAAK,IAAI;EAG1B,KAAK,kBAAkB,kBAAkB,KAAK,aAAa,GAAG,KAAK,aAAa;CAClF;CAEA,MAAc,gBAAgB,MAA8B;EAG1D,MAAM,OAAO,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;EAC1C,OAAO,KAAK,cAAc,IAAI;CAChC;CAEA,MAAc,cAAc,MAAc,aAAa,GAAsB;EAC3E,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,YAAY,iBAAiB,WAAW,MAAM,GAAG,KAAK,OAAO;EAEnE,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,KAAK;IACrC,QAAQ,KAAK;IACb,SAAS,KAAK;IACd;IACA,QAAQ,WAAW;GACrB,CAAC;GAED,aAAa,SAAS;GAEtB,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,kBAAkB,SAAS,QAAQ,SAAS,YAAY,gBAAgB,SAAS,OAAO,CAAC;GAGrG,OAAO;EACT,SAAS,OAAO;GACd,aAAa,SAAS;GAGtB,IAAI,EADgB,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,MACrE,aAAa,KAAK,aAAa,YAAY;IAC7D,MAAM,UAAU,KAAK,aAAa,qBAC9B,KAAK,aAAa,aAAa,KAAK,IAAI,GAAG,UAAU,IACrD,KAAK,aAAa;IAEtB,MAAM,aAAa,iBAAiB,oBAAqB,MAAM,gBAAgB,IAAK;IACpF,MAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,IAAI,YAAY,KAAK,OAAO,CAAC;IAElE,MAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,KAAK,CAAC;IACvD,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC;GAChD;GAEA,MAAM;EACR;CACF;CAEA,qBAAmC;EACjC,MAAM,WAAW,KAAK,UAAU,SAAS,KAAK;EAC9C,IAAI,YAAY,GACd;EAGF,KAAK,UAAU,OAAO,GAAG,QAAQ;EACjC,IAAI,KAAK,oBAAoB,GAC3B,QAAQ,KACN,iDAAiD,KAAK,cAAc,kEACtE;EAEF,KAAK,mBAAmB;CAC1B;CAIA,eAA6B;EAC3B,IAAI,KAAK,cAAc;GACrB,KAAK,iBAAiB;GACtB;EACF;EACA,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;GACzB,QAAQ,MAAM,yCAAyC,GAAG;EAC5D,CAAC;CACH;CAEA,SAAwB;EAEtB,IAAI,KAAK,cAAc;GACrB,KAAK,iBAAiB;GACtB,OAAO,KAAK;EACd;EACA,IAAI,KAAK,UAAU,WAAW,GAC5B,OAAO,QAAQ,QAAQ;EAGzB,KAAK,iBAAiB;EACtB,MAAM,QAAQ,KAAK,WAAW,CAAC,CAAC,cAAc;GAC5C,KAAK,eAAe;EACtB,CAAC;EACD,KAAK,eAAe;EAIpB,MAAM,yBAAyB;GAC7B,IAAI,KAAK,UAAU,UAAU,KAAK,aAAc,KAAK,kBAAkB,KAAK,UAAU,SAAS,GAC7F,KAAK,aAAa;EAEtB;EACA,MAAM,KAAK,mBAAkB,UAAS;GACpC,IAAI,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,GACvE,iBAAiB;EAErB,CAAC;EACD,OAAO;CACT;CAEA,MAAc,aAA4B;EACxC,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,OAAO,KAAK,UAAU,OAAO,GAAG,KAAK,SAAS;EAEpD,IAAI;GACF,MAAM,KAAK,gBAAgB,IAAI;GAC/B,KAAK,YAAY;EACnB,SAAS,OAAO;GAEd,IAAI,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,GAAG;IAC1E,KAAK,mBAAmB,KAAK;IAC7B,QAAQ,KACN,sDAAsD,MAAM,OAAO,aAAa,KAAK,OAAO,gDAC9F;IACA,MAAM;GACR;GAGA,KAAK,UAAU,QAAQ,GAAG,IAAI;GAC9B,KAAK,mBAAmB;GACxB,MAAM;EACR;CACF;CAEA,OAAO,OAAY,UAAmB,UAAoD;EACxF,IAAI,OAAO,aAAa,YAAY;GAClC,KAAK,WAAW,OAAO,YAAY,QAAQ,QAAQ;GACnD,OAAO;EACT;EAEA,KAAK,WAAW,OAAO,YAAY,SAAS,UAAwB;GAClE,IAAI,OAAO,QAAQ,MAAM,6BAA6B,KAAK;EAC7D,CAAC;EACD,OAAO;CACT;CAEA,WAAW,OAAe,MAAc,IAAoB;EAC1D,IAAI;GAEF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;GAG/D,MAAM,MAAM,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO;IAAE,GAAG;IAAQ,MAAM,KAAK,IAAI;GAAE,IAAI;GAGlG,KAAK,UAAU,KAAK,GAAG;GACvB,KAAK,mBAAmB;GAGxB,IAAI,KAAK,UAAU,UAAU,KAAK,WAChC,KAAK,aAAa;GAIpB,GAAG,MAAM,KAAK;EAChB,SAAS,OAAO;GACd,GAAG,KAAK;EACV;CACF;CAEA,SAAS,KAAY,IAAoB;EACvC,cAAc,KAAK,eAAe;EAIlC,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cAAc;GAClD,MAAM,QAAQ,YAAY;IACxB,MAAM,KAAK,cAAc,YAAY,CAAC,CAAC;IACvC,OAAO,KAAK,gBAAgB,KAAK,UAAU,SAAS,GAClD,OAAO,KAAK,gBAAgB,KAAK,OAAO;GAE5C;GAEA,MAAM,CAAC,CACJ,WAAW,GAAG,GAAG,CAAC,CAAC,CACnB,OAAM,aAAY;IACjB,QAAQ,MAAM,yBAAyB,QAAQ;IAC/C,GAAG,OAAO,QAAQ;GACpB,CAAC;EACL,OACE,GAAG,GAAG;CAEV;CAEA,MAAM,SAAS,QAcZ;EAGD,QAAQ,KACN,wGACF;EAEA,OAAO;GACL,MAAM,CAAC;GACP,OAAO;GACP,MAAM,QAAQ,SAAS,IAAI,IAAK,QAAQ,QAAQ;GAChD,SAAS,QAAQ,WAAW;GAC5B,SAAS;EACX;CACF;CAEA,MAAM,gBAAgB,EACpB,OAAO,QACP,UAAU,WACV,QAAQ,SACR,UAAU,WACV,SAAS,UACT,MACA,WAeC;EAGD,QAAQ,KACN,+GACF;EAEA,OAAO;GACL,MAAM,CAAC;GACP,OAAO;GACP,MAAM,SAAS,IAAI,IAAK,QAAQ;GAChC,SAAS,WAAW;GACpB,SAAS;EACX;CACF;CAGA,kBAA2C;EACzC,OAAO,CAAC,GAAG,KAAK,SAAS;CAC3B;CAEA,cAA2B;EACzB,KAAK,YAAY,CAAC;CACpB;CAEA,qBAAoC;EAClC,OAAO,KAAK;CACd;CAEA,mBAAkC;EAChC,OAAO,KAAK;CACd;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while logs are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered logs still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef(): void {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n this.updateIntervalRef();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n this.updateIntervalRef();\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
|
package/dist/upstash/index.cjs
CHANGED
|
@@ -2,6 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_pagination = require("../pagination-D4VAcPxN.cjs");
|
|
3
3
|
let _mastra_core_logger = require("@mastra/core/logger");
|
|
4
4
|
//#region src/upstash/index.ts
|
|
5
|
+
/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */
|
|
6
|
+
var UpstashTrimError = class extends Error {
|
|
7
|
+
name = "UpstashTrimError";
|
|
8
|
+
};
|
|
5
9
|
var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
6
10
|
upstashUrl;
|
|
7
11
|
upstashToken;
|
|
@@ -34,6 +38,11 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
34
38
|
console.error("Error flushing logs to Upstash:", err);
|
|
35
39
|
});
|
|
36
40
|
}, this.flushInterval);
|
|
41
|
+
this.updateIntervalRef();
|
|
42
|
+
}
|
|
43
|
+
updateIntervalRef() {
|
|
44
|
+
if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
|
|
45
|
+
else this.flushIntervalId.unref?.();
|
|
37
46
|
}
|
|
38
47
|
async executeUpstashCommands(commands) {
|
|
39
48
|
const response = await fetch(`${this.upstashUrl}/pipeline`, {
|
|
@@ -53,32 +62,41 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
53
62
|
if (this.logBuffer.length === 0) return Promise.resolve();
|
|
54
63
|
this.flushPromise = this.flushBatch().finally(() => {
|
|
55
64
|
this.flushPromise = null;
|
|
65
|
+
this.updateIntervalRef();
|
|
56
66
|
});
|
|
57
67
|
return this.flushPromise;
|
|
58
68
|
}
|
|
59
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
|
|
71
|
+
* front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
|
|
72
|
+
*/
|
|
60
73
|
async flushBatch() {
|
|
61
74
|
const now = Date.now();
|
|
62
75
|
const logs = this.logBuffer.splice(0, this.batchSize);
|
|
76
|
+
const commands = [[
|
|
77
|
+
"LPUSH",
|
|
78
|
+
this.listName,
|
|
79
|
+
...logs.map((log) => JSON.stringify(log))
|
|
80
|
+
]];
|
|
81
|
+
if (this.maxListLength > 0) commands.push([
|
|
82
|
+
"LTRIM",
|
|
83
|
+
this.listName,
|
|
84
|
+
0,
|
|
85
|
+
this.maxListLength - 1
|
|
86
|
+
]);
|
|
87
|
+
let results;
|
|
63
88
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
...logs.map((log) => JSON.stringify(log))
|
|
68
|
-
]];
|
|
69
|
-
if (this.maxListLength > 0) commands.push([
|
|
70
|
-
"LTRIM",
|
|
71
|
-
this.listName,
|
|
72
|
-
0,
|
|
73
|
-
this.maxListLength - 1
|
|
74
|
-
]);
|
|
75
|
-
await this.executeUpstashCommands(commands);
|
|
76
|
-
this.lastFlush = now;
|
|
89
|
+
results = await this.executeUpstashCommands(commands);
|
|
90
|
+
const pushError = Array.isArray(results) ? results[0]?.error : void 0;
|
|
91
|
+
if (pushError) throw new Error(`Failed to push logs to Upstash: ${pushError}`);
|
|
77
92
|
} catch (error) {
|
|
78
93
|
this.logBuffer.unshift(...logs);
|
|
79
94
|
this.enforceBufferLimit();
|
|
80
95
|
throw error;
|
|
81
96
|
}
|
|
97
|
+
this.lastFlush = now;
|
|
98
|
+
const trimError = Array.isArray(results) ? results[1]?.error : void 0;
|
|
99
|
+
if (trimError) throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);
|
|
82
100
|
}
|
|
83
101
|
/** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
|
|
84
102
|
enforceBufferLimit() {
|
|
@@ -106,6 +124,7 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
106
124
|
time: Date.now()
|
|
107
125
|
} : parsed;
|
|
108
126
|
this.logBuffer.push(log);
|
|
127
|
+
this.updateIntervalRef();
|
|
109
128
|
if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) this._flush().catch((err) => {
|
|
110
129
|
console.error("Error flushing logs to Upstash:", err);
|
|
111
130
|
});
|
|
@@ -124,8 +143,17 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
|
|
|
124
143
|
else cb(err);
|
|
125
144
|
}
|
|
126
145
|
async drainBuffer() {
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
let trimError;
|
|
147
|
+
await this.flushPromise?.catch((error) => {
|
|
148
|
+
if (error instanceof UpstashTrimError) trimError ??= error;
|
|
149
|
+
});
|
|
150
|
+
while (this.logBuffer.length > 0 || this.flushPromise) try {
|
|
151
|
+
await (this.flushPromise ?? this._flush());
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (!(error instanceof UpstashTrimError)) throw error;
|
|
154
|
+
trimError ??= error;
|
|
155
|
+
}
|
|
156
|
+
if (trimError) throw trimError;
|
|
129
157
|
}
|
|
130
158
|
parseLogs(logs) {
|
|
131
159
|
if (!Array.isArray(logs)) return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["LoggerTransport","normalizePagination"],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n return this.flushPromise;\n }\n\n /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n await this.executeUpstashCommands(commands);\n this.lastFlush = now;\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n private async drainBuffer(): Promise<void> {\n await this.flushPromise?.catch(() => {});\n while (this.logBuffer.length > 0 || this.flushPromise) {\n await (this.flushPromise ?? this._flush());\n }\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["LoggerTransport","normalizePagination"],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\n/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */\nclass UpstashTrimError extends Error {\n name = 'UpstashTrimError';\n}\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while records are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered records still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef() {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n return this.flushPromise;\n }\n\n /**\n * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the\n * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.\n */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n let results: any;\n try {\n results = await this.executeUpstashCommands(commands);\n // Upstash answers HTTP 200 even when a command fails, reporting { error } per command\n const pushError = Array.isArray(results) ? results[0]?.error : undefined;\n if (pushError) {\n throw new Error(`Failed to push logs to Upstash: ${pushError}`);\n }\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n\n this.lastFlush = now;\n const trimError = Array.isArray(results) ? results[1]?.error : undefined;\n if (trimError) {\n throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.updateIntervalRef();\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n // A trim failure leaves nothing to resend, so draining continues and the first one is reported at the end.\n private async drainBuffer(): Promise<void> {\n let trimError: UpstashTrimError | undefined;\n await this.flushPromise?.catch(error => {\n if (error instanceof UpstashTrimError) trimError ??= error;\n });\n while (this.logBuffer.length > 0 || this.flushPromise) {\n try {\n await (this.flushPromise ?? this._flush());\n } catch (error) {\n if (!(error instanceof UpstashTrimError)) throw error;\n trimError ??= error;\n }\n }\n if (trimError) throw trimError;\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/upstash/index.d.ts
CHANGED
|
@@ -24,10 +24,14 @@ export declare class UpstashTransport extends LoggerTransport {
|
|
|
24
24
|
flushInterval?: number;
|
|
25
25
|
upstashToken: string;
|
|
26
26
|
});
|
|
27
|
+
private updateIntervalRef;
|
|
27
28
|
private executeUpstashCommands;
|
|
28
29
|
/** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */
|
|
29
30
|
_flush(): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
|
|
33
|
+
* front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
|
|
34
|
+
*/
|
|
31
35
|
private flushBatch;
|
|
32
36
|
/** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
|
|
33
37
|
private enforceBufferLimit;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"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"}
|
package/dist/upstash/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { t as normalizePagination } from "../pagination-BfSC4BZ-.js";
|
|
2
2
|
import { LoggerTransport } from "@mastra/core/logger";
|
|
3
3
|
//#region src/upstash/index.ts
|
|
4
|
+
/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */
|
|
5
|
+
var UpstashTrimError = class extends Error {
|
|
6
|
+
name = "UpstashTrimError";
|
|
7
|
+
};
|
|
4
8
|
var UpstashTransport = class extends LoggerTransport {
|
|
5
9
|
upstashUrl;
|
|
6
10
|
upstashToken;
|
|
@@ -33,6 +37,11 @@ var UpstashTransport = class extends LoggerTransport {
|
|
|
33
37
|
console.error("Error flushing logs to Upstash:", err);
|
|
34
38
|
});
|
|
35
39
|
}, this.flushInterval);
|
|
40
|
+
this.updateIntervalRef();
|
|
41
|
+
}
|
|
42
|
+
updateIntervalRef() {
|
|
43
|
+
if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
|
|
44
|
+
else this.flushIntervalId.unref?.();
|
|
36
45
|
}
|
|
37
46
|
async executeUpstashCommands(commands) {
|
|
38
47
|
const response = await fetch(`${this.upstashUrl}/pipeline`, {
|
|
@@ -52,32 +61,41 @@ var UpstashTransport = class extends LoggerTransport {
|
|
|
52
61
|
if (this.logBuffer.length === 0) return Promise.resolve();
|
|
53
62
|
this.flushPromise = this.flushBatch().finally(() => {
|
|
54
63
|
this.flushPromise = null;
|
|
64
|
+
this.updateIntervalRef();
|
|
55
65
|
});
|
|
56
66
|
return this.flushPromise;
|
|
57
67
|
}
|
|
58
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
|
|
70
|
+
* front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
|
|
71
|
+
*/
|
|
59
72
|
async flushBatch() {
|
|
60
73
|
const now = Date.now();
|
|
61
74
|
const logs = this.logBuffer.splice(0, this.batchSize);
|
|
75
|
+
const commands = [[
|
|
76
|
+
"LPUSH",
|
|
77
|
+
this.listName,
|
|
78
|
+
...logs.map((log) => JSON.stringify(log))
|
|
79
|
+
]];
|
|
80
|
+
if (this.maxListLength > 0) commands.push([
|
|
81
|
+
"LTRIM",
|
|
82
|
+
this.listName,
|
|
83
|
+
0,
|
|
84
|
+
this.maxListLength - 1
|
|
85
|
+
]);
|
|
86
|
+
let results;
|
|
62
87
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
...logs.map((log) => JSON.stringify(log))
|
|
67
|
-
]];
|
|
68
|
-
if (this.maxListLength > 0) commands.push([
|
|
69
|
-
"LTRIM",
|
|
70
|
-
this.listName,
|
|
71
|
-
0,
|
|
72
|
-
this.maxListLength - 1
|
|
73
|
-
]);
|
|
74
|
-
await this.executeUpstashCommands(commands);
|
|
75
|
-
this.lastFlush = now;
|
|
88
|
+
results = await this.executeUpstashCommands(commands);
|
|
89
|
+
const pushError = Array.isArray(results) ? results[0]?.error : void 0;
|
|
90
|
+
if (pushError) throw new Error(`Failed to push logs to Upstash: ${pushError}`);
|
|
76
91
|
} catch (error) {
|
|
77
92
|
this.logBuffer.unshift(...logs);
|
|
78
93
|
this.enforceBufferLimit();
|
|
79
94
|
throw error;
|
|
80
95
|
}
|
|
96
|
+
this.lastFlush = now;
|
|
97
|
+
const trimError = Array.isArray(results) ? results[1]?.error : void 0;
|
|
98
|
+
if (trimError) throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);
|
|
81
99
|
}
|
|
82
100
|
/** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
|
|
83
101
|
enforceBufferLimit() {
|
|
@@ -105,6 +123,7 @@ var UpstashTransport = class extends LoggerTransport {
|
|
|
105
123
|
time: Date.now()
|
|
106
124
|
} : parsed;
|
|
107
125
|
this.logBuffer.push(log);
|
|
126
|
+
this.updateIntervalRef();
|
|
108
127
|
if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) this._flush().catch((err) => {
|
|
109
128
|
console.error("Error flushing logs to Upstash:", err);
|
|
110
129
|
});
|
|
@@ -123,8 +142,17 @@ var UpstashTransport = class extends LoggerTransport {
|
|
|
123
142
|
else cb(err);
|
|
124
143
|
}
|
|
125
144
|
async drainBuffer() {
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
let trimError;
|
|
146
|
+
await this.flushPromise?.catch((error) => {
|
|
147
|
+
if (error instanceof UpstashTrimError) trimError ??= error;
|
|
148
|
+
});
|
|
149
|
+
while (this.logBuffer.length > 0 || this.flushPromise) try {
|
|
150
|
+
await (this.flushPromise ?? this._flush());
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (!(error instanceof UpstashTrimError)) throw error;
|
|
153
|
+
trimError ??= error;
|
|
154
|
+
}
|
|
155
|
+
if (trimError) throw trimError;
|
|
128
156
|
}
|
|
129
157
|
parseLogs(logs) {
|
|
130
158
|
if (!Array.isArray(logs)) return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n return this.flushPromise;\n }\n\n /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n await this.executeUpstashCommands(commands);\n this.lastFlush = now;\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n private async drainBuffer(): Promise<void> {\n await this.flushPromise?.catch(() => {});\n while (this.logBuffer.length > 0 || this.flushPromise) {\n await (this.flushPromise ?? this._flush());\n }\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\n/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */\nclass UpstashTrimError extends Error {\n name = 'UpstashTrimError';\n}\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while records are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered records still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef() {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n return this.flushPromise;\n }\n\n /**\n * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the\n * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.\n */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n let results: any;\n try {\n results = await this.executeUpstashCommands(commands);\n // Upstash answers HTTP 200 even when a command fails, reporting { error } per command\n const pushError = Array.isArray(results) ? results[0]?.error : undefined;\n if (pushError) {\n throw new Error(`Failed to push logs to Upstash: ${pushError}`);\n }\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n\n this.lastFlush = now;\n const trimError = Array.isArray(results) ? results[1]?.error : undefined;\n if (trimError) {\n throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.updateIntervalRef();\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n // A trim failure leaves nothing to resend, so draining continues and the first one is reported at the end.\n private async drainBuffer(): Promise<void> {\n let trimError: UpstashTrimError | undefined;\n await this.flushPromise?.catch(error => {\n if (error instanceof UpstashTrimError) trimError ??= error;\n });\n while (this.logBuffer.length > 0 || this.flushPromise) {\n try {\n await (this.flushPromise ?? this._flush());\n } catch (error) {\n if (!(error instanceof UpstashTrimError)) throw error;\n trimError ??= error;\n }\n }\n if (trimError) throw trimError;\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/loggers",
|
|
3
|
-
"version": "1.3.9-alpha.
|
|
3
|
+
"version": "1.3.9-alpha.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -71,14 +71,13 @@
|
|
|
71
71
|
"@types/node": "22.20.1",
|
|
72
72
|
"@vitest/coverage-v8": "4.1.11",
|
|
73
73
|
"@vitest/ui": "4.1.11",
|
|
74
|
-
"eslint": "^10.7.0",
|
|
75
74
|
"tsdown": "0.22.9",
|
|
76
75
|
"tsx": "^4.23.1",
|
|
77
76
|
"typescript": "^7.0.2",
|
|
78
77
|
"vitest": "4.1.11",
|
|
79
|
-
"@internal/types-builder": "0.0.119",
|
|
80
78
|
"@internal/lint": "0.0.144",
|
|
81
|
-
"@
|
|
79
|
+
"@internal/types-builder": "0.0.119",
|
|
80
|
+
"@mastra/core": "1.79.0-alpha.6"
|
|
82
81
|
},
|
|
83
82
|
"peerDependencies": {
|
|
84
83
|
"@mastra/core": ">=1.0.0-0 <2.0.0-0"
|
|
@@ -90,7 +89,7 @@
|
|
|
90
89
|
"build:lib": "tsdown --silent --config tsdown.config.ts",
|
|
91
90
|
"build:watch": "tsdown --watch --silent --config tsdown.config.ts",
|
|
92
91
|
"test": "vitest run",
|
|
93
|
-
"lint": "oxlint
|
|
94
|
-
"lint:fix": "oxlint --
|
|
92
|
+
"lint": "oxlint --type-aware .",
|
|
93
|
+
"lint:fix": "oxlint --type-aware --fix ."
|
|
95
94
|
}
|
|
96
95
|
}
|