agentfootprint 11.1.0 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +1 -0
- package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -4
- package/dist/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
- package/dist/adapters/mcp/agentcoreRuntime/transport.js +48 -22
- package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/adapters/observability/cloudwatch.js +101 -110
- package/dist/adapters/observability/cloudwatch.js.map +1 -1
- package/dist/adapters/observability/file.js +48 -71
- package/dist/adapters/observability/file.js.map +1 -1
- package/dist/adapters/observability/outbox.js +217 -0
- package/dist/adapters/observability/outbox.js.map +1 -0
- package/dist/adapters/observability/xray.js +56 -70
- package/dist/adapters/observability/xray.js.map +1 -1
- package/dist/core/RunnerBase.js +20 -1
- package/dist/core/RunnerBase.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +6 -2
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js +35 -4
- package/dist/esm/adapters/mcp/agentcoreRuntime/sessionBusy.js.map +1 -1
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.d.ts +38 -12
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +47 -21
- package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
- package/dist/esm/adapters/observability/cloudwatch.d.ts +6 -2
- package/dist/esm/adapters/observability/cloudwatch.js +101 -110
- package/dist/esm/adapters/observability/cloudwatch.js.map +1 -1
- package/dist/esm/adapters/observability/file.d.ts +8 -4
- package/dist/esm/adapters/observability/file.js +48 -71
- package/dist/esm/adapters/observability/file.js.map +1 -1
- package/dist/esm/adapters/observability/outbox.d.ts +104 -0
- package/dist/esm/adapters/observability/outbox.js +213 -0
- package/dist/esm/adapters/observability/outbox.js.map +1 -0
- package/dist/esm/adapters/observability/xray.js +56 -70
- package/dist/esm/adapters/observability/xray.js.map +1 -1
- package/dist/esm/core/RunnerBase.d.ts +18 -0
- package/dist/esm/core/RunnerBase.js +20 -1
- package/dist/esm/core/RunnerBase.js.map +1 -1
- package/dist/esm/core/runner.d.ts +1 -0
- package/dist/esm/lib/longTimeout.d.ts +34 -0
- package/dist/esm/lib/longTimeout.js +56 -0
- package/dist/esm/lib/longTimeout.js.map +1 -0
- package/dist/esm/lib/sleep.d.ts +2 -0
- package/dist/esm/lib/sleep.js +2 -0
- package/dist/esm/lib/sleep.js.map +1 -1
- package/dist/esm/mcp/backoff.d.ts +81 -0
- package/dist/esm/mcp/backoff.js +158 -0
- package/dist/esm/mcp/backoff.js.map +1 -0
- package/dist/esm/mcp/mcpClient.js +3 -1
- package/dist/esm/mcp/mcpClient.js.map +1 -1
- package/dist/esm/mcp/throttleRetry.d.ts +198 -28
- package/dist/esm/mcp/throttleRetry.js +428 -83
- package/dist/esm/mcp/throttleRetry.js.map +1 -1
- package/dist/esm/mcp/types.d.ts +16 -7
- package/dist/esm/strategies/attach.js +3 -0
- package/dist/esm/strategies/attach.js.map +1 -1
- package/dist/esm/strategies/flushDeadline.d.ts +47 -0
- package/dist/esm/strategies/flushDeadline.js +96 -0
- package/dist/esm/strategies/flushDeadline.js.map +1 -0
- package/dist/lib/longTimeout.js +60 -0
- package/dist/lib/longTimeout.js.map +1 -0
- package/dist/lib/sleep.js +2 -0
- package/dist/lib/sleep.js.map +1 -1
- package/dist/mcp/backoff.js +163 -0
- package/dist/mcp/backoff.js.map +1 -0
- package/dist/mcp/mcpClient.js +3 -1
- package/dist/mcp/mcpClient.js.map +1 -1
- package/dist/mcp/throttleRetry.js +432 -85
- package/dist/mcp/throttleRetry.js.map +1 -1
- package/dist/strategies/attach.js +3 -0
- package/dist/strategies/attach.js.map +1 -1
- package/dist/strategies/flushDeadline.js +102 -0
- package/dist/strategies/flushDeadline.js.map +1 -0
- package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts +6 -2
- package/dist/types/adapters/mcp/agentcoreRuntime/sessionBusy.d.ts.map +1 -1
- package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts +38 -12
- package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
- package/dist/types/adapters/observability/cloudwatch.d.ts +6 -2
- package/dist/types/adapters/observability/cloudwatch.d.ts.map +1 -1
- package/dist/types/adapters/observability/file.d.ts +8 -4
- package/dist/types/adapters/observability/file.d.ts.map +1 -1
- package/dist/types/adapters/observability/outbox.d.ts +105 -0
- package/dist/types/adapters/observability/outbox.d.ts.map +1 -0
- package/dist/types/adapters/observability/xray.d.ts.map +1 -1
- package/dist/types/core/RunnerBase.d.ts +18 -0
- package/dist/types/core/RunnerBase.d.ts.map +1 -1
- package/dist/types/core/runner.d.ts +1 -0
- package/dist/types/core/runner.d.ts.map +1 -1
- package/dist/types/lib/longTimeout.d.ts +35 -0
- package/dist/types/lib/longTimeout.d.ts.map +1 -0
- package/dist/types/lib/sleep.d.ts +2 -0
- package/dist/types/lib/sleep.d.ts.map +1 -1
- package/dist/types/mcp/backoff.d.ts +82 -0
- package/dist/types/mcp/backoff.d.ts.map +1 -0
- package/dist/types/mcp/throttleRetry.d.ts +198 -28
- package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
- package/dist/types/mcp/types.d.ts +16 -7
- package/dist/types/mcp/types.d.ts.map +1 -1
- package/dist/types/strategies/attach.d.ts.map +1 -1
- package/dist/types/strategies/flushDeadline.d.ts +48 -0
- package/dist/types/strategies/flushDeadline.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -41,6 +41,23 @@
|
|
|
41
41
|
import { lazyRequire } from '../../lib/lazyRequire.js';
|
|
42
42
|
import { toWireJson } from '../../lib/wireJson.js';
|
|
43
43
|
import { rateLimitedConsoleSink } from './deliveryErrors.js';
|
|
44
|
+
import { createOutbox } from './outbox.js';
|
|
45
|
+
/**
|
|
46
|
+
* What ONE `PutLogEvents` request may carry — CloudWatch Logs' documented
|
|
47
|
+
* per-request limits (PutLogEvents API reference): "The maximum number of log
|
|
48
|
+
* events in a batch is 10,000", and "The maximum batch size is 1,048,576
|
|
49
|
+
* bytes. This size is calculated as the sum of all event messages in UTF-8,
|
|
50
|
+
* plus 26 bytes for each log event."
|
|
51
|
+
*
|
|
52
|
+
* These split a delivery into legal requests; they are NOT triggers.
|
|
53
|
+
* `maxBatchEvents` / `maxBatchBytes` still decide when a delivery starts,
|
|
54
|
+
* exactly as before. The split matters because puts now go one at a time
|
|
55
|
+
* (#223): a slow put lets a backlog build behind it, and without the split
|
|
56
|
+
* the next delivery sent the whole backlog as one request CloudWatch refuses.
|
|
57
|
+
*/
|
|
58
|
+
const PUT_LOG_EVENTS_MAX_EVENTS = 10_000;
|
|
59
|
+
const PUT_LOG_EVENTS_MAX_BYTES = 1_048_576;
|
|
60
|
+
const PUT_LOG_EVENTS_BYTES_PER_EVENT = 26;
|
|
44
61
|
// ─── Generic base — also used by agentcoreObservability ──────────────
|
|
45
62
|
/**
|
|
46
63
|
* Internal: shared CloudWatch Logs base used by every adapter that
|
|
@@ -62,11 +79,8 @@ export function _buildCloudWatchObservability(opts, strategyName) {
|
|
|
62
79
|
const maxBatchEvents = opts.maxBatchEvents ?? 100;
|
|
63
80
|
const maxBatchBytes = opts.maxBatchBytes ?? 10_240;
|
|
64
81
|
const flushIntervalMs = opts.flushIntervalMs ?? 1000;
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
let bufferBytes = 0;
|
|
68
|
-
let lastFlushPromise = Promise.resolve();
|
|
69
|
-
let timer;
|
|
82
|
+
// Set by `stop()`: no more events accepted. NOT a guard on delivery (8.11.1):
|
|
83
|
+
// stopping does not authorise throwing away what was already accepted.
|
|
70
84
|
let stopped = false;
|
|
71
85
|
// What we know about the log stream (8.11.0). This is the per-(group,stream)
|
|
72
86
|
// create latch — group and stream are fixed for the life of a strategy, so
|
|
@@ -91,14 +105,6 @@ export function _buildCloudWatchObservability(opts, strategyName) {
|
|
|
91
105
|
client = createCloudWatchClient(opts.region, strategyName, opts._sdk);
|
|
92
106
|
return client;
|
|
93
107
|
}
|
|
94
|
-
function scheduleTimedFlush() {
|
|
95
|
-
if (timer || flushIntervalMs <= 0 || stopped)
|
|
96
|
-
return;
|
|
97
|
-
timer = setTimeout(() => {
|
|
98
|
-
timer = undefined;
|
|
99
|
-
void doFlush();
|
|
100
|
-
}, flushIntervalMs);
|
|
101
|
-
}
|
|
102
108
|
/** Route a delivery failure through whatever `_onError` IS RIGHT NOW.
|
|
103
109
|
* Reading it at call time (rather than capturing a hook at construction)
|
|
104
110
|
* is the whole fix: a consumer who assigns `_onError` after building the
|
|
@@ -114,90 +120,89 @@ export function _buildCloudWatchObservability(opts, strategyName) {
|
|
|
114
120
|
logEvents: batch,
|
|
115
121
|
});
|
|
116
122
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// progress — an infinite microtask spin that starved the event loop of
|
|
123
|
-
// the very process trying to shut down. Same stance as `auditExport`:
|
|
124
|
-
// stop observing, never destroy what was collected.
|
|
125
|
-
if (buffer.length === 0)
|
|
126
|
-
return;
|
|
127
|
-
// Snapshot + clear so concurrent emits during the in-flight put
|
|
128
|
-
// accumulate into the next batch.
|
|
129
|
-
const batch = buffer.splice(0);
|
|
130
|
-
bufferBytes = 0;
|
|
123
|
+
/** Ship one batch. Throws when it could not — the outbox drops the batch
|
|
124
|
+
* and reports it once. Runs inside the outbox's queue, so two puts are
|
|
125
|
+
* never in flight at once and CloudWatch's per-stream order holds. */
|
|
126
|
+
async function shipBatch(batch) {
|
|
127
|
+
const client = ensureClient();
|
|
131
128
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
await putBatch(client, batch);
|
|
135
|
-
streamState = 'created'; // a successful put proves the stream is there
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
// The one recoverable shape: CloudWatch rejects a put into a stream
|
|
140
|
-
// that doesn't exist. Before 8.11.0 this adapter asserted that
|
|
141
|
-
// precondition and never established it, so any stream name that
|
|
142
|
-
// didn't already exist dropped every event, forever, in silence —
|
|
143
|
-
// and the documented `${HOSTNAME}/${Date.now()}` convention can never
|
|
144
|
-
// pre-exist, so following the docs guaranteed the bug.
|
|
145
|
-
if (!isMissingStreamError(err) || streamState === 'unavailable')
|
|
146
|
-
throw err;
|
|
147
|
-
if (!client.createLogStream) {
|
|
148
|
-
throw new Error(`log stream '${logStreamName}' does not exist in log group ` +
|
|
149
|
-
`'${opts.logGroupName}', and the injected \`_client\` has no ` +
|
|
150
|
-
`\`createLogStream\` to create it.`);
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
await client.createLogStream({
|
|
154
|
-
logGroupName: opts.logGroupName,
|
|
155
|
-
logStreamName,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
catch (createErr) {
|
|
159
|
-
// Two processes racing to create the same stream is normal and
|
|
160
|
-
// expected — whoever lost the race still has a usable stream.
|
|
161
|
-
if (!isAlreadyExistsError(createErr)) {
|
|
162
|
-
// Anything else (no `logs:CreateLogStream`, or the log GROUP
|
|
163
|
-
// itself is missing) will not fix itself. Latch it off so we
|
|
164
|
-
// never loop, and say which of the two it probably is.
|
|
165
|
-
streamState = 'unavailable';
|
|
166
|
-
throw new Error(`could not create log stream '${logStreamName}' in log group ` +
|
|
167
|
-
`'${opts.logGroupName}': ${errorMessage(createErr)}. Check that the log ` +
|
|
168
|
-
`GROUP exists (this adapter never creates one) and that the role allows ` +
|
|
169
|
-
`\`logs:CreateLogStream\`.`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
streamState = 'created';
|
|
173
|
-
// Exactly ONE re-put of the same batch. Not a retry loop: if this
|
|
174
|
-
// fails the events are gone, and saying so is better than growing a
|
|
175
|
-
// buffer during an outage.
|
|
176
|
-
await putBatch(client, batch);
|
|
177
|
-
}
|
|
129
|
+
await putBatch(client, batch);
|
|
130
|
+
streamState = 'created'; // a successful put proves the stream is there
|
|
178
131
|
}
|
|
179
132
|
catch (err) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
133
|
+
// The one recoverable shape: CloudWatch rejects a put into a stream
|
|
134
|
+
// that doesn't exist. Before 8.11.0 this adapter asserted that
|
|
135
|
+
// precondition and never established it, so any stream name that
|
|
136
|
+
// didn't already exist dropped every event, forever, in silence —
|
|
137
|
+
// and the documented `${HOSTNAME}/${Date.now()}` convention can never
|
|
138
|
+
// pre-exist, so following the docs guaranteed the bug.
|
|
139
|
+
if (!isMissingStreamError(err) || streamState === 'unavailable')
|
|
140
|
+
throw err;
|
|
141
|
+
await createStream(client);
|
|
142
|
+
// Exactly ONE re-put of the same batch. Not a retry loop: if this
|
|
143
|
+
// fails the events are gone, and saying so is better than growing a
|
|
144
|
+
// buffer during an outage.
|
|
145
|
+
await putBatch(client, batch);
|
|
183
146
|
}
|
|
184
147
|
}
|
|
148
|
+
/** Create the missing stream, or latch it off for good when retrying cannot
|
|
149
|
+
* help. Losing a create race to another process is success. */
|
|
150
|
+
async function createStream(client) {
|
|
151
|
+
if (!client.createLogStream) {
|
|
152
|
+
throw new Error(`log stream '${logStreamName}' does not exist in log group ` +
|
|
153
|
+
`'${opts.logGroupName}', and the injected \`_client\` has no ` +
|
|
154
|
+
`\`createLogStream\` to create it.`);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await client.createLogStream({ logGroupName: opts.logGroupName, logStreamName });
|
|
158
|
+
}
|
|
159
|
+
catch (createErr) {
|
|
160
|
+
// Two processes racing to create the same stream is normal and
|
|
161
|
+
// expected — whoever lost the race still has a usable stream.
|
|
162
|
+
if (!isAlreadyExistsError(createErr)) {
|
|
163
|
+
// Anything else (no `logs:CreateLogStream`, or the log GROUP itself is
|
|
164
|
+
// missing) will not fix itself. Latch it off so we never loop, and say
|
|
165
|
+
// which of the two it probably is.
|
|
166
|
+
streamState = 'unavailable';
|
|
167
|
+
throw new Error(`could not create log stream '${logStreamName}' in log group ` +
|
|
168
|
+
`'${opts.logGroupName}': ${errorMessage(createErr)}. Check that the log ` +
|
|
169
|
+
`GROUP exists (this adapter never creates one) and that the role allows ` +
|
|
170
|
+
`\`logs:CreateLogStream\`.`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
streamState = 'created';
|
|
174
|
+
}
|
|
175
|
+
// Buffered log events, and the one queue every put goes through — size
|
|
176
|
+
// trigger, timer, `flush()`, shutdown (outbox.ts; #223).
|
|
177
|
+
const outbox = createOutbox({
|
|
178
|
+
name: strategyName,
|
|
179
|
+
// When a delivery starts — compared as given, as before.
|
|
180
|
+
maxItems: maxBatchEvents,
|
|
181
|
+
maxBytes: maxBatchBytes,
|
|
182
|
+
// What one put may carry: the oldest events that fit; the rest ship in
|
|
183
|
+
// the next put, right after, in order. The byte measure is the one `add`
|
|
184
|
+
// records (UTF-8 message bytes) plus CloudWatch's 26 per event. An event
|
|
185
|
+
// too large for any request is still sent — alone — as it was before;
|
|
186
|
+
// CloudWatch refuses it and that one event is reported dropped, without
|
|
187
|
+
// taking its neighbours with it.
|
|
188
|
+
maxItemsPerDelivery: PUT_LOG_EVENTS_MAX_EVENTS,
|
|
189
|
+
maxBytesPerDelivery: PUT_LOG_EVENTS_MAX_BYTES,
|
|
190
|
+
overheadBytesPerItem: PUT_LOG_EVENTS_BYTES_PER_EVENT,
|
|
191
|
+
flushIntervalMs,
|
|
192
|
+
// An un-stopped strategy lingers up to `flushIntervalMs` so the timer can
|
|
193
|
+
// still ship the tail (see `stop()`).
|
|
194
|
+
timerHoldsProcess: true,
|
|
195
|
+
deliver: shipBatch,
|
|
196
|
+
dropped: (batch, cause) => new Error(`${batch.length} event(s) dropped shipping to log group '${opts.logGroupName}' ` +
|
|
197
|
+
`stream '${logStreamName}': ${errorMessage(cause)}`),
|
|
198
|
+
report: reportFailure,
|
|
199
|
+
});
|
|
185
200
|
function enqueue(event) {
|
|
186
201
|
if (stopped)
|
|
187
202
|
return;
|
|
188
203
|
// The wire rule (`lib/wireJson.ts`) — an Error's custom properties never ship.
|
|
189
204
|
const message = toWireJson(event);
|
|
190
|
-
|
|
191
|
-
buffer.push({ timestamp: Date.now(), message });
|
|
192
|
-
bufferBytes += bytes;
|
|
193
|
-
if (buffer.length >= maxBatchEvents || bufferBytes >= maxBatchBytes) {
|
|
194
|
-
// Size trigger — flush immediately. Chain onto last to preserve
|
|
195
|
-
// CloudWatch's per-stream ordering requirement.
|
|
196
|
-
lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
scheduleTimedFlush();
|
|
200
|
-
}
|
|
205
|
+
outbox.add({ timestamp: Date.now(), message }, Buffer.byteLength(message, 'utf8'));
|
|
201
206
|
}
|
|
202
207
|
const strategy = {
|
|
203
208
|
name: strategyName,
|
|
@@ -218,25 +223,14 @@ export function _buildCloudWatchObservability(opts, strategyName) {
|
|
|
218
223
|
* since 8.11.1: a flush after a stop still ships what was already
|
|
219
224
|
* accepted.
|
|
220
225
|
*/
|
|
221
|
-
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
for (;;) {
|
|
230
|
-
const pending = buffer.length;
|
|
231
|
-
// Chain, never replace: CloudWatch requires per-stream ordering, so a
|
|
232
|
-
// drain waits for the in-flight put rather than racing it.
|
|
233
|
-
lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
|
|
234
|
-
await lastFlushPromise;
|
|
235
|
-
if (buffer.length === 0)
|
|
236
|
-
return;
|
|
237
|
-
if (buffer.length >= pending)
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
226
|
+
flush() {
|
|
227
|
+
// Resolves once every event accepted before the call is shipped (or
|
|
228
|
+
// dropped and reported) — one a timed put already had in flight
|
|
229
|
+
// included — after one more sweep of what arrived while it waited; it
|
|
230
|
+
// does not chase a producer that keeps emitting. A put that never
|
|
231
|
+
// settles holds it. Since 8.11.1 delivery ignores `stop()`, so a flush
|
|
232
|
+
// after a stop still ships and never spins.
|
|
233
|
+
return outbox.drain();
|
|
240
234
|
},
|
|
241
235
|
/** Stop the flush timer and stop accepting events. **The framework does
|
|
242
236
|
* not call this for you** — an un-stopped strategy keeps a `setTimeout`
|
|
@@ -248,10 +242,7 @@ export function _buildCloudWatchObservability(opts, strategyName) {
|
|
|
248
242
|
* `stop()` still ships it (8.11.1). */
|
|
249
243
|
stop() {
|
|
250
244
|
stopped = true;
|
|
251
|
-
|
|
252
|
-
clearTimeout(timer);
|
|
253
|
-
timer = undefined;
|
|
254
|
-
}
|
|
245
|
+
outbox.stop();
|
|
255
246
|
},
|
|
256
247
|
/**
|
|
257
248
|
* Where errors go. Two callers: the dispatch layer (when `exportEvent`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudwatch.js","sourceRoot":"","sources":["../../../../src/adapters/observability/cloudwatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cloudwatch.js","sourceRoot":"","sources":["../../../../src/adapters/observability/cloudwatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,10 +36,14 @@
|
|
|
36
36
|
* `exportEvent` is sync and never touches the disk: it serializes, buffers, and
|
|
37
37
|
* returns. Batches are appended asynchronously on a size trigger
|
|
38
38
|
* (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
|
|
39
|
-
* `flush()
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* `flush()` — all through ONE queue (`outbox.ts`), so one append is in flight
|
|
40
|
+
* at a time: this sink's appends never interleave, and lines land in dispatch
|
|
41
|
+
* order (#223). That is not atomicity — a disk that fails partway through an
|
|
42
|
+
* append (ENOSPC) can still leave part of a line behind, and that batch is
|
|
43
|
+
* reported dropped. A hard kill loses the buffer and can interrupt the append
|
|
44
|
+
* in flight — the price of not making telemetry a term in agent-loop latency.
|
|
45
|
+
* Call `flush()` (or `agent.shutdown()`, which does) at process end; see the
|
|
46
|
+
* 8.12.0 lifecycle laws on {@link BaseStrategy.flush}.
|
|
43
47
|
*
|
|
44
48
|
* ## Rotation is ONE generation, and that is deliberate
|
|
45
49
|
*
|
|
@@ -36,10 +36,14 @@
|
|
|
36
36
|
* `exportEvent` is sync and never touches the disk: it serializes, buffers, and
|
|
37
37
|
* returns. Batches are appended asynchronously on a size trigger
|
|
38
38
|
* (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
|
|
39
|
-
* `flush()
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* `flush()` — all through ONE queue (`outbox.ts`), so one append is in flight
|
|
40
|
+
* at a time: this sink's appends never interleave, and lines land in dispatch
|
|
41
|
+
* order (#223). That is not atomicity — a disk that fails partway through an
|
|
42
|
+
* append (ENOSPC) can still leave part of a line behind, and that batch is
|
|
43
|
+
* reported dropped. A hard kill loses the buffer and can interrupt the append
|
|
44
|
+
* in flight — the price of not making telemetry a term in agent-loop latency.
|
|
45
|
+
* Call `flush()` (or `agent.shutdown()`, which does) at process end; see the
|
|
46
|
+
* 8.12.0 lifecycle laws on {@link BaseStrategy.flush}.
|
|
43
47
|
*
|
|
44
48
|
* ## Rotation is ONE generation, and that is deliberate
|
|
45
49
|
*
|
|
@@ -72,6 +76,7 @@
|
|
|
72
76
|
import { lazyRequire } from '../../lib/lazyRequire.js';
|
|
73
77
|
import { toWireJson } from '../../lib/wireJson.js';
|
|
74
78
|
import { rateLimitedConsoleSink } from './deliveryErrors.js';
|
|
79
|
+
import { createOutbox } from './outbox.js';
|
|
75
80
|
// ─── Public factory ──────────────────────────────────────────────────
|
|
76
81
|
/**
|
|
77
82
|
* NDJSON-to-a-local-file observability strategy. See
|
|
@@ -117,27 +122,13 @@ export function fileObservability(opts) {
|
|
|
117
122
|
`Fix the path or the permission — this sink refuses to start rather than ` +
|
|
118
123
|
`silently drop every event.`);
|
|
119
124
|
}
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let lastFlushPromise = Promise.resolve();
|
|
124
|
-
let timer;
|
|
125
|
+
// Set by `stop()`: this sink accepts no more events. It is NOT a guard on
|
|
126
|
+
// writing — `stop()` does not authorise throwing away what was already
|
|
127
|
+
// accepted, and `flush()` after it still writes (the 8.11.1 stance).
|
|
125
128
|
let stopped = false;
|
|
126
129
|
// The fallback when the consumer wires nothing. Rate-limited; a
|
|
127
130
|
// consumer-supplied sink is not.
|
|
128
131
|
const consoleSink = rateLimitedConsoleSink(strategyName);
|
|
129
|
-
function scheduleTimedFlush() {
|
|
130
|
-
if (timer || flushIntervalMs <= 0 || stopped)
|
|
131
|
-
return;
|
|
132
|
-
timer = setTimeout(() => {
|
|
133
|
-
timer = undefined;
|
|
134
|
-
void doFlush();
|
|
135
|
-
}, flushIntervalMs);
|
|
136
|
-
// Never hold the process open for telemetry. `unref` exists on Node's
|
|
137
|
-
// Timeout and not on the browser's number — feature-detected, so a
|
|
138
|
-
// non-Node runtime with an injected `_fs` still works.
|
|
139
|
-
timer.unref?.();
|
|
140
|
-
}
|
|
141
132
|
/** Route a delivery failure through whatever `_onError` IS RIGHT NOW —
|
|
142
133
|
* reading it at call time, so a consumer who assigns `_onError` after
|
|
143
134
|
* construction still receives them (the 8.11.0 fix). */
|
|
@@ -159,28 +150,29 @@ export function fileObservability(opts) {
|
|
|
159
150
|
await fs.rename(path, rotatedPath);
|
|
160
151
|
bytesOnDisk = 0;
|
|
161
152
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const batch = buffer.splice(0);
|
|
172
|
-
const batchBytes = bufferBytes;
|
|
173
|
-
bufferBytes = 0;
|
|
174
|
-
const data = `${batch.join('\n')}\n`;
|
|
175
|
-
try {
|
|
176
|
-
await rotateIfNeeded(batchBytes);
|
|
177
|
-
await fs.appendFile(path, data);
|
|
178
|
-
bytesOnDisk += byteLength(data);
|
|
179
|
-
}
|
|
180
|
-
catch (err) {
|
|
181
|
-
reportFailure(new Error(`${batch.length} event(s) dropped writing to '${path}': ${errorMessage(err)}`));
|
|
182
|
-
}
|
|
153
|
+
/** One batch: rotate if it would cross the ceiling, then append. Runs
|
|
154
|
+
* inside the outbox's queue, so no other batch — and no other rotation —
|
|
155
|
+
* can start until this one settles. */
|
|
156
|
+
async function writeBatch(lines) {
|
|
157
|
+
const data = `${lines.join('\n')}\n`;
|
|
158
|
+
const bytes = byteLength(data);
|
|
159
|
+
await rotateIfNeeded(bytes);
|
|
160
|
+
await fs.appendFile(path, data);
|
|
161
|
+
bytesOnDisk += bytes;
|
|
183
162
|
}
|
|
163
|
+
// Buffered lines, and the one queue every write goes through — size
|
|
164
|
+
// trigger, timer, `flush()`, shutdown (outbox.ts; #223).
|
|
165
|
+
const outbox = createOutbox({
|
|
166
|
+
name: strategyName,
|
|
167
|
+
maxItems: maxBufferEvents,
|
|
168
|
+
maxBytes: maxBufferBytes,
|
|
169
|
+
flushIntervalMs,
|
|
170
|
+
// Never hold the process open for a local write.
|
|
171
|
+
timerHoldsProcess: false,
|
|
172
|
+
deliver: writeBatch,
|
|
173
|
+
dropped: (lines, cause) => new Error(`${lines.length} event(s) dropped writing to '${path}': ${errorMessage(cause)}`),
|
|
174
|
+
report: reportFailure,
|
|
175
|
+
});
|
|
184
176
|
function enqueue(event) {
|
|
185
177
|
if (stopped)
|
|
186
178
|
return;
|
|
@@ -202,17 +194,11 @@ export function fileObservability(opts) {
|
|
|
202
194
|
// write, and a bare "undefined" line would corrupt the NDJSON stream.
|
|
203
195
|
if (line === undefined)
|
|
204
196
|
return;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// offline reader can rely on.
|
|
211
|
-
lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
scheduleTimedFlush();
|
|
215
|
-
}
|
|
197
|
+
// + the newline this line will carry. The outbox decides when it is
|
|
198
|
+
// written, and writes it after every line accepted before it — the file's
|
|
199
|
+
// line order IS the dispatch order, the one thing an offline reader can
|
|
200
|
+
// rely on.
|
|
201
|
+
outbox.add(line, byteLength(line) + 1);
|
|
216
202
|
}
|
|
217
203
|
const strategy = {
|
|
218
204
|
name: strategyName,
|
|
@@ -223,31 +209,22 @@ export function fileObservability(opts) {
|
|
|
223
209
|
* Write what is buffered. Called for you on shutdown (8.12.0) — by the
|
|
224
210
|
* handle `enable.observability()` returns, by `agent.shutdown()`, and by a
|
|
225
211
|
* `standingAgent` closing. Safe at any time, including after `stop()`.
|
|
212
|
+
*
|
|
213
|
+
* Resolves once every event accepted before the call is written (or
|
|
214
|
+
* dropped and reported) — one a timed write already had in flight
|
|
215
|
+
* included — after one more sweep of what arrived while it waited. It
|
|
216
|
+
* does not chase a producer that keeps emitting. A write that never
|
|
217
|
+
* settles holds it: nothing can cancel an in-flight write.
|
|
226
218
|
*/
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// line; a pass that removes none ends the drain instead of retrying. The
|
|
230
|
-
// shape the CloudWatch adapter arrived at in 8.11.1, for the same reason:
|
|
231
|
-
// a drain that cannot finish must return, never spin.
|
|
232
|
-
for (;;) {
|
|
233
|
-
const pending = buffer.length;
|
|
234
|
-
lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
|
|
235
|
-
await lastFlushPromise;
|
|
236
|
-
if (buffer.length === 0)
|
|
237
|
-
return;
|
|
238
|
-
if (buffer.length >= pending)
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
219
|
+
flush() {
|
|
220
|
+
return outbox.drain();
|
|
241
221
|
},
|
|
242
222
|
/** Clear the timer and stop accepting events. Terminal: there is no
|
|
243
223
|
* restart. What it does NOT do is discard the buffer — `flush()` after
|
|
244
224
|
* `stop()` still writes it. */
|
|
245
225
|
stop() {
|
|
246
226
|
stopped = true;
|
|
247
|
-
|
|
248
|
-
clearTimeout(timer);
|
|
249
|
-
timer = undefined;
|
|
250
|
-
}
|
|
227
|
+
outbox.stop();
|
|
251
228
|
},
|
|
252
229
|
/** Where failures go. Two callers: the dispatch layer (when `exportEvent`
|
|
253
230
|
* itself throws) and this adapter's own write path. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/adapters/observability/file.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/adapters/observability/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outbox — the one owner of buffered, timed, serialized delivery for the
|
|
3
|
+
* batching sinks in this folder: `file`, `cloudwatch` (and `agentcore`, which
|
|
4
|
+
* is built on it) and `xray`.
|
|
5
|
+
*
|
|
6
|
+
* A sink accepts items on its hot path (sync, no I/O) and hands them here. The
|
|
7
|
+
* outbox decides WHEN a batch leaves and makes sure only one leaves at a time:
|
|
8
|
+
*
|
|
9
|
+
* - **One queue, every trigger.** The size trigger, the timer and `drain()`
|
|
10
|
+
* (the sink's `flush()`, and so every shutdown) all queue a delivery on
|
|
11
|
+
* the same promise chain. Nothing calls `deliver` any other way.
|
|
12
|
+
* - **One delivery in flight.** A delivery starts only after the previous
|
|
13
|
+
* one settled. A large `fs.promises.appendFile` is several `write()`s; two
|
|
14
|
+
* of them interleaved tear NDJSON lines (reported in #223, where the timer
|
|
15
|
+
* bypassed the chain). Whatever a sink does around its write — the file
|
|
16
|
+
* sink's rotation — runs inside its delivery, so it can never be skipped
|
|
17
|
+
* or reordered either. This keeps ONE sink's deliveries from interleaving;
|
|
18
|
+
* it does not make a delivery atomic (a disk that fails partway through an
|
|
19
|
+
* append can still leave part of it behind).
|
|
20
|
+
* - **Oldest first, within the receiver's limits.** A delivery takes the
|
|
21
|
+
* oldest waiting items, so items leave in the order they were accepted.
|
|
22
|
+
* When a receiving service limits one request (`maxItemsPerDelivery`,
|
|
23
|
+
* `maxBytesPerDelivery` — CloudWatch's PutLogEvents), a delivery takes
|
|
24
|
+
* only the oldest items that fit, and the rest go in the next delivery,
|
|
25
|
+
* right after. That matters BECAUSE of the queue: a slow delivery lets a
|
|
26
|
+
* backlog build behind it, which one request may not be allowed to carry.
|
|
27
|
+
* Each part settles on its own — shipped, or dropped and reported.
|
|
28
|
+
* - **`drain()` is bounded.** It resolves once every item accepted BEFORE
|
|
29
|
+
* the call has settled (delivered, or dropped and reported), one the timer
|
|
30
|
+
* already had in flight included — then makes one sweep of what arrived
|
|
31
|
+
* while it waited. It never chases a producer that keeps emitting: under
|
|
32
|
+
* steady traffic it ends after about two deliveries.
|
|
33
|
+
* - **A failed batch is dropped and reported once**, then the next batch
|
|
34
|
+
* goes. It is never requeued, so a full disk or an outage cannot grow the
|
|
35
|
+
* outbox. The queue never rejects: a failure report that itself throws is
|
|
36
|
+
* said on the console instead of becoming an unhandled rejection.
|
|
37
|
+
*
|
|
38
|
+
* What it does NOT do: bound a delivery that never settles. A hung disk holds
|
|
39
|
+
* the queue, and `drain()` with it; nothing can cancel an in-flight write.
|
|
40
|
+
*
|
|
41
|
+
* Role: leaf helper for `adapters/observability/*`. Composed as a field,
|
|
42
|
+
* never inherited.
|
|
43
|
+
*/
|
|
44
|
+
/** What a sink tells its outbox. */
|
|
45
|
+
export interface OutboxOptions<T> {
|
|
46
|
+
/** The sink's name: it prefixes a refused option and the console line when
|
|
47
|
+
* a failure report throws. */
|
|
48
|
+
readonly name: string;
|
|
49
|
+
/** Size trigger: deliver once `waiting >= maxItems`. Compared as given —
|
|
50
|
+
* the meaning each sink's option had before the outbox, so no value that
|
|
51
|
+
* worked is refused: `0` or a negative number triggers on every item,
|
|
52
|
+
* `2.5` on the third, `NaN` or `Infinity` never (then only the byte
|
|
53
|
+
* trigger, the timer and `drain()` deliver). It never limits what a
|
|
54
|
+
* delivery takes. */
|
|
55
|
+
readonly maxItems: number;
|
|
56
|
+
/** Size trigger: deliver once `waitingBytes >= maxBytes`, compared as
|
|
57
|
+
* given. Omitted → no byte trigger. */
|
|
58
|
+
readonly maxBytes?: number;
|
|
59
|
+
/** Most items one delivery takes: a positive whole number — a sink maps its
|
|
60
|
+
* own option to one first, and refuses by its own name what cannot map.
|
|
61
|
+
* Omitted → everything waiting. */
|
|
62
|
+
readonly maxItemsPerDelivery?: number;
|
|
63
|
+
/** Most bytes one delivery may carry, as the receiving service counts them:
|
|
64
|
+
* for each item, the `bytes` it was added with plus `overheadBytesPerItem`.
|
|
65
|
+
* A delivery always takes at least one item, so an item too large for any
|
|
66
|
+
* delivery still goes — alone — and the service's answer decides it.
|
|
67
|
+
* Omitted → no byte limit. */
|
|
68
|
+
readonly maxBytesPerDelivery?: number;
|
|
69
|
+
/** Bytes the receiving service adds to each item it counts (CloudWatch
|
|
70
|
+
* Logs: 26 per event). Default 0. */
|
|
71
|
+
readonly overheadBytesPerItem?: number;
|
|
72
|
+
/** Timer trigger for sparse traffic, in ms. `0` disables it: only the size
|
|
73
|
+
* trigger and `drain()` deliver. */
|
|
74
|
+
readonly flushIntervalMs: number;
|
|
75
|
+
/** Does an armed timer keep the process alive? Each sink decides: the file
|
|
76
|
+
* sink says no (a local write is never worth holding a process open for),
|
|
77
|
+
* the network sinks say yes (an un-stopped one lingers up to
|
|
78
|
+
* `flushIntervalMs` so its timer can still ship the tail). */
|
|
79
|
+
readonly timerHoldsProcess: boolean;
|
|
80
|
+
/** Ship one batch. Never called while another call is still in flight. */
|
|
81
|
+
readonly deliver: (batch: readonly T[]) => Promise<void>;
|
|
82
|
+
/** Describe a batch `deliver` failed to ship — the report it is dropped
|
|
83
|
+
* with. */
|
|
84
|
+
readonly dropped: (batch: readonly T[], cause: unknown) => Error;
|
|
85
|
+
/** Where that report goes. Read at call time by the sink, so a consumer
|
|
86
|
+
* who assigns `_onError` after construction still receives it. */
|
|
87
|
+
readonly report: (error: Error) => void;
|
|
88
|
+
}
|
|
89
|
+
/** The three things a sink does with its outbox. */
|
|
90
|
+
export interface Outbox<T> {
|
|
91
|
+
/** Accept one item. Sync, never throws, never does I/O. `bytes` counts
|
|
92
|
+
* toward the byte trigger. Accepting is the SINK's decision — the outbox
|
|
93
|
+
* takes what it is given, including after `stop()`. */
|
|
94
|
+
add(item: T, bytes?: number): void;
|
|
95
|
+
/** Settle everything accepted before this call — behind whatever is in
|
|
96
|
+
* flight — then sweep once what arrived meanwhile. Never rejects. */
|
|
97
|
+
drain(): Promise<void>;
|
|
98
|
+
/** Cancel the timer and arm no new one. Discards nothing: `drain()`
|
|
99
|
+
* afterwards still delivers what was accepted. */
|
|
100
|
+
stop(): void;
|
|
101
|
+
}
|
|
102
|
+
/** Build the outbox one sink owns. A per-delivery cap that could take
|
|
103
|
+
* nothing is refused: such a delivery could never finish a drain. */
|
|
104
|
+
export declare function createOutbox<T>(opts: OutboxOptions<T>): Outbox<T>;
|