logquill 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +392 -6
- package/dist/browser.d.ts +468 -0
- package/dist/browser.mjs +783 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/index.cjs +756 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +561 -126
- package/dist/index.d.ts +561 -126
- package/dist/index.mjs +736 -9
- package/dist/index.mjs.map +1 -1
- package/dist/langchain.cjs +120 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +128 -0
- package/dist/langchain.d.ts +128 -0
- package/dist/langchain.mjs +116 -0
- package/dist/langchain.mjs.map +1 -0
- package/dist/logger-CRdmCDfC.d.cts +299 -0
- package/dist/logger-CRdmCDfC.d.ts +299 -0
- package/dist/winston.cjs +108 -0
- package/dist/winston.cjs.map +1 -0
- package/dist/winston.d.cts +55 -0
- package/dist/winston.d.ts +55 -0
- package/dist/winston.mjs +101 -0
- package/dist/winston.mjs.map +1 -0
- package/package.json +57 -12
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
// src/core/levels.ts
|
|
2
|
+
var Level = /* @__PURE__ */ ((Level2) => {
|
|
3
|
+
Level2[Level2["TRACE"] = 5] = "TRACE";
|
|
4
|
+
Level2[Level2["DEBUG"] = 10] = "DEBUG";
|
|
5
|
+
Level2[Level2["INFO"] = 20] = "INFO";
|
|
6
|
+
Level2[Level2["WARN"] = 30] = "WARN";
|
|
7
|
+
Level2[Level2["ERROR"] = 40] = "ERROR";
|
|
8
|
+
Level2[Level2["FATAL"] = 50] = "FATAL";
|
|
9
|
+
return Level2;
|
|
10
|
+
})(Level || {});
|
|
11
|
+
var NAME_TO_LEVEL = {
|
|
12
|
+
TRACE: 5 /* TRACE */,
|
|
13
|
+
DEBUG: 10 /* DEBUG */,
|
|
14
|
+
INFO: 20 /* INFO */,
|
|
15
|
+
WARN: 30 /* WARN */,
|
|
16
|
+
ERROR: 40 /* ERROR */,
|
|
17
|
+
FATAL: 50 /* FATAL */
|
|
18
|
+
};
|
|
19
|
+
function levelName(level) {
|
|
20
|
+
return Level[level];
|
|
21
|
+
}
|
|
22
|
+
function parseLevel(level) {
|
|
23
|
+
if (typeof level === "string") {
|
|
24
|
+
const parsed = NAME_TO_LEVEL[level.toUpperCase()];
|
|
25
|
+
if (parsed === void 0) {
|
|
26
|
+
throw new Error(`Unknown log level: ${level}`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
if (Level[level] === void 0) {
|
|
31
|
+
throw new Error(`Unknown log level: ${String(level)}`);
|
|
32
|
+
}
|
|
33
|
+
return level;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/core/records.ts
|
|
37
|
+
function utcTimestamp() {
|
|
38
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
39
|
+
}
|
|
40
|
+
function createRecord(params) {
|
|
41
|
+
return {
|
|
42
|
+
timestamp: utcTimestamp(),
|
|
43
|
+
level: levelName(params.level),
|
|
44
|
+
logger: params.logger,
|
|
45
|
+
message: params.message,
|
|
46
|
+
meta: params.meta
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/core/formatter.ts
|
|
51
|
+
var JSONFormatter = class {
|
|
52
|
+
/** Returns `JSON.stringify(record)`. */
|
|
53
|
+
format(record) {
|
|
54
|
+
return JSON.stringify(record);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/core/plugin.ts
|
|
59
|
+
var FunctionPlugin = class {
|
|
60
|
+
func;
|
|
61
|
+
constructor(func) {
|
|
62
|
+
this.func = func;
|
|
63
|
+
}
|
|
64
|
+
beforeLog(record) {
|
|
65
|
+
return this.func(record);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/plugins/context-plugin.ts
|
|
70
|
+
var ContextPlugin = class {
|
|
71
|
+
/** Fixed key/value pairs merged into every record's `meta`. */
|
|
72
|
+
context;
|
|
73
|
+
constructor(context) {
|
|
74
|
+
this.context = context;
|
|
75
|
+
}
|
|
76
|
+
beforeLog(record) {
|
|
77
|
+
return { ...record, meta: { ...this.context, ...record.meta } };
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/plugins/redact-plugin.ts
|
|
82
|
+
var DEFAULT_REDACTED_KEYS = ["password", "token", "secret", "api_key", "authorization"];
|
|
83
|
+
var RedactPlugin = class {
|
|
84
|
+
keys;
|
|
85
|
+
/** Placeholder a matched value is replaced with. */
|
|
86
|
+
replacement;
|
|
87
|
+
constructor(options = {}) {
|
|
88
|
+
this.keys = new Set((options.keys ?? DEFAULT_REDACTED_KEYS).map((key) => key.toLowerCase()));
|
|
89
|
+
this.replacement = options.replacement ?? "***";
|
|
90
|
+
}
|
|
91
|
+
beforeLog(record) {
|
|
92
|
+
const meta = {};
|
|
93
|
+
for (const [key, value] of Object.entries(record.meta)) {
|
|
94
|
+
meta[key] = this.keys.has(key.toLowerCase()) ? this.replacement : value;
|
|
95
|
+
}
|
|
96
|
+
return { ...record, meta };
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/plugins/pii-redact-plugin.ts
|
|
101
|
+
var DEFAULT_PII_PATTERNS = {
|
|
102
|
+
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
103
|
+
ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
104
|
+
creditCard: /\b(?:\d[ -]?){13,16}\b/g,
|
|
105
|
+
phone: /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g
|
|
106
|
+
};
|
|
107
|
+
var MAX_DEPTH = 50;
|
|
108
|
+
var PIIRedactPlugin = class {
|
|
109
|
+
/** Named patterns scanned for in every string `meta` value. */
|
|
110
|
+
patterns;
|
|
111
|
+
/** Placeholder a matched substring is replaced with. */
|
|
112
|
+
replacement;
|
|
113
|
+
constructor(options = {}) {
|
|
114
|
+
this.patterns = options.patterns ?? DEFAULT_PII_PATTERNS;
|
|
115
|
+
this.replacement = options.replacement ?? "***";
|
|
116
|
+
}
|
|
117
|
+
beforeLog(record) {
|
|
118
|
+
return { ...record, meta: this.redactValue(record.meta, /* @__PURE__ */ new Set(), 0) };
|
|
119
|
+
}
|
|
120
|
+
redactValue(value, seen, depth) {
|
|
121
|
+
if (depth > MAX_DEPTH) {
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === "string") {
|
|
125
|
+
return this.redactText(value);
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
if (seen.has(value)) {
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
const nextSeen = new Set(seen).add(value);
|
|
132
|
+
return value.map((entry) => this.redactValue(entry, nextSeen, depth + 1));
|
|
133
|
+
}
|
|
134
|
+
if (value !== null && typeof value === "object") {
|
|
135
|
+
if (seen.has(value)) {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
const nextSeen = new Set(seen).add(value);
|
|
139
|
+
const result = {};
|
|
140
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
141
|
+
result[key] = this.redactValue(entryValue, nextSeen, depth + 1);
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
redactText(text) {
|
|
148
|
+
let redacted = text;
|
|
149
|
+
for (const pattern of Object.values(this.patterns)) {
|
|
150
|
+
const global = pattern.global ? pattern : new RegExp(pattern.source, `${pattern.flags}g`);
|
|
151
|
+
redacted = redacted.replace(global, this.replacement);
|
|
152
|
+
}
|
|
153
|
+
return redacted;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// src/plugins/sampling-plugin.ts
|
|
158
|
+
var SamplingPlugin = class {
|
|
159
|
+
/** Fraction of non-elevated records kept, in `[0, 1]`. */
|
|
160
|
+
rate;
|
|
161
|
+
/** `meta` key holding the trace/run id used to group buffered records. */
|
|
162
|
+
traceKey;
|
|
163
|
+
/** A record at or above this level elevates its whole trace. */
|
|
164
|
+
elevateAt;
|
|
165
|
+
/** Transports buffered records are flushed straight to on elevation; `undefined` disables tail-based elevation. */
|
|
166
|
+
transports;
|
|
167
|
+
/** Total buffered records allowed across every trace before the oldest trace is evicted. */
|
|
168
|
+
maxBufferedRecords;
|
|
169
|
+
/** Distinct trace ids held at once before the oldest is evicted. */
|
|
170
|
+
maxTraces;
|
|
171
|
+
rng;
|
|
172
|
+
buffer = /* @__PURE__ */ new Map();
|
|
173
|
+
bufferedCount = 0;
|
|
174
|
+
elevated = /* @__PURE__ */ new Set();
|
|
175
|
+
constructor(rate, options = {}) {
|
|
176
|
+
if (rate < 0 || rate > 1) {
|
|
177
|
+
throw new Error(`rate must be between 0 and 1, got ${String(rate)}`);
|
|
178
|
+
}
|
|
179
|
+
this.rate = rate;
|
|
180
|
+
this.rng = options.rng ?? Math.random;
|
|
181
|
+
this.traceKey = options.traceKey ?? "traceId";
|
|
182
|
+
this.elevateAt = parseLevel(options.elevateAt ?? 40 /* ERROR */);
|
|
183
|
+
this.transports = options.transports;
|
|
184
|
+
this.maxBufferedRecords = options.maxBufferedRecords ?? 1e3;
|
|
185
|
+
this.maxTraces = options.maxTraces ?? 200;
|
|
186
|
+
}
|
|
187
|
+
beforeLog(record) {
|
|
188
|
+
const transports = this.transports;
|
|
189
|
+
if (transports === void 0) {
|
|
190
|
+
return this.rng() < this.rate ? record : null;
|
|
191
|
+
}
|
|
192
|
+
const traceId = record.meta[this.traceKey];
|
|
193
|
+
if (traceId !== void 0 && this.elevated.has(traceId)) {
|
|
194
|
+
return record;
|
|
195
|
+
}
|
|
196
|
+
const keep = this.rng() < this.rate;
|
|
197
|
+
const reachedElevateLevel = parseLevel(record.level) >= this.elevateAt;
|
|
198
|
+
if (traceId !== void 0 && reachedElevateLevel) {
|
|
199
|
+
this.elevate(traceId, transports);
|
|
200
|
+
return record;
|
|
201
|
+
}
|
|
202
|
+
if (keep) {
|
|
203
|
+
return record;
|
|
204
|
+
}
|
|
205
|
+
if (traceId !== void 0) {
|
|
206
|
+
this.bufferRecord(traceId, record);
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
elevate(traceId, transports) {
|
|
211
|
+
this.elevated.add(traceId);
|
|
212
|
+
const buffered = this.buffer.get(traceId) ?? [];
|
|
213
|
+
this.buffer.delete(traceId);
|
|
214
|
+
this.bufferedCount -= buffered.length;
|
|
215
|
+
for (const bufferedRecord of buffered) {
|
|
216
|
+
for (const transport of transports) {
|
|
217
|
+
transport.write(transport.format(bufferedRecord), bufferedRecord);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
bufferRecord(traceId, record) {
|
|
222
|
+
let records = this.buffer.get(traceId);
|
|
223
|
+
if (records) {
|
|
224
|
+
this.buffer.delete(traceId);
|
|
225
|
+
this.buffer.set(traceId, records);
|
|
226
|
+
} else {
|
|
227
|
+
if (this.buffer.size >= this.maxTraces) {
|
|
228
|
+
this.evictOldestTrace();
|
|
229
|
+
}
|
|
230
|
+
records = [];
|
|
231
|
+
this.buffer.set(traceId, records);
|
|
232
|
+
}
|
|
233
|
+
records.push(record);
|
|
234
|
+
this.bufferedCount += 1;
|
|
235
|
+
while (this.bufferedCount > this.maxBufferedRecords && this.buffer.size > 0) {
|
|
236
|
+
this.evictOldestTrace();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
evictOldestTrace() {
|
|
240
|
+
const oldest = this.buffer.entries().next();
|
|
241
|
+
if (oldest.done) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const [oldestKey, oldestRecords] = oldest.value;
|
|
245
|
+
this.buffer.delete(oldestKey);
|
|
246
|
+
this.bufferedCount -= oldestRecords.length;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// src/transports/transport.ts
|
|
251
|
+
var Transport = class {
|
|
252
|
+
/** Turns a `LogRecord` into the string this transport actually writes. Defaults to `JSONFormatter`. */
|
|
253
|
+
formatter;
|
|
254
|
+
constructor(formatter = new JSONFormatter()) {
|
|
255
|
+
this.formatter = formatter;
|
|
256
|
+
}
|
|
257
|
+
/** Formats `record` via `this.formatter`. Called once per record before `write()`. */
|
|
258
|
+
format(record) {
|
|
259
|
+
return this.formatter.format(record);
|
|
260
|
+
}
|
|
261
|
+
/** Flush/release resources on shutdown. No-op unless a transport overrides it. */
|
|
262
|
+
close() {
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
function hasFlush(transport) {
|
|
266
|
+
return typeof transport.flush === "function";
|
|
267
|
+
}
|
|
268
|
+
var CollectingTransport = class extends Transport {
|
|
269
|
+
/** Every formatted string passed to `write()`, in call order. */
|
|
270
|
+
formatted = [];
|
|
271
|
+
/** Every raw `LogRecord` passed to `write()`, in call order. */
|
|
272
|
+
records = [];
|
|
273
|
+
/** Set once `close()` has been called. */
|
|
274
|
+
closed = false;
|
|
275
|
+
write(formatted, record) {
|
|
276
|
+
this.formatted.push(formatted);
|
|
277
|
+
this.records.push(record);
|
|
278
|
+
}
|
|
279
|
+
close() {
|
|
280
|
+
this.closed = true;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/transports/console-transport.ts
|
|
285
|
+
var COLORS = {
|
|
286
|
+
[5 /* TRACE */]: "\x1B[90m",
|
|
287
|
+
// gray
|
|
288
|
+
[10 /* DEBUG */]: "\x1B[36m",
|
|
289
|
+
// cyan
|
|
290
|
+
[20 /* INFO */]: "\x1B[32m",
|
|
291
|
+
// green
|
|
292
|
+
[30 /* WARN */]: "\x1B[33m",
|
|
293
|
+
// yellow
|
|
294
|
+
[40 /* ERROR */]: "\x1B[31m",
|
|
295
|
+
// red
|
|
296
|
+
[50 /* FATAL */]: "\x1B[35m"
|
|
297
|
+
// magenta
|
|
298
|
+
};
|
|
299
|
+
var RESET = "\x1B[0m";
|
|
300
|
+
function defaultColorize() {
|
|
301
|
+
const env = typeof process === "undefined" ? void 0 : process.env;
|
|
302
|
+
return env?.NO_COLOR === void 0;
|
|
303
|
+
}
|
|
304
|
+
var ConsoleTransport = class extends Transport {
|
|
305
|
+
/** Whether each line is wrapped in an ANSI color escape for its level. */
|
|
306
|
+
colorize;
|
|
307
|
+
out;
|
|
308
|
+
constructor(options = {}) {
|
|
309
|
+
super(options.formatter);
|
|
310
|
+
this.colorize = options.colorize ?? defaultColorize();
|
|
311
|
+
this.out = options.console ?? console;
|
|
312
|
+
}
|
|
313
|
+
/** Writes `formatted` via `console.log`, or `console.error` for ERROR/FATAL records. */
|
|
314
|
+
write(formatted, record) {
|
|
315
|
+
const level = parseLevel(record.level);
|
|
316
|
+
const line = this.colorize ? this.applyColor(formatted, level) : formatted;
|
|
317
|
+
if (level >= 40 /* ERROR */) {
|
|
318
|
+
this.out.error(line);
|
|
319
|
+
} else {
|
|
320
|
+
this.out.log(line);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
applyColor(formatted, level) {
|
|
324
|
+
return `${COLORS[level]}${formatted}${RESET}`;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/transports/beacon-transport.ts
|
|
329
|
+
function defaultBeaconSender(url, batch) {
|
|
330
|
+
const body = batch.join("\n");
|
|
331
|
+
const nav = typeof navigator === "undefined" ? void 0 : navigator;
|
|
332
|
+
if (nav && typeof nav.sendBeacon === "function") {
|
|
333
|
+
nav.sendBeacon(url, body);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
void fetch(url, { method: "POST", body, keepalive: true }).catch((error) => {
|
|
337
|
+
console.error("BeaconTransport: failed to send log batch", error);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
var BeaconTransport = class extends Transport {
|
|
341
|
+
/** Endpoint each batch is sent to. */
|
|
342
|
+
url;
|
|
343
|
+
/** Buffer is flushed once it holds this many lines. */
|
|
344
|
+
batchSize;
|
|
345
|
+
sender;
|
|
346
|
+
batch = [];
|
|
347
|
+
constructor(url, options = {}) {
|
|
348
|
+
super(options.formatter);
|
|
349
|
+
this.url = url;
|
|
350
|
+
this.batchSize = options.batchSize ?? 20;
|
|
351
|
+
this.sender = options.sender ?? defaultBeaconSender;
|
|
352
|
+
}
|
|
353
|
+
/** Buffers the formatted line, flushing the batch once `batchSize` is reached. */
|
|
354
|
+
write(formatted) {
|
|
355
|
+
this.batch.push(formatted);
|
|
356
|
+
if (this.batch.length >= this.batchSize) {
|
|
357
|
+
this.flush();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/** Send the current batch now, even if it hasn't reached `batchSize`. */
|
|
361
|
+
flush() {
|
|
362
|
+
if (this.batch.length === 0) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const batch = this.batch;
|
|
366
|
+
this.batch = [];
|
|
367
|
+
try {
|
|
368
|
+
this.sender(this.url, batch);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error("BeaconTransport: failed to send log batch", error);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
close() {
|
|
374
|
+
this.flush();
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/core/context-browser.ts
|
|
379
|
+
var contextStack = [];
|
|
380
|
+
function currentContext() {
|
|
381
|
+
return contextStack[contextStack.length - 1] ?? {};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/core/dispatch-queue.ts
|
|
385
|
+
function isPromise(value) {
|
|
386
|
+
return typeof value === "object" && typeof value.then === "function";
|
|
387
|
+
}
|
|
388
|
+
function defaultScheduler(run) {
|
|
389
|
+
if (typeof setImmediate === "function") {
|
|
390
|
+
setImmediate(run);
|
|
391
|
+
} else {
|
|
392
|
+
queueMicrotask(run);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function defaultOnDrop(count, policy) {
|
|
396
|
+
console.warn(
|
|
397
|
+
`DispatchQueue: dropped ${String(count)} record(s) \u2014 queue exceeded its configured size under the "${policy}" backpressure policy`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
var DispatchQueue = class {
|
|
401
|
+
/** Maximum number of pending tasks held at once, as configured via `DispatchQueueOptions`. */
|
|
402
|
+
maxSize;
|
|
403
|
+
/** Backpressure policy applied once `maxSize` is reached, as configured via `DispatchQueueOptions`. */
|
|
404
|
+
policy;
|
|
405
|
+
onDrop;
|
|
406
|
+
warnIntervalMs;
|
|
407
|
+
tasks = [];
|
|
408
|
+
draining = false;
|
|
409
|
+
scheduled = false;
|
|
410
|
+
idleWaiters = [];
|
|
411
|
+
droppedSinceWarning = 0;
|
|
412
|
+
lastWarnAt = 0;
|
|
413
|
+
constructor(options = {}) {
|
|
414
|
+
this.maxSize = options.maxSize ?? 1e4;
|
|
415
|
+
this.policy = options.policy ?? "dropOldest";
|
|
416
|
+
this.onDrop = options.onDrop ?? defaultOnDrop;
|
|
417
|
+
this.warnIntervalMs = options.warnIntervalMs ?? 5e3;
|
|
418
|
+
}
|
|
419
|
+
/** Number of tasks currently waiting to run. Bounded by `maxSize`. */
|
|
420
|
+
get size() {
|
|
421
|
+
return this.tasks.length;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Queue `task` to run outside the current call stack, applying the
|
|
425
|
+
* configured backpressure policy if the queue is already full. Under
|
|
426
|
+
* `"block"`, `task` may run synchronously before this call returns.
|
|
427
|
+
*/
|
|
428
|
+
enqueue(task) {
|
|
429
|
+
if (this.tasks.length < this.maxSize) {
|
|
430
|
+
this.tasks.push(task);
|
|
431
|
+
this.schedule();
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
switch (this.policy) {
|
|
435
|
+
case "dropNewest":
|
|
436
|
+
this.recordDrop();
|
|
437
|
+
return;
|
|
438
|
+
case "block":
|
|
439
|
+
this.runInline(task);
|
|
440
|
+
return;
|
|
441
|
+
case "dropOldest":
|
|
442
|
+
default:
|
|
443
|
+
this.tasks.shift();
|
|
444
|
+
this.recordDrop();
|
|
445
|
+
this.tasks.push(task);
|
|
446
|
+
this.schedule();
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/** Resolves once every task queued so far has run. Safe to call when idle. */
|
|
451
|
+
async flush() {
|
|
452
|
+
if (this.tasks.length === 0 && !this.draining) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
await new Promise((resolve) => {
|
|
456
|
+
this.idleWaiters.push(resolve);
|
|
457
|
+
this.schedule();
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
recordDrop() {
|
|
461
|
+
this.droppedSinceWarning += 1;
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
if (now - this.lastWarnAt >= this.warnIntervalMs) {
|
|
464
|
+
const count = this.droppedSinceWarning;
|
|
465
|
+
this.droppedSinceWarning = 0;
|
|
466
|
+
this.lastWarnAt = now;
|
|
467
|
+
try {
|
|
468
|
+
this.onDrop(count, this.policy);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
runInline(task) {
|
|
474
|
+
try {
|
|
475
|
+
const result = task();
|
|
476
|
+
if (isPromise(result)) {
|
|
477
|
+
result.catch((error) => {
|
|
478
|
+
console.error("DispatchQueue: a blocked task failed", error);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
} catch (error) {
|
|
482
|
+
console.error("DispatchQueue: a blocked task failed", error);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
schedule() {
|
|
486
|
+
if (this.scheduled) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
this.scheduled = true;
|
|
490
|
+
defaultScheduler(() => {
|
|
491
|
+
this.scheduled = false;
|
|
492
|
+
void this.drainAll();
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
async drainAll() {
|
|
496
|
+
if (this.draining) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
this.draining = true;
|
|
500
|
+
try {
|
|
501
|
+
while (this.tasks.length > 0) {
|
|
502
|
+
const task = this.tasks.shift();
|
|
503
|
+
if (!task) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
await task();
|
|
508
|
+
} catch (error) {
|
|
509
|
+
console.error("DispatchQueue: a queued task failed", error);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} finally {
|
|
513
|
+
this.draining = false;
|
|
514
|
+
const waiters = this.idleWaiters;
|
|
515
|
+
this.idleWaiters = [];
|
|
516
|
+
for (const resolve of waiters) {
|
|
517
|
+
resolve();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// src/core/span-browser.ts
|
|
524
|
+
var spanStack = [];
|
|
525
|
+
function currentSpanId() {
|
|
526
|
+
return spanStack[spanStack.length - 1];
|
|
527
|
+
}
|
|
528
|
+
function newSpanId() {
|
|
529
|
+
const bytes = new Uint8Array(8);
|
|
530
|
+
crypto.getRandomValues(bytes);
|
|
531
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
532
|
+
}
|
|
533
|
+
function runInSpan(spanId, fn) {
|
|
534
|
+
spanStack.push(spanId);
|
|
535
|
+
let result;
|
|
536
|
+
try {
|
|
537
|
+
result = fn();
|
|
538
|
+
} catch (error) {
|
|
539
|
+
spanStack.pop();
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
if (result instanceof Promise) {
|
|
543
|
+
return result.finally(() => spanStack.pop());
|
|
544
|
+
}
|
|
545
|
+
spanStack.pop();
|
|
546
|
+
return result;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/core/logger.ts
|
|
550
|
+
function formatSpanError(error) {
|
|
551
|
+
if (error instanceof Error) {
|
|
552
|
+
return `${error.name}: ${error.message}`;
|
|
553
|
+
}
|
|
554
|
+
return String(error);
|
|
555
|
+
}
|
|
556
|
+
function withStackFromErr(meta) {
|
|
557
|
+
const err = meta.err;
|
|
558
|
+
if (!(err instanceof Error)) {
|
|
559
|
+
return meta;
|
|
560
|
+
}
|
|
561
|
+
const next = { ...meta };
|
|
562
|
+
delete next.err;
|
|
563
|
+
next.stack = err.stack ?? `${err.name}: ${err.message}`;
|
|
564
|
+
return next;
|
|
565
|
+
}
|
|
566
|
+
var Logger = class _Logger {
|
|
567
|
+
/** This logger's name, as passed to the constructor (or derived via `.child()`). Appears on every record as `logger`. */
|
|
568
|
+
name;
|
|
569
|
+
/** Every transport a written record is sent to. */
|
|
570
|
+
transports;
|
|
571
|
+
/** Every plugin registered via `.use()`, in registration order. */
|
|
572
|
+
plugins;
|
|
573
|
+
currentLevel;
|
|
574
|
+
baseMeta;
|
|
575
|
+
dispatchQueue;
|
|
576
|
+
constructor(name, options = {}) {
|
|
577
|
+
this.name = name;
|
|
578
|
+
this.currentLevel = parseLevel(options.level ?? 20 /* INFO */);
|
|
579
|
+
this.transports = options.transports ? [...options.transports] : [];
|
|
580
|
+
this.plugins = [];
|
|
581
|
+
for (const plugin of options.plugins ?? []) {
|
|
582
|
+
this.use(plugin);
|
|
583
|
+
}
|
|
584
|
+
this.baseMeta = options.meta ? { ...options.meta } : {};
|
|
585
|
+
this.dispatchQueue = new DispatchQueue(options.queue);
|
|
586
|
+
}
|
|
587
|
+
/** This logger's current minimum level — records below it are filtered before any plugin runs. */
|
|
588
|
+
get level() {
|
|
589
|
+
return this.currentLevel;
|
|
590
|
+
}
|
|
591
|
+
/** Changes the minimum level records must meet to reach a transport. Accepts a `Level`, its numeric weight, or its name. */
|
|
592
|
+
setLevel(level) {
|
|
593
|
+
this.currentLevel = parseLevel(level);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Register a plugin, or a plain `beforeLog`-style function. A function is
|
|
597
|
+
* wrapped internally as an anonymous `Plugin` (`FunctionPlugin`) — the
|
|
598
|
+
* same middleware ergonomics as Express/Koa, without needing to read the
|
|
599
|
+
* `Plugin` interface first. Returns `this` so calls can be chained.
|
|
600
|
+
*/
|
|
601
|
+
use(plugin) {
|
|
602
|
+
this.plugins.push(typeof plugin === "function" ? new FunctionPlugin(plugin) : plugin);
|
|
603
|
+
return this;
|
|
604
|
+
}
|
|
605
|
+
/** Number of dispatched records not yet written to their transports. Bounded by the `queue` option. */
|
|
606
|
+
get queueSize() {
|
|
607
|
+
return this.dispatchQueue.size;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Waits for every record dispatched so far to reach its transports'
|
|
611
|
+
* `write()` (and any plugin `afterLog` hooks). Note this does *not* force
|
|
612
|
+
* a batching transport (SQL, a queue, `HTTPTransport`, ...) to send a
|
|
613
|
+
* batch still under its own `maxRecords`/`maxBytes` threshold early — it
|
|
614
|
+
* only guarantees the record has been handed to that transport, the same
|
|
615
|
+
* contract `write()` always had. Before a process may pause or exit
|
|
616
|
+
* (a serverless freeze, a shutdown signal), prefer `withLambda`/
|
|
617
|
+
* `installShutdownHandlers`, which additionally force every batching
|
|
618
|
+
* transport to send its current buffer regardless of threshold.
|
|
619
|
+
*/
|
|
620
|
+
async flush() {
|
|
621
|
+
await this.dispatchQueue.flush();
|
|
622
|
+
}
|
|
623
|
+
/** Flush every pending record, then close every attached transport. Call once, on shutdown. */
|
|
624
|
+
async close() {
|
|
625
|
+
await this.flush();
|
|
626
|
+
for (const transport of this.transports) {
|
|
627
|
+
transport.close();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
/** A logger scoped under this one, inheriting its level, transports, plugins, and dispatch queue. */
|
|
631
|
+
child(name, meta = {}) {
|
|
632
|
+
const child = new _Logger(`${this.name}.${name}`, {
|
|
633
|
+
level: this.currentLevel,
|
|
634
|
+
transports: this.transports,
|
|
635
|
+
plugins: this.plugins,
|
|
636
|
+
meta: { ...this.baseMeta, ...meta }
|
|
637
|
+
});
|
|
638
|
+
child.dispatchQueue = this.dispatchQueue;
|
|
639
|
+
return child;
|
|
640
|
+
}
|
|
641
|
+
notifyError(plugin, error, record) {
|
|
642
|
+
try {
|
|
643
|
+
plugin.onError?.(error, record);
|
|
644
|
+
} catch {
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
dispatch(level, message, meta) {
|
|
648
|
+
if (level < this.currentLevel) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
let record = createRecord({
|
|
652
|
+
level,
|
|
653
|
+
logger: this.name,
|
|
654
|
+
message,
|
|
655
|
+
meta: { ...this.baseMeta, ...currentContext(), ...withStackFromErr(meta) }
|
|
656
|
+
});
|
|
657
|
+
const parentSpanId = currentSpanId();
|
|
658
|
+
if (parentSpanId !== void 0) {
|
|
659
|
+
record.meta.parentSpanId ??= parentSpanId;
|
|
660
|
+
}
|
|
661
|
+
for (const plugin of this.plugins) {
|
|
662
|
+
let result;
|
|
663
|
+
try {
|
|
664
|
+
result = plugin.beforeLog ? plugin.beforeLog(record) : record;
|
|
665
|
+
} catch (error) {
|
|
666
|
+
this.notifyError(plugin, error, record);
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
if (result === null) {
|
|
670
|
+
return null;
|
|
671
|
+
}
|
|
672
|
+
record = result;
|
|
673
|
+
}
|
|
674
|
+
this.dispatchQueue.enqueue(() => {
|
|
675
|
+
this.writeAndNotify(record);
|
|
676
|
+
});
|
|
677
|
+
return record;
|
|
678
|
+
}
|
|
679
|
+
writeAndNotify(record) {
|
|
680
|
+
for (const transport of this.transports) {
|
|
681
|
+
try {
|
|
682
|
+
transport.write(transport.format(record), record);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
console.error(`${transport.constructor.name}: failed to write a log record`, error);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const plugin of this.plugins) {
|
|
688
|
+
try {
|
|
689
|
+
plugin.afterLog?.(record);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
this.notifyError(plugin, error, record);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/** Logs at `TRACE` — the lowest level, for fine-grained diagnostic detail. Returns the record, or `null` if filtered/dropped. */
|
|
696
|
+
trace(message, meta = {}) {
|
|
697
|
+
return this.dispatch(5 /* TRACE */, message, meta);
|
|
698
|
+
}
|
|
699
|
+
/** Logs at `DEBUG`. Returns the record, or `null` if filtered/dropped. */
|
|
700
|
+
debug(message, meta = {}) {
|
|
701
|
+
return this.dispatch(10 /* DEBUG */, message, meta);
|
|
702
|
+
}
|
|
703
|
+
/** Logs at `INFO`. Returns the record, or `null` if filtered/dropped. */
|
|
704
|
+
info(message, meta = {}) {
|
|
705
|
+
return this.dispatch(20 /* INFO */, message, meta);
|
|
706
|
+
}
|
|
707
|
+
/** Logs at `WARN`. Returns the record, or `null` if filtered/dropped. */
|
|
708
|
+
warn(message, meta = {}) {
|
|
709
|
+
return this.dispatch(30 /* WARN */, message, meta);
|
|
710
|
+
}
|
|
711
|
+
/** Logs at `ERROR`. Returns the record, or `null` if filtered/dropped. */
|
|
712
|
+
error(message, meta = {}) {
|
|
713
|
+
return this.dispatch(40 /* ERROR */, message, meta);
|
|
714
|
+
}
|
|
715
|
+
/** Logs at `FATAL` — the highest level, for errors that precede an unrecoverable failure. Returns the record, or `null` if filtered/dropped. */
|
|
716
|
+
fatal(message, meta = {}) {
|
|
717
|
+
return this.dispatch(50 /* FATAL */, message, meta);
|
|
718
|
+
}
|
|
719
|
+
/** `.info()` tagged `meta.kind = "thought"` — an agent's internal reasoning step, for harness/agentic tracing. */
|
|
720
|
+
thought(message, meta = {}) {
|
|
721
|
+
return this.dispatch(20 /* INFO */, message, { kind: "thought", ...meta });
|
|
722
|
+
}
|
|
723
|
+
/** `.info()` tagged `meta.kind = "action"` — an agent taking an action (a tool call, an LLM request), for harness/agentic tracing. */
|
|
724
|
+
action(message, meta = {}) {
|
|
725
|
+
return this.dispatch(20 /* INFO */, message, { kind: "action", ...meta });
|
|
726
|
+
}
|
|
727
|
+
/** `.info()` tagged `meta.kind = "observation"` — the result an agent observed from an action, for harness/agentic tracing. */
|
|
728
|
+
observation(message, meta = {}) {
|
|
729
|
+
return this.dispatch(20 /* INFO */, message, { kind: "observation", ...meta });
|
|
730
|
+
}
|
|
731
|
+
/** `.info()` tagged `meta.kind = "decision"` — an agent's concluding decision for a step or run, for harness/agentic tracing. */
|
|
732
|
+
decision(message, meta = {}) {
|
|
733
|
+
return this.dispatch(20 /* INFO */, message, { kind: "decision", ...meta });
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* `await logger.span("callLlm", async () => {...})` — runs `fn`, and on
|
|
737
|
+
* settling (success or throw) emits one record for the span itself
|
|
738
|
+
* carrying `meta.spanId` and `meta.durationMs`. Every record logged
|
|
739
|
+
* inside `fn` — through any method, and through any further `await` —
|
|
740
|
+
* is automatically stamped with `meta.parentSpanId` pointing at this
|
|
741
|
+
* span, so nested/sub-agent calls reconstruct their exact nesting when
|
|
742
|
+
* sorted by `spanId`/`parentSpanId`.
|
|
743
|
+
*
|
|
744
|
+
* Still emits its record — at `ERROR`, with `meta.error` set — if `fn`
|
|
745
|
+
* throws; the error itself propagates unchanged to the caller.
|
|
746
|
+
*
|
|
747
|
+
* `spanId`/`parentSpanId` normally auto-generate/auto-nest; pass them in
|
|
748
|
+
* `options` to adopt an id handed in from elsewhere (e.g. a framework
|
|
749
|
+
* adapter translating an id it already received).
|
|
750
|
+
*/
|
|
751
|
+
async span(name, fn, options = {}) {
|
|
752
|
+
const { spanId: explicitSpanId, parentSpanId: explicitParentSpanId, ...meta } = options;
|
|
753
|
+
const spanId = explicitSpanId ?? newSpanId();
|
|
754
|
+
const start = performance.now();
|
|
755
|
+
try {
|
|
756
|
+
const result = await runInSpan(spanId, () => fn());
|
|
757
|
+
this.finishSpan(name, spanId, explicitParentSpanId, performance.now() - start, meta);
|
|
758
|
+
return result;
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.finishSpan(name, spanId, explicitParentSpanId, performance.now() - start, meta, error);
|
|
761
|
+
throw error;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
finishSpan(name, spanId, explicitParentSpanId, durationMs, meta, error) {
|
|
765
|
+
const fullMeta = {
|
|
766
|
+
spanId,
|
|
767
|
+
durationMs: Math.round(durationMs * 1e3) / 1e3,
|
|
768
|
+
...meta
|
|
769
|
+
};
|
|
770
|
+
if (explicitParentSpanId !== void 0) {
|
|
771
|
+
fullMeta.parentSpanId = explicitParentSpanId;
|
|
772
|
+
}
|
|
773
|
+
fullMeta.kind ??= "span";
|
|
774
|
+
if (error !== void 0) {
|
|
775
|
+
fullMeta.error = formatSpanError(error);
|
|
776
|
+
}
|
|
777
|
+
this.dispatch(error !== void 0 ? 40 /* ERROR */ : 20 /* INFO */, name, fullMeta);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
export { BeaconTransport, CollectingTransport, ConsoleTransport, ContextPlugin, DEFAULT_PII_PATTERNS, DEFAULT_REDACTED_KEYS, FunctionPlugin, JSONFormatter, Level, Logger, PIIRedactPlugin, RedactPlugin, SamplingPlugin, Transport, createRecord, hasFlush, levelName, parseLevel, utcTimestamp };
|
|
782
|
+
//# sourceMappingURL=browser.mjs.map
|
|
783
|
+
//# sourceMappingURL=browser.mjs.map
|