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.
@@ -0,0 +1,299 @@
1
+ /** Log levels, shared by name and numeric weight with the logquill-python contract. */
2
+ declare enum Level {
3
+ /** Finest-grained diagnostic detail. */
4
+ TRACE = 5,
5
+ /** Diagnostic detail useful in development. */
6
+ DEBUG = 10,
7
+ /** Notable events during normal operation. */
8
+ INFO = 20,
9
+ /** Something unexpected, but not (yet) an error. */
10
+ WARN = 30,
11
+ /** An error that didn't necessarily stop the operation. */
12
+ ERROR = 40,
13
+ /** An error that precedes an unrecoverable failure. */
14
+ FATAL = 50
15
+ }
16
+ /** A level given as a `Level`, a level name (any case), or its numeric weight. */
17
+ type LevelInput = Level | number | string;
18
+ /** The level's name, e.g. `levelName(Level.INFO) === "INFO"`. */
19
+ declare function levelName(level: Level): string;
20
+ /** Normalize a level given as a `Level`, level name, or numeric weight. Throws if unknown. */
21
+ declare function parseLevel(level: LevelInput): Level;
22
+
23
+ /** The cross-language record shape shared with logquill-python. */
24
+ interface LogRecord {
25
+ /** ISO8601 UTC timestamp with millisecond precision. */
26
+ timestamp: string;
27
+ /** Level name, e.g. `"INFO"`. */
28
+ level: string;
29
+ /** Name of the `Logger` that created this record. */
30
+ logger: string;
31
+ /** The log message. */
32
+ message: string;
33
+ /** Structured payload — always present, even if empty. */
34
+ meta: Record<string, unknown>;
35
+ }
36
+ /** ISO8601 UTC timestamp with millisecond precision, matching Python's `utc_timestamp()`. */
37
+ declare function utcTimestamp(): string;
38
+ /** Builds a `LogRecord` with the current UTC timestamp. Used internally by `Logger`; exported for transports/plugins that need to construct a record directly. */
39
+ declare function createRecord(params: {
40
+ level: Level;
41
+ logger: string;
42
+ message: string;
43
+ meta: Record<string, unknown>;
44
+ }): LogRecord;
45
+
46
+ /** `format(record) -> string`, per the transport contract shared with logquill-python. */
47
+ interface Formatter {
48
+ /** Turns a `LogRecord` into the string a `Transport` writes. */
49
+ format(record: LogRecord): string;
50
+ }
51
+ /** Serializes a record to the canonical JSON line shape. */
52
+ declare class JSONFormatter implements Formatter {
53
+ /** Returns `JSON.stringify(record)`. */
54
+ format(record: LogRecord): string;
55
+ }
56
+
57
+ /**
58
+ * The plugin pipeline hooks, matching the Python hook names: `beforeLog`,
59
+ * `afterLog`, `onError`. All hooks are optional — implement only what you need.
60
+ * A hook that throws cannot crash logging: the pipeline catches it, routes it
61
+ * to `onError`, and moves on.
62
+ */
63
+ interface Plugin {
64
+ /** Return a (possibly modified) record, or `null` to drop it. */
65
+ beforeLog?(record: LogRecord): LogRecord | null;
66
+ /** Called after the record has been dispatched to every transport. */
67
+ afterLog?(record: LogRecord): void;
68
+ /** Called when one of this plugin's own hooks throws. */
69
+ onError?(error: unknown, record: LogRecord): void;
70
+ }
71
+ /** A plain `beforeLog`-style function, as accepted by `Logger.use()` in place of a `Plugin`. */
72
+ type MiddlewareFunc = (record: LogRecord) => LogRecord | null;
73
+ /**
74
+ * Wraps a plain `beforeLog`-style function as a `Plugin`. `Logger.use()`
75
+ * builds one of these automatically when given a function instead of a
76
+ * `Plugin` — Express/Koa-style middleware ergonomics, without needing to
77
+ * read the `Plugin` interface first. There's no `next()` chaining: the
78
+ * pipeline already calls hooks in sequence, so this is sugar for a
79
+ * single-method `Plugin`, not a new execution model.
80
+ */
81
+ declare class FunctionPlugin implements Plugin {
82
+ private readonly func;
83
+ constructor(func: MiddlewareFunc);
84
+ beforeLog(record: LogRecord): LogRecord | null;
85
+ }
86
+
87
+ /** How a full queue behaves when another task arrives. */
88
+ type BackpressurePolicy = "dropOldest" | "dropNewest" | "block";
89
+ /** Options for the {@link DispatchQueue} constructor. */
90
+ interface DispatchQueueOptions {
91
+ /** Maximum number of pending tasks the queue holds at once. Default 10_000. */
92
+ maxSize?: number;
93
+ /**
94
+ * What happens when `enqueue()` is called while the queue is already at
95
+ * `maxSize`:
96
+ * - `"dropOldest"` (default) — evict the longest-waiting task (it never
97
+ * runs) and enqueue the new one. Favors recent records.
98
+ * - `"dropNewest"` — discard the incoming task; everything already queued
99
+ * is left alone. Favors records already in flight.
100
+ * - `"block"` — run the task synchronously, right now, on the caller's
101
+ * stack instead of queueing it. Nothing is ever dropped, at the cost of
102
+ * the caller (e.g. `logger.info()`) taking as long as the write itself —
103
+ * real backpressure rather than a silent drop.
104
+ */
105
+ policy?: BackpressurePolicy;
106
+ /**
107
+ * Called at most once per `warnIntervalMs` (default 5000) when the queue
108
+ * drops tasks, with the number dropped since the last call — a
109
+ * rate-limited way to surface sustained overload without flooding the
110
+ * caller's own logs with one warning per drop. Defaults to `console.warn`.
111
+ */
112
+ onDrop?: (droppedSinceLastWarning: number, policy: BackpressurePolicy) => void;
113
+ /** Minimum gap between `onDrop` calls. Default 5000ms. */
114
+ warnIntervalMs?: number;
115
+ }
116
+ /** A unit of deferred work queued via `DispatchQueue.enqueue()`. */
117
+ type Task = () => void | Promise<void>;
118
+ /**
119
+ * A bounded, in-order queue of pending write tasks, drained outside the
120
+ * caller's own call stack (`setImmediate`/microtask) so `Logger` methods can
121
+ * return before the I/O they triggered actually runs. Backed by an explicit
122
+ * size cap and backpressure policy — see `DispatchQueueOptions` — so a
123
+ * sustained burst can never grow memory unboundedly.
124
+ */
125
+ declare class DispatchQueue {
126
+ /** Maximum number of pending tasks held at once, as configured via `DispatchQueueOptions`. */
127
+ readonly maxSize: number;
128
+ /** Backpressure policy applied once `maxSize` is reached, as configured via `DispatchQueueOptions`. */
129
+ readonly policy: BackpressurePolicy;
130
+ private readonly onDrop;
131
+ private readonly warnIntervalMs;
132
+ private readonly tasks;
133
+ private draining;
134
+ private scheduled;
135
+ private idleWaiters;
136
+ private droppedSinceWarning;
137
+ private lastWarnAt;
138
+ constructor(options?: DispatchQueueOptions);
139
+ /** Number of tasks currently waiting to run. Bounded by `maxSize`. */
140
+ get size(): number;
141
+ /**
142
+ * Queue `task` to run outside the current call stack, applying the
143
+ * configured backpressure policy if the queue is already full. Under
144
+ * `"block"`, `task` may run synchronously before this call returns.
145
+ */
146
+ enqueue(task: Task): void;
147
+ /** Resolves once every task queued so far has run. Safe to call when idle. */
148
+ flush(): Promise<void>;
149
+ private recordDrop;
150
+ private runInline;
151
+ private schedule;
152
+ private drainAll;
153
+ }
154
+
155
+ /**
156
+ * Sink for log records, per the cross-language transport contract:
157
+ * `format(record) -> string`, `write(formatted, record)`, `close()` on shutdown.
158
+ */
159
+ declare abstract class Transport {
160
+ /** Turns a `LogRecord` into the string this transport actually writes. Defaults to `JSONFormatter`. */
161
+ formatter: Formatter;
162
+ constructor(formatter?: Formatter);
163
+ /** Formats `record` via `this.formatter`. Called once per record before `write()`. */
164
+ format(record: LogRecord): string;
165
+ /** Sends the already-formatted record to this transport's sink. */
166
+ abstract write(formatted: string, record: LogRecord): void;
167
+ /** Flush/release resources on shutdown. No-op unless a transport overrides it. */
168
+ close(): void;
169
+ }
170
+ /** Duck-typed: a transport (typically a `BatchingTransport`) whose `flush()` sends its current buffer now, without closing. */
171
+ interface FlushableTransport {
172
+ /** Sends the current buffer now, even if it hasn't reached its own flush threshold. */
173
+ flush(): void | Promise<void>;
174
+ }
175
+ /** Type guard for {@link FlushableTransport} — true for any transport (e.g. every `BatchingTransport`) that exposes a `flush()` method. */
176
+ declare function hasFlush(transport: Transport): transport is Transport & FlushableTransport;
177
+ /** In-memory transport for tests: collects every (formatted, record) pair written to it. */
178
+ declare class CollectingTransport extends Transport {
179
+ /** Every formatted string passed to `write()`, in call order. */
180
+ readonly formatted: string[];
181
+ /** Every raw `LogRecord` passed to `write()`, in call order. */
182
+ readonly records: LogRecord[];
183
+ /** Set once `close()` has been called. */
184
+ closed: boolean;
185
+ write(formatted: string, record: LogRecord): void;
186
+ close(): void;
187
+ }
188
+
189
+ /** Options for `Logger.span()`. Any extra keys become the span's own `meta`. */
190
+ interface SpanOptions extends Record<string, unknown> {
191
+ /** Adopt an id handed in from elsewhere (e.g. a framework's own run id) instead of generating one. */
192
+ spanId?: string;
193
+ /** Adopt a parent id explicitly, overriding auto-nesting from an enclosing `span()` block. */
194
+ parentSpanId?: string;
195
+ }
196
+ /** Options for the {@link Logger} constructor. */
197
+ interface LoggerOptions {
198
+ /** Minimum level that reaches a transport; records below it are dropped before any plugin runs. Default `INFO`. */
199
+ level?: LevelInput;
200
+ /** Sinks every record that passes the level filter and plugin pipeline is written to. */
201
+ transports?: Transport[];
202
+ /** Registered via `.use()` in order — a plain function is wrapped as an anonymous `Plugin`. */
203
+ plugins?: (Plugin | MiddlewareFunc)[];
204
+ /** Merged into every record's `meta`, before a call-site `meta` value (which always wins on collision). */
205
+ meta?: Record<string, unknown>;
206
+ /** Bounds and backpressure policy for the internal async dispatch queue. See `DispatchQueueOptions`. */
207
+ queue?: DispatchQueueOptions;
208
+ }
209
+ /**
210
+ * The core logger: leveled, structured logging over a pluggable transport
211
+ * and plugin pipeline. Every log call returns the finished `LogRecord` (or
212
+ * `null` if it was filtered by level or dropped by a plugin) synchronously —
213
+ * the actual transport writes are dispatched onto an internal queue so the
214
+ * call returns before any I/O runs; see `flush()`/`close()`.
215
+ */
216
+ declare class Logger {
217
+ /** This logger's name, as passed to the constructor (or derived via `.child()`). Appears on every record as `logger`. */
218
+ readonly name: string;
219
+ /** Every transport a written record is sent to. */
220
+ readonly transports: Transport[];
221
+ /** Every plugin registered via `.use()`, in registration order. */
222
+ readonly plugins: Plugin[];
223
+ private currentLevel;
224
+ private readonly baseMeta;
225
+ private dispatchQueue;
226
+ constructor(name: string, options?: LoggerOptions);
227
+ /** This logger's current minimum level — records below it are filtered before any plugin runs. */
228
+ get level(): Level;
229
+ /** Changes the minimum level records must meet to reach a transport. Accepts a `Level`, its numeric weight, or its name. */
230
+ setLevel(level: LevelInput): void;
231
+ /**
232
+ * Register a plugin, or a plain `beforeLog`-style function. A function is
233
+ * wrapped internally as an anonymous `Plugin` (`FunctionPlugin`) — the
234
+ * same middleware ergonomics as Express/Koa, without needing to read the
235
+ * `Plugin` interface first. Returns `this` so calls can be chained.
236
+ */
237
+ use(plugin: Plugin | MiddlewareFunc): this;
238
+ /** Number of dispatched records not yet written to their transports. Bounded by the `queue` option. */
239
+ get queueSize(): number;
240
+ /**
241
+ * Waits for every record dispatched so far to reach its transports'
242
+ * `write()` (and any plugin `afterLog` hooks). Note this does *not* force
243
+ * a batching transport (SQL, a queue, `HTTPTransport`, ...) to send a
244
+ * batch still under its own `maxRecords`/`maxBytes` threshold early — it
245
+ * only guarantees the record has been handed to that transport, the same
246
+ * contract `write()` always had. Before a process may pause or exit
247
+ * (a serverless freeze, a shutdown signal), prefer `withLambda`/
248
+ * `installShutdownHandlers`, which additionally force every batching
249
+ * transport to send its current buffer regardless of threshold.
250
+ */
251
+ flush(): Promise<void>;
252
+ /** Flush every pending record, then close every attached transport. Call once, on shutdown. */
253
+ close(): Promise<void>;
254
+ /** A logger scoped under this one, inheriting its level, transports, plugins, and dispatch queue. */
255
+ child(name: string, meta?: Record<string, unknown>): Logger;
256
+ private notifyError;
257
+ private dispatch;
258
+ private writeAndNotify;
259
+ /** Logs at `TRACE` — the lowest level, for fine-grained diagnostic detail. Returns the record, or `null` if filtered/dropped. */
260
+ trace(message: string, meta?: Record<string, unknown>): LogRecord | null;
261
+ /** Logs at `DEBUG`. Returns the record, or `null` if filtered/dropped. */
262
+ debug(message: string, meta?: Record<string, unknown>): LogRecord | null;
263
+ /** Logs at `INFO`. Returns the record, or `null` if filtered/dropped. */
264
+ info(message: string, meta?: Record<string, unknown>): LogRecord | null;
265
+ /** Logs at `WARN`. Returns the record, or `null` if filtered/dropped. */
266
+ warn(message: string, meta?: Record<string, unknown>): LogRecord | null;
267
+ /** Logs at `ERROR`. Returns the record, or `null` if filtered/dropped. */
268
+ error(message: string, meta?: Record<string, unknown>): LogRecord | null;
269
+ /** Logs at `FATAL` — the highest level, for errors that precede an unrecoverable failure. Returns the record, or `null` if filtered/dropped. */
270
+ fatal(message: string, meta?: Record<string, unknown>): LogRecord | null;
271
+ /** `.info()` tagged `meta.kind = "thought"` — an agent's internal reasoning step, for harness/agentic tracing. */
272
+ thought(message: string, meta?: Record<string, unknown>): LogRecord | null;
273
+ /** `.info()` tagged `meta.kind = "action"` — an agent taking an action (a tool call, an LLM request), for harness/agentic tracing. */
274
+ action(message: string, meta?: Record<string, unknown>): LogRecord | null;
275
+ /** `.info()` tagged `meta.kind = "observation"` — the result an agent observed from an action, for harness/agentic tracing. */
276
+ observation(message: string, meta?: Record<string, unknown>): LogRecord | null;
277
+ /** `.info()` tagged `meta.kind = "decision"` — an agent's concluding decision for a step or run, for harness/agentic tracing. */
278
+ decision(message: string, meta?: Record<string, unknown>): LogRecord | null;
279
+ /**
280
+ * `await logger.span("callLlm", async () => {...})` — runs `fn`, and on
281
+ * settling (success or throw) emits one record for the span itself
282
+ * carrying `meta.spanId` and `meta.durationMs`. Every record logged
283
+ * inside `fn` — through any method, and through any further `await` —
284
+ * is automatically stamped with `meta.parentSpanId` pointing at this
285
+ * span, so nested/sub-agent calls reconstruct their exact nesting when
286
+ * sorted by `spanId`/`parentSpanId`.
287
+ *
288
+ * Still emits its record — at `ERROR`, with `meta.error` set — if `fn`
289
+ * throws; the error itself propagates unchanged to the caller.
290
+ *
291
+ * `spanId`/`parentSpanId` normally auto-generate/auto-nest; pass them in
292
+ * `options` to adopt an id handed in from elsewhere (e.g. a framework
293
+ * adapter translating an id it already received).
294
+ */
295
+ span<T>(name: string, fn: () => T | Promise<T>, options?: SpanOptions): Promise<T>;
296
+ private finishSpan;
297
+ }
298
+
299
+ export { type BackpressurePolicy as B, CollectingTransport as C, DispatchQueue as D, type Formatter as F, JSONFormatter as J, type LogRecord as L, type MiddlewareFunc as M, type Plugin as P, type SpanOptions as S, Transport as T, Level as a, Logger as b, type LevelInput as c, type DispatchQueueOptions as d, type FlushableTransport as e, FunctionPlugin as f, type LoggerOptions as g, type Task as h, createRecord as i, hasFlush as j, levelName as l, parseLevel as p, utcTimestamp as u };
@@ -0,0 +1,299 @@
1
+ /** Log levels, shared by name and numeric weight with the logquill-python contract. */
2
+ declare enum Level {
3
+ /** Finest-grained diagnostic detail. */
4
+ TRACE = 5,
5
+ /** Diagnostic detail useful in development. */
6
+ DEBUG = 10,
7
+ /** Notable events during normal operation. */
8
+ INFO = 20,
9
+ /** Something unexpected, but not (yet) an error. */
10
+ WARN = 30,
11
+ /** An error that didn't necessarily stop the operation. */
12
+ ERROR = 40,
13
+ /** An error that precedes an unrecoverable failure. */
14
+ FATAL = 50
15
+ }
16
+ /** A level given as a `Level`, a level name (any case), or its numeric weight. */
17
+ type LevelInput = Level | number | string;
18
+ /** The level's name, e.g. `levelName(Level.INFO) === "INFO"`. */
19
+ declare function levelName(level: Level): string;
20
+ /** Normalize a level given as a `Level`, level name, or numeric weight. Throws if unknown. */
21
+ declare function parseLevel(level: LevelInput): Level;
22
+
23
+ /** The cross-language record shape shared with logquill-python. */
24
+ interface LogRecord {
25
+ /** ISO8601 UTC timestamp with millisecond precision. */
26
+ timestamp: string;
27
+ /** Level name, e.g. `"INFO"`. */
28
+ level: string;
29
+ /** Name of the `Logger` that created this record. */
30
+ logger: string;
31
+ /** The log message. */
32
+ message: string;
33
+ /** Structured payload — always present, even if empty. */
34
+ meta: Record<string, unknown>;
35
+ }
36
+ /** ISO8601 UTC timestamp with millisecond precision, matching Python's `utc_timestamp()`. */
37
+ declare function utcTimestamp(): string;
38
+ /** Builds a `LogRecord` with the current UTC timestamp. Used internally by `Logger`; exported for transports/plugins that need to construct a record directly. */
39
+ declare function createRecord(params: {
40
+ level: Level;
41
+ logger: string;
42
+ message: string;
43
+ meta: Record<string, unknown>;
44
+ }): LogRecord;
45
+
46
+ /** `format(record) -> string`, per the transport contract shared with logquill-python. */
47
+ interface Formatter {
48
+ /** Turns a `LogRecord` into the string a `Transport` writes. */
49
+ format(record: LogRecord): string;
50
+ }
51
+ /** Serializes a record to the canonical JSON line shape. */
52
+ declare class JSONFormatter implements Formatter {
53
+ /** Returns `JSON.stringify(record)`. */
54
+ format(record: LogRecord): string;
55
+ }
56
+
57
+ /**
58
+ * The plugin pipeline hooks, matching the Python hook names: `beforeLog`,
59
+ * `afterLog`, `onError`. All hooks are optional — implement only what you need.
60
+ * A hook that throws cannot crash logging: the pipeline catches it, routes it
61
+ * to `onError`, and moves on.
62
+ */
63
+ interface Plugin {
64
+ /** Return a (possibly modified) record, or `null` to drop it. */
65
+ beforeLog?(record: LogRecord): LogRecord | null;
66
+ /** Called after the record has been dispatched to every transport. */
67
+ afterLog?(record: LogRecord): void;
68
+ /** Called when one of this plugin's own hooks throws. */
69
+ onError?(error: unknown, record: LogRecord): void;
70
+ }
71
+ /** A plain `beforeLog`-style function, as accepted by `Logger.use()` in place of a `Plugin`. */
72
+ type MiddlewareFunc = (record: LogRecord) => LogRecord | null;
73
+ /**
74
+ * Wraps a plain `beforeLog`-style function as a `Plugin`. `Logger.use()`
75
+ * builds one of these automatically when given a function instead of a
76
+ * `Plugin` — Express/Koa-style middleware ergonomics, without needing to
77
+ * read the `Plugin` interface first. There's no `next()` chaining: the
78
+ * pipeline already calls hooks in sequence, so this is sugar for a
79
+ * single-method `Plugin`, not a new execution model.
80
+ */
81
+ declare class FunctionPlugin implements Plugin {
82
+ private readonly func;
83
+ constructor(func: MiddlewareFunc);
84
+ beforeLog(record: LogRecord): LogRecord | null;
85
+ }
86
+
87
+ /** How a full queue behaves when another task arrives. */
88
+ type BackpressurePolicy = "dropOldest" | "dropNewest" | "block";
89
+ /** Options for the {@link DispatchQueue} constructor. */
90
+ interface DispatchQueueOptions {
91
+ /** Maximum number of pending tasks the queue holds at once. Default 10_000. */
92
+ maxSize?: number;
93
+ /**
94
+ * What happens when `enqueue()` is called while the queue is already at
95
+ * `maxSize`:
96
+ * - `"dropOldest"` (default) — evict the longest-waiting task (it never
97
+ * runs) and enqueue the new one. Favors recent records.
98
+ * - `"dropNewest"` — discard the incoming task; everything already queued
99
+ * is left alone. Favors records already in flight.
100
+ * - `"block"` — run the task synchronously, right now, on the caller's
101
+ * stack instead of queueing it. Nothing is ever dropped, at the cost of
102
+ * the caller (e.g. `logger.info()`) taking as long as the write itself —
103
+ * real backpressure rather than a silent drop.
104
+ */
105
+ policy?: BackpressurePolicy;
106
+ /**
107
+ * Called at most once per `warnIntervalMs` (default 5000) when the queue
108
+ * drops tasks, with the number dropped since the last call — a
109
+ * rate-limited way to surface sustained overload without flooding the
110
+ * caller's own logs with one warning per drop. Defaults to `console.warn`.
111
+ */
112
+ onDrop?: (droppedSinceLastWarning: number, policy: BackpressurePolicy) => void;
113
+ /** Minimum gap between `onDrop` calls. Default 5000ms. */
114
+ warnIntervalMs?: number;
115
+ }
116
+ /** A unit of deferred work queued via `DispatchQueue.enqueue()`. */
117
+ type Task = () => void | Promise<void>;
118
+ /**
119
+ * A bounded, in-order queue of pending write tasks, drained outside the
120
+ * caller's own call stack (`setImmediate`/microtask) so `Logger` methods can
121
+ * return before the I/O they triggered actually runs. Backed by an explicit
122
+ * size cap and backpressure policy — see `DispatchQueueOptions` — so a
123
+ * sustained burst can never grow memory unboundedly.
124
+ */
125
+ declare class DispatchQueue {
126
+ /** Maximum number of pending tasks held at once, as configured via `DispatchQueueOptions`. */
127
+ readonly maxSize: number;
128
+ /** Backpressure policy applied once `maxSize` is reached, as configured via `DispatchQueueOptions`. */
129
+ readonly policy: BackpressurePolicy;
130
+ private readonly onDrop;
131
+ private readonly warnIntervalMs;
132
+ private readonly tasks;
133
+ private draining;
134
+ private scheduled;
135
+ private idleWaiters;
136
+ private droppedSinceWarning;
137
+ private lastWarnAt;
138
+ constructor(options?: DispatchQueueOptions);
139
+ /** Number of tasks currently waiting to run. Bounded by `maxSize`. */
140
+ get size(): number;
141
+ /**
142
+ * Queue `task` to run outside the current call stack, applying the
143
+ * configured backpressure policy if the queue is already full. Under
144
+ * `"block"`, `task` may run synchronously before this call returns.
145
+ */
146
+ enqueue(task: Task): void;
147
+ /** Resolves once every task queued so far has run. Safe to call when idle. */
148
+ flush(): Promise<void>;
149
+ private recordDrop;
150
+ private runInline;
151
+ private schedule;
152
+ private drainAll;
153
+ }
154
+
155
+ /**
156
+ * Sink for log records, per the cross-language transport contract:
157
+ * `format(record) -> string`, `write(formatted, record)`, `close()` on shutdown.
158
+ */
159
+ declare abstract class Transport {
160
+ /** Turns a `LogRecord` into the string this transport actually writes. Defaults to `JSONFormatter`. */
161
+ formatter: Formatter;
162
+ constructor(formatter?: Formatter);
163
+ /** Formats `record` via `this.formatter`. Called once per record before `write()`. */
164
+ format(record: LogRecord): string;
165
+ /** Sends the already-formatted record to this transport's sink. */
166
+ abstract write(formatted: string, record: LogRecord): void;
167
+ /** Flush/release resources on shutdown. No-op unless a transport overrides it. */
168
+ close(): void;
169
+ }
170
+ /** Duck-typed: a transport (typically a `BatchingTransport`) whose `flush()` sends its current buffer now, without closing. */
171
+ interface FlushableTransport {
172
+ /** Sends the current buffer now, even if it hasn't reached its own flush threshold. */
173
+ flush(): void | Promise<void>;
174
+ }
175
+ /** Type guard for {@link FlushableTransport} — true for any transport (e.g. every `BatchingTransport`) that exposes a `flush()` method. */
176
+ declare function hasFlush(transport: Transport): transport is Transport & FlushableTransport;
177
+ /** In-memory transport for tests: collects every (formatted, record) pair written to it. */
178
+ declare class CollectingTransport extends Transport {
179
+ /** Every formatted string passed to `write()`, in call order. */
180
+ readonly formatted: string[];
181
+ /** Every raw `LogRecord` passed to `write()`, in call order. */
182
+ readonly records: LogRecord[];
183
+ /** Set once `close()` has been called. */
184
+ closed: boolean;
185
+ write(formatted: string, record: LogRecord): void;
186
+ close(): void;
187
+ }
188
+
189
+ /** Options for `Logger.span()`. Any extra keys become the span's own `meta`. */
190
+ interface SpanOptions extends Record<string, unknown> {
191
+ /** Adopt an id handed in from elsewhere (e.g. a framework's own run id) instead of generating one. */
192
+ spanId?: string;
193
+ /** Adopt a parent id explicitly, overriding auto-nesting from an enclosing `span()` block. */
194
+ parentSpanId?: string;
195
+ }
196
+ /** Options for the {@link Logger} constructor. */
197
+ interface LoggerOptions {
198
+ /** Minimum level that reaches a transport; records below it are dropped before any plugin runs. Default `INFO`. */
199
+ level?: LevelInput;
200
+ /** Sinks every record that passes the level filter and plugin pipeline is written to. */
201
+ transports?: Transport[];
202
+ /** Registered via `.use()` in order — a plain function is wrapped as an anonymous `Plugin`. */
203
+ plugins?: (Plugin | MiddlewareFunc)[];
204
+ /** Merged into every record's `meta`, before a call-site `meta` value (which always wins on collision). */
205
+ meta?: Record<string, unknown>;
206
+ /** Bounds and backpressure policy for the internal async dispatch queue. See `DispatchQueueOptions`. */
207
+ queue?: DispatchQueueOptions;
208
+ }
209
+ /**
210
+ * The core logger: leveled, structured logging over a pluggable transport
211
+ * and plugin pipeline. Every log call returns the finished `LogRecord` (or
212
+ * `null` if it was filtered by level or dropped by a plugin) synchronously —
213
+ * the actual transport writes are dispatched onto an internal queue so the
214
+ * call returns before any I/O runs; see `flush()`/`close()`.
215
+ */
216
+ declare class Logger {
217
+ /** This logger's name, as passed to the constructor (or derived via `.child()`). Appears on every record as `logger`. */
218
+ readonly name: string;
219
+ /** Every transport a written record is sent to. */
220
+ readonly transports: Transport[];
221
+ /** Every plugin registered via `.use()`, in registration order. */
222
+ readonly plugins: Plugin[];
223
+ private currentLevel;
224
+ private readonly baseMeta;
225
+ private dispatchQueue;
226
+ constructor(name: string, options?: LoggerOptions);
227
+ /** This logger's current minimum level — records below it are filtered before any plugin runs. */
228
+ get level(): Level;
229
+ /** Changes the minimum level records must meet to reach a transport. Accepts a `Level`, its numeric weight, or its name. */
230
+ setLevel(level: LevelInput): void;
231
+ /**
232
+ * Register a plugin, or a plain `beforeLog`-style function. A function is
233
+ * wrapped internally as an anonymous `Plugin` (`FunctionPlugin`) — the
234
+ * same middleware ergonomics as Express/Koa, without needing to read the
235
+ * `Plugin` interface first. Returns `this` so calls can be chained.
236
+ */
237
+ use(plugin: Plugin | MiddlewareFunc): this;
238
+ /** Number of dispatched records not yet written to their transports. Bounded by the `queue` option. */
239
+ get queueSize(): number;
240
+ /**
241
+ * Waits for every record dispatched so far to reach its transports'
242
+ * `write()` (and any plugin `afterLog` hooks). Note this does *not* force
243
+ * a batching transport (SQL, a queue, `HTTPTransport`, ...) to send a
244
+ * batch still under its own `maxRecords`/`maxBytes` threshold early — it
245
+ * only guarantees the record has been handed to that transport, the same
246
+ * contract `write()` always had. Before a process may pause or exit
247
+ * (a serverless freeze, a shutdown signal), prefer `withLambda`/
248
+ * `installShutdownHandlers`, which additionally force every batching
249
+ * transport to send its current buffer regardless of threshold.
250
+ */
251
+ flush(): Promise<void>;
252
+ /** Flush every pending record, then close every attached transport. Call once, on shutdown. */
253
+ close(): Promise<void>;
254
+ /** A logger scoped under this one, inheriting its level, transports, plugins, and dispatch queue. */
255
+ child(name: string, meta?: Record<string, unknown>): Logger;
256
+ private notifyError;
257
+ private dispatch;
258
+ private writeAndNotify;
259
+ /** Logs at `TRACE` — the lowest level, for fine-grained diagnostic detail. Returns the record, or `null` if filtered/dropped. */
260
+ trace(message: string, meta?: Record<string, unknown>): LogRecord | null;
261
+ /** Logs at `DEBUG`. Returns the record, or `null` if filtered/dropped. */
262
+ debug(message: string, meta?: Record<string, unknown>): LogRecord | null;
263
+ /** Logs at `INFO`. Returns the record, or `null` if filtered/dropped. */
264
+ info(message: string, meta?: Record<string, unknown>): LogRecord | null;
265
+ /** Logs at `WARN`. Returns the record, or `null` if filtered/dropped. */
266
+ warn(message: string, meta?: Record<string, unknown>): LogRecord | null;
267
+ /** Logs at `ERROR`. Returns the record, or `null` if filtered/dropped. */
268
+ error(message: string, meta?: Record<string, unknown>): LogRecord | null;
269
+ /** Logs at `FATAL` — the highest level, for errors that precede an unrecoverable failure. Returns the record, or `null` if filtered/dropped. */
270
+ fatal(message: string, meta?: Record<string, unknown>): LogRecord | null;
271
+ /** `.info()` tagged `meta.kind = "thought"` — an agent's internal reasoning step, for harness/agentic tracing. */
272
+ thought(message: string, meta?: Record<string, unknown>): LogRecord | null;
273
+ /** `.info()` tagged `meta.kind = "action"` — an agent taking an action (a tool call, an LLM request), for harness/agentic tracing. */
274
+ action(message: string, meta?: Record<string, unknown>): LogRecord | null;
275
+ /** `.info()` tagged `meta.kind = "observation"` — the result an agent observed from an action, for harness/agentic tracing. */
276
+ observation(message: string, meta?: Record<string, unknown>): LogRecord | null;
277
+ /** `.info()` tagged `meta.kind = "decision"` — an agent's concluding decision for a step or run, for harness/agentic tracing. */
278
+ decision(message: string, meta?: Record<string, unknown>): LogRecord | null;
279
+ /**
280
+ * `await logger.span("callLlm", async () => {...})` — runs `fn`, and on
281
+ * settling (success or throw) emits one record for the span itself
282
+ * carrying `meta.spanId` and `meta.durationMs`. Every record logged
283
+ * inside `fn` — through any method, and through any further `await` —
284
+ * is automatically stamped with `meta.parentSpanId` pointing at this
285
+ * span, so nested/sub-agent calls reconstruct their exact nesting when
286
+ * sorted by `spanId`/`parentSpanId`.
287
+ *
288
+ * Still emits its record — at `ERROR`, with `meta.error` set — if `fn`
289
+ * throws; the error itself propagates unchanged to the caller.
290
+ *
291
+ * `spanId`/`parentSpanId` normally auto-generate/auto-nest; pass them in
292
+ * `options` to adopt an id handed in from elsewhere (e.g. a framework
293
+ * adapter translating an id it already received).
294
+ */
295
+ span<T>(name: string, fn: () => T | Promise<T>, options?: SpanOptions): Promise<T>;
296
+ private finishSpan;
297
+ }
298
+
299
+ export { type BackpressurePolicy as B, CollectingTransport as C, DispatchQueue as D, type Formatter as F, JSONFormatter as J, type LogRecord as L, type MiddlewareFunc as M, type Plugin as P, type SpanOptions as S, Transport as T, Level as a, Logger as b, type LevelInput as c, type DispatchQueueOptions as d, type FlushableTransport as e, FunctionPlugin as f, type LoggerOptions as g, type Task as h, createRecord as i, hasFlush as j, levelName as l, parseLevel as p, utcTimestamp as u };