logquill 0.1.2 → 0.3.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 +358 -8
- package/dist/index.cjs +1497 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1078 -13
- package/dist/index.d.ts +1078 -13
- package/dist/index.mjs +1470 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +57 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,21 @@ interface Plugin {
|
|
|
54
54
|
/** Called when one of this plugin's own hooks throws. */
|
|
55
55
|
onError?(error: unknown, record: LogRecord): void;
|
|
56
56
|
}
|
|
57
|
+
/** A plain `beforeLog`-style function, as accepted by `Logger.use()` in place of a `Plugin`. */
|
|
58
|
+
type MiddlewareFunc = (record: LogRecord) => LogRecord | null;
|
|
59
|
+
/**
|
|
60
|
+
* Wraps a plain `beforeLog`-style function as a `Plugin`. `Logger.use()`
|
|
61
|
+
* builds one of these automatically when given a function instead of a
|
|
62
|
+
* `Plugin` — Express/Koa-style middleware ergonomics, without needing to
|
|
63
|
+
* read the `Plugin` interface first. There's no `next()` chaining: the
|
|
64
|
+
* pipeline already calls hooks in sequence, so this is sugar for a
|
|
65
|
+
* single-method `Plugin`, not a new execution model.
|
|
66
|
+
*/
|
|
67
|
+
declare class FunctionPlugin implements Plugin {
|
|
68
|
+
private readonly func;
|
|
69
|
+
constructor(func: MiddlewareFunc);
|
|
70
|
+
beforeLog(record: LogRecord): LogRecord | null;
|
|
71
|
+
}
|
|
57
72
|
|
|
58
73
|
/**
|
|
59
74
|
* Injects fixed key/value pairs into every record's `meta`.
|
|
@@ -78,15 +93,44 @@ declare class RedactPlugin implements Plugin {
|
|
|
78
93
|
beforeLog(record: LogRecord): LogRecord;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Syntactic (not semantic) patterns — matched on shape, so both false
|
|
98
|
+
* positives (a random 9-digit number) and false negatives (anything that
|
|
99
|
+
* doesn't look like these shapes) are expected. Override via `patterns`
|
|
100
|
+
* for anything stricter.
|
|
101
|
+
*/
|
|
102
|
+
declare const DEFAULT_PII_PATTERNS: Readonly<Record<string, RegExp>>;
|
|
103
|
+
interface PIIRedactPluginOptions {
|
|
104
|
+
patterns?: Readonly<Record<string, RegExp>>;
|
|
105
|
+
replacement?: string;
|
|
83
106
|
}
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Regex-based PII redaction over `meta` **values**, not just keys.
|
|
109
|
+
*
|
|
110
|
+
* Complements `RedactPlugin`, which redacts by exact key match —
|
|
111
|
+
* `PIIRedactPlugin` scans string values (recursively through nested
|
|
112
|
+
* objects/arrays) for emails, SSNs, credit-card numbers, and phone
|
|
113
|
+
* numbers, and redacts matches wherever they appear, regardless of which
|
|
114
|
+
* key holds them (a `notes` field containing a stray SSN is still caught).
|
|
115
|
+
*
|
|
116
|
+
* Detection is pattern-based: fast and dependency-free, but it matches on
|
|
117
|
+
* syntactic shape, not meaning — a random 9-digit number can false-positive
|
|
118
|
+
* as an SSN, and anything that doesn't fit these shapes (a name, a street
|
|
119
|
+
* address) is a false negative. Pass your own `patterns` to extend or
|
|
120
|
+
* replace the defaults.
|
|
121
|
+
*
|
|
122
|
+
* Recursion into nested `meta` structures is depth- and cycle-bounded, so a
|
|
123
|
+
* circular reference or a pathologically deep structure can't hang or
|
|
124
|
+
* crash the caller — it's left unredacted past the bound rather than
|
|
125
|
+
* throwing.
|
|
126
|
+
*/
|
|
127
|
+
declare class PIIRedactPlugin implements Plugin {
|
|
128
|
+
readonly patterns: Readonly<Record<string, RegExp>>;
|
|
129
|
+
readonly replacement: string;
|
|
130
|
+
constructor(options?: PIIRedactPluginOptions);
|
|
131
|
+
beforeLog(record: LogRecord): LogRecord;
|
|
132
|
+
private redactValue;
|
|
133
|
+
private redactText;
|
|
90
134
|
}
|
|
91
135
|
|
|
92
136
|
/**
|
|
@@ -110,6 +154,1022 @@ declare class CollectingTransport extends Transport {
|
|
|
110
154
|
close(): void;
|
|
111
155
|
}
|
|
112
156
|
|
|
157
|
+
interface SamplingPluginOptions {
|
|
158
|
+
rng?: () => number;
|
|
159
|
+
/** `meta` key holding the trace/run id used to group buffered records. Default `"traceId"`. */
|
|
160
|
+
traceKey?: string;
|
|
161
|
+
/** A record at or above this level elevates its whole trace. Default `Level.ERROR`. */
|
|
162
|
+
elevateAt?: LevelInput;
|
|
163
|
+
/** Enables tail-based elevation, writing straight to these transports on elevation. Omit to disable and get plain rate-based sampling. */
|
|
164
|
+
transports?: Transport[];
|
|
165
|
+
/** Total buffered records allowed across every trace before the oldest trace is evicted. Default 1000. */
|
|
166
|
+
maxBufferedRecords?: number;
|
|
167
|
+
/** Distinct trace ids held at once before the oldest is evicted. Default 200. */
|
|
168
|
+
maxTraces?: number;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Keeps roughly `rate` of records (0.0-1.0), dropping the rest.
|
|
172
|
+
*
|
|
173
|
+
* With `transports` set, sampling becomes tail-based per trace: a record
|
|
174
|
+
* that would otherwise be dropped is buffered under its `meta[traceKey]`
|
|
175
|
+
* value instead of discarded outright. If any later record sharing that
|
|
176
|
+
* trace id reaches `elevateAt` or above, the whole trace is "elevated" —
|
|
177
|
+
* every buffered record for that trace id is flushed straight to
|
|
178
|
+
* `transports`, and every subsequent record for that trace id ships
|
|
179
|
+
* unconditionally. This is what lets a sampled-out request still produce a
|
|
180
|
+
* complete trace once it turns out to matter (it errored).
|
|
181
|
+
*
|
|
182
|
+
* Flushing writes buffered records directly to `transports` — pass the
|
|
183
|
+
* same array given to the `Logger`. This bypasses `beforeLog`/`afterLog`/
|
|
184
|
+
* `onError` for any plugin *after* `SamplingPlugin` in the pipeline (the
|
|
185
|
+
* plugins before it already ran, since that's how the buffered record was
|
|
186
|
+
* built); put `SamplingPlugin` last if that matters for your pipeline.
|
|
187
|
+
*
|
|
188
|
+
* Without `transports`, tail-based elevation is inactive and this behaves
|
|
189
|
+
* exactly like plain rate-based sampling (the original behavior) — a
|
|
190
|
+
* record without `meta[traceKey]` is also just rate-sampled, since there's
|
|
191
|
+
* no trace to buffer it under.
|
|
192
|
+
*
|
|
193
|
+
* Buffering is bounded: at most `maxBufferedRecords` records total and
|
|
194
|
+
* `maxTraces` distinct trace ids are held at once. Once either limit is
|
|
195
|
+
* hit, the oldest buffered trace is evicted (and its records are lost, not
|
|
196
|
+
* flushed) — a deliberate bounded-memory trade-off, not a bug: an
|
|
197
|
+
* unbounded per-trace buffer would let a single pathologically long-lived
|
|
198
|
+
* or high-cardinality trace grow memory without limit.
|
|
199
|
+
*/
|
|
200
|
+
declare class SamplingPlugin implements Plugin {
|
|
201
|
+
readonly rate: number;
|
|
202
|
+
readonly traceKey: string;
|
|
203
|
+
readonly elevateAt: Level;
|
|
204
|
+
readonly transports: Transport[] | undefined;
|
|
205
|
+
readonly maxBufferedRecords: number;
|
|
206
|
+
readonly maxTraces: number;
|
|
207
|
+
private readonly rng;
|
|
208
|
+
private readonly buffer;
|
|
209
|
+
private bufferedCount;
|
|
210
|
+
private readonly elevated;
|
|
211
|
+
constructor(rate: number, options?: SamplingPluginOptions);
|
|
212
|
+
beforeLog(record: LogRecord): LogRecord | null;
|
|
213
|
+
private elevate;
|
|
214
|
+
private bufferRecord;
|
|
215
|
+
private evictOldestTrace;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
declare const GENESIS_HASH: string;
|
|
219
|
+
interface TamperEvidentPluginOptions {
|
|
220
|
+
genesisHash?: string;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Hash-chains every record so tampering with a written log can be
|
|
224
|
+
* detected after the fact.
|
|
225
|
+
*
|
|
226
|
+
* Each record gets `meta.hash` — a SHA-256 hex digest over the record's
|
|
227
|
+
* own content plus the previous record's hash (`meta.prevHash`) — the same
|
|
228
|
+
* hash-chain construction used by tamper-evident/append-only logs: editing
|
|
229
|
+
* or deleting any one line breaks every hash after it in the chain, even
|
|
230
|
+
* if the tamperer edits the file directly and not through this plugin.
|
|
231
|
+
* Opt-in — hashing every record has a real, measurable CPU cost, so it
|
|
232
|
+
* isn't part of the default pipeline.
|
|
233
|
+
*
|
|
234
|
+
* Verify a previously-written log with `TamperEvidentPlugin.verifyChain`,
|
|
235
|
+
* which re-derives each record's hash from its content and confirms it
|
|
236
|
+
* matches both the stored `meta.hash` and the chain built from the records
|
|
237
|
+
* before it, in order.
|
|
238
|
+
*/
|
|
239
|
+
declare class TamperEvidentPlugin implements Plugin {
|
|
240
|
+
private readonly genesisHash;
|
|
241
|
+
private lastHash;
|
|
242
|
+
constructor(options?: TamperEvidentPluginOptions);
|
|
243
|
+
beforeLog(record: LogRecord): LogRecord;
|
|
244
|
+
/**
|
|
245
|
+
* Returns `true` iff every record's hash matches its content plus the
|
|
246
|
+
* previous record's hash, in the given order. Returns `false` at the
|
|
247
|
+
* first break in the chain (an edited, removed, or reordered record).
|
|
248
|
+
*/
|
|
249
|
+
static verifyChain(records: Iterable<Pick<LogRecord, "timestamp" | "level" | "logger" | "message" | "meta">>, options?: TamperEvidentPluginOptions): boolean;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
interface AlertingPluginOptions {
|
|
253
|
+
/** A record at or above this level fires an alert. Default `Level.ERROR`. */
|
|
254
|
+
threshold?: LevelInput;
|
|
255
|
+
/** How long a dedupe window stays open before a collapsed follow-up alert (if any) fires. Default 300_000 (5 minutes). */
|
|
256
|
+
dedupeWindowMs?: number;
|
|
257
|
+
/** Groups records into the same dedupe window. Default: `level:logger:message`. */
|
|
258
|
+
dedupeKey?: (record: LogRecord) => string;
|
|
259
|
+
/** Distinct concurrent dedupe keys tracked at once; beyond this, new keys are dropped rather than tracked. Default 500. */
|
|
260
|
+
maxTrackedKeys?: number;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Base class for plugins that fire an external alert on ERROR/FATAL (or any
|
|
264
|
+
* configurable `threshold`).
|
|
265
|
+
*
|
|
266
|
+
* A concrete subclass implements only `sendAlert(record, occurrences)` —
|
|
267
|
+
* everything else (thresholding, deduplication, never blocking the caller,
|
|
268
|
+
* never letting a broken destination crash logging) lives here.
|
|
269
|
+
*
|
|
270
|
+
* The first record at or above `threshold` for a given dedupe key (by
|
|
271
|
+
* default: level + logger + message) fires `sendAlert` right away, without
|
|
272
|
+
* awaiting it — so the log call that triggered it is never blocked on a
|
|
273
|
+
* webhook, SMTP handshake, or any other I/O, even if the destination is
|
|
274
|
+
* slow or unreachable. This stands in for the shared async dispatch queue
|
|
275
|
+
* a later phase will introduce; once that queue exists, `AlertingPlugin`
|
|
276
|
+
* can route through it instead of firing its own unawaited call per alert.
|
|
277
|
+
*
|
|
278
|
+
* Any further record matching the same dedupe key within
|
|
279
|
+
* `dedupeWindowMs` of the first is *not* sent again — it just increments a
|
|
280
|
+
* counter. When the window closes, if more than one record matched,
|
|
281
|
+
* exactly one follow-up alert is sent with the total occurrence count,
|
|
282
|
+
* instead of spamming the destination once per record. Tracking is bounded
|
|
283
|
+
* to `maxTrackedKeys` distinct concurrent dedupe keys; beyond that, new
|
|
284
|
+
* keys are dropped rather than tracked (alerting degrades under extreme
|
|
285
|
+
* cardinality, logging itself never does).
|
|
286
|
+
*
|
|
287
|
+
* `sendAlert` is always called without being awaited, and any rejection is
|
|
288
|
+
* routed to this plugin's own `onError`, the same as any other plugin hook
|
|
289
|
+
* that throws.
|
|
290
|
+
*/
|
|
291
|
+
declare abstract class AlertingPlugin implements Plugin {
|
|
292
|
+
readonly threshold: Level;
|
|
293
|
+
readonly dedupeWindowMs: number;
|
|
294
|
+
readonly maxTrackedKeys: number;
|
|
295
|
+
private readonly dedupeKeyFn;
|
|
296
|
+
private readonly windows;
|
|
297
|
+
constructor(options?: AlertingPluginOptions);
|
|
298
|
+
afterLog(record: LogRecord): void;
|
|
299
|
+
private flush;
|
|
300
|
+
private safeSend;
|
|
301
|
+
/**
|
|
302
|
+
* Send one alert for `record`, representing `occurrences` collapsed
|
|
303
|
+
* duplicates (1 on first occurrence; the deduped total on a follow-up
|
|
304
|
+
* flush). Override in a concrete subclass — never call this directly,
|
|
305
|
+
* `AlertingPlugin` calls it without awaiting it.
|
|
306
|
+
*/
|
|
307
|
+
protected abstract sendAlert(record: LogRecord, occurrences: number): void | Promise<void>;
|
|
308
|
+
onError?(error: unknown, record: LogRecord): void;
|
|
309
|
+
/** Cancel any pending dedupe-window timers. Call on logger shutdown. */
|
|
310
|
+
close(): void;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Posts one alert body to a Slack incoming webhook URL. Swap in a fake for tests. */
|
|
314
|
+
type SlackSender = (webhookUrl: string, body: string) => Promise<void> | void;
|
|
315
|
+
interface SlackAlertPluginOptions extends AlertingPluginOptions {
|
|
316
|
+
sender?: SlackSender;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Sends deduplicated `AlertingPlugin` alerts to a Slack incoming webhook.
|
|
320
|
+
*
|
|
321
|
+
* `webhookUrl` is the full "Incoming Webhook" URL from Slack's app config.
|
|
322
|
+
* Uses `fetch` — no extra dependency required. Pass `sender` to swap in a
|
|
323
|
+
* fake for tests or an alternate backend.
|
|
324
|
+
*/
|
|
325
|
+
declare class SlackAlertPlugin extends AlertingPlugin {
|
|
326
|
+
readonly webhookUrl: string;
|
|
327
|
+
private readonly sender;
|
|
328
|
+
constructor(webhookUrl: string, options?: SlackAlertPluginOptions);
|
|
329
|
+
protected sendAlert(record: LogRecord, occurrences: number): Promise<void>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** POSTs one PagerDuty Events API v2 payload. Swap in a fake for tests. */
|
|
333
|
+
type PagerDutySender = (body: string) => Promise<void> | void;
|
|
334
|
+
interface PagerDutyAlertPluginOptions extends AlertingPluginOptions {
|
|
335
|
+
sender?: PagerDutySender;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Sends deduplicated `AlertingPlugin` alerts to PagerDuty via the Events
|
|
339
|
+
* API v2 (`POST https://events.pagerduty.com/v2/enqueue`).
|
|
340
|
+
*
|
|
341
|
+
* `routingKey` is an Events API v2 integration key from a PagerDuty
|
|
342
|
+
* service. Uses `fetch` — no extra dependency required. Pass `sender` to
|
|
343
|
+
* swap in a fake for tests or an alternate backend.
|
|
344
|
+
*/
|
|
345
|
+
declare class PagerDutyAlertPlugin extends AlertingPlugin {
|
|
346
|
+
readonly routingKey: string;
|
|
347
|
+
private readonly sender;
|
|
348
|
+
constructor(routingKey: string, options?: PagerDutyAlertPluginOptions);
|
|
349
|
+
protected sendAlert(record: LogRecord, occurrences: number): Promise<void>;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface EmailMessage {
|
|
353
|
+
from: string;
|
|
354
|
+
to: string[];
|
|
355
|
+
subject: string;
|
|
356
|
+
text: string;
|
|
357
|
+
}
|
|
358
|
+
/** Sends one email message. Swap in a fake for tests. */
|
|
359
|
+
type EmailSender = (message: EmailMessage) => Promise<void> | void;
|
|
360
|
+
/** The subset of a `nodemailer` transporter that `EmailAlertPlugin` needs. */
|
|
361
|
+
interface NodemailerTransporterLike {
|
|
362
|
+
sendMail(message: {
|
|
363
|
+
from: string;
|
|
364
|
+
to: string;
|
|
365
|
+
subject: string;
|
|
366
|
+
text: string;
|
|
367
|
+
}): Promise<unknown>;
|
|
368
|
+
}
|
|
369
|
+
interface EmailAlertPluginOptions extends AlertingPluginOptions {
|
|
370
|
+
smtpHost: string;
|
|
371
|
+
smtpPort: number;
|
|
372
|
+
fromAddr: string;
|
|
373
|
+
toAddrs: string[];
|
|
374
|
+
username?: string;
|
|
375
|
+
password?: string;
|
|
376
|
+
/** `false` for an SMTP server that doesn't support STARTTLS (e.g. a local relay). Default `true`. */
|
|
377
|
+
useTls?: boolean;
|
|
378
|
+
/** Pre-built sender, e.g. for tests. Skips the `nodemailer` auto-import entirely. */
|
|
379
|
+
sender?: EmailSender;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Sends deduplicated `AlertingPlugin` alerts by email over SMTP.
|
|
383
|
+
*
|
|
384
|
+
* `nodemailer` is an optional peer dependency: install it yourself, or
|
|
385
|
+
* pass `sender` to swap in a fake for tests or an alternate backend —
|
|
386
|
+
* `username`/`password` are only used if both are set.
|
|
387
|
+
*/
|
|
388
|
+
declare class EmailAlertPlugin extends AlertingPlugin {
|
|
389
|
+
readonly smtpHost: string;
|
|
390
|
+
readonly smtpPort: number;
|
|
391
|
+
readonly fromAddr: string;
|
|
392
|
+
readonly toAddrs: string[];
|
|
393
|
+
private readonly username;
|
|
394
|
+
private readonly password;
|
|
395
|
+
private readonly useTls;
|
|
396
|
+
private readonly injectedSender;
|
|
397
|
+
private transporter;
|
|
398
|
+
constructor(options: EmailAlertPluginOptions);
|
|
399
|
+
protected sendAlert(record: LogRecord, occurrences: number): Promise<void>;
|
|
400
|
+
private importTransporter;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
interface BatchingTransportOptions {
|
|
404
|
+
formatter?: Formatter;
|
|
405
|
+
/** Flush once the buffer holds this many items. Default 100. */
|
|
406
|
+
maxRecords?: number;
|
|
407
|
+
/** Flush once the buffer's estimated byte size reaches this many bytes. Default 1_000_000 (1MB). */
|
|
408
|
+
maxBytes?: number;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Shared base for every batching sink transport (SQL, NoSQL, message queues,
|
|
412
|
+
* batching cloud-native transports). Buffers items and flushes once either
|
|
413
|
+
* `maxRecords` or `maxBytes` is reached, so no batching transport can grow
|
|
414
|
+
* its buffer unboundedly under a sustained burst.
|
|
415
|
+
*
|
|
416
|
+
* `write()`/`close()` stay non-blocking from the caller's perspective: a
|
|
417
|
+
* failed `sendBatch()` is reported via `console.error` rather than thrown,
|
|
418
|
+
* matching `HTTPTransport`'s contract.
|
|
419
|
+
*/
|
|
420
|
+
declare abstract class BatchingTransport<T = LogRecord> extends Transport {
|
|
421
|
+
readonly maxRecords: number;
|
|
422
|
+
readonly maxBytes: number;
|
|
423
|
+
private buffer;
|
|
424
|
+
private bufferBytes;
|
|
425
|
+
constructor(options?: BatchingTransportOptions);
|
|
426
|
+
/** Converts a written record into the buffered item type. Defaults to the record itself. */
|
|
427
|
+
protected toItem(formatted: string, record: LogRecord): T;
|
|
428
|
+
/** Estimated byte size of one buffered item, used for the `maxBytes` bound. */
|
|
429
|
+
protected sizeOf(item: T): number;
|
|
430
|
+
write(formatted: string, record: LogRecord): void;
|
|
431
|
+
/** Send the current batch now, even if it hasn't reached a bound. */
|
|
432
|
+
flush(): void;
|
|
433
|
+
close(): void;
|
|
434
|
+
/** Deliver one batch to the backend. Always called with a non-empty batch. */
|
|
435
|
+
protected abstract sendBatch(batch: readonly T[]): Promise<void> | void;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** One row of the fixed `logs` table schema shared across every SQL transport. */
|
|
439
|
+
interface SQLLogRow {
|
|
440
|
+
timestamp: string;
|
|
441
|
+
level: string;
|
|
442
|
+
logger: string;
|
|
443
|
+
message: string;
|
|
444
|
+
/** `record.meta`, JSON-serialized — every SQL dialect can store this as TEXT/JSON/JSONB. */
|
|
445
|
+
meta: string;
|
|
446
|
+
runId: string | null;
|
|
447
|
+
spanId: string | null;
|
|
448
|
+
parentSpanId: string | null;
|
|
449
|
+
traceId: string | null;
|
|
450
|
+
}
|
|
451
|
+
interface BaseSQLTransportOptions extends BatchingTransportOptions {
|
|
452
|
+
/** Table to write into. Default `"logs"`. */
|
|
453
|
+
tableName?: string;
|
|
454
|
+
/**
|
|
455
|
+
* Dev/test convenience only: run `createTableSQL()` before the first insert.
|
|
456
|
+
* Production schema/migrations are the caller's responsibility — never
|
|
457
|
+
* auto-create schema unless this is explicitly set. Default `false`.
|
|
458
|
+
*/
|
|
459
|
+
ensureSchema?: boolean;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Abstract base for every SQL transport (`SQLiteTransport`, `PostgresTransport`,
|
|
463
|
+
* `MySQLTransport`, ...). Owns the fixed schema and the record → row mapping;
|
|
464
|
+
* each driver-specific subclass only implements `ensureTable()`/`insertRows()`.
|
|
465
|
+
* Inserts are always batched — never one query per log call.
|
|
466
|
+
*/
|
|
467
|
+
declare abstract class BaseSQLTransport extends BatchingTransport<SQLLogRow> {
|
|
468
|
+
readonly tableName: string;
|
|
469
|
+
readonly ensureSchema: boolean;
|
|
470
|
+
private schemaEnsured;
|
|
471
|
+
constructor(options?: BaseSQLTransportOptions);
|
|
472
|
+
protected toItem(_formatted: string, record: LogRecord): SQLLogRow;
|
|
473
|
+
protected sizeOf(row: SQLLogRow): number;
|
|
474
|
+
/**
|
|
475
|
+
* Minimal, dialect-generic `CREATE TABLE IF NOT EXISTS` for dev/test use via
|
|
476
|
+
* `ensureSchema: true`. Production deployments should manage this table with
|
|
477
|
+
* a real migration instead — override in a subclass for dialect-correct
|
|
478
|
+
* column types (e.g. `JSONB` on Postgres).
|
|
479
|
+
*/
|
|
480
|
+
createTableSQL(): string;
|
|
481
|
+
protected sendBatch(rows: readonly SQLLogRow[]): Promise<void>;
|
|
482
|
+
/** Only invoked when `ensureSchema: true` was explicitly passed. */
|
|
483
|
+
protected abstract ensureTable(): Promise<void>;
|
|
484
|
+
/** Always-batched insert of every row in `rows` — never one query per row. */
|
|
485
|
+
protected abstract insertRows(rows: readonly SQLLogRow[]): Promise<void>;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** The subset of a `better-sqlite3` prepared statement that `SQLiteTransport` needs. */
|
|
489
|
+
interface SQLiteStatementLike {
|
|
490
|
+
run(...params: unknown[]): unknown;
|
|
491
|
+
}
|
|
492
|
+
/** The subset of a `better-sqlite3` `Database` that `SQLiteTransport` needs. Inject a fake in tests. */
|
|
493
|
+
interface SQLiteClientLike {
|
|
494
|
+
exec(sql: string): unknown;
|
|
495
|
+
prepare(sql: string): SQLiteStatementLike;
|
|
496
|
+
transaction<Args extends unknown[]>(fn: (...args: Args) => void): (...args: Args) => void;
|
|
497
|
+
}
|
|
498
|
+
interface SQLiteTransportOptions extends BaseSQLTransportOptions {
|
|
499
|
+
/** Pre-built client, e.g. for tests. Skips the `better-sqlite3` auto-import entirely. */
|
|
500
|
+
client?: SQLiteClientLike;
|
|
501
|
+
/** Passed to `better-sqlite3` when no `client` is injected. Default `":memory:"`. */
|
|
502
|
+
filename?: string;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Zero-setup SQL sink — no server process, just a local (or in-memory) file
|
|
506
|
+
* via `better-sqlite3`. Useful for local dev and pairs with the CLI trace
|
|
507
|
+
* viewer (v2.0), which can read this file directly.
|
|
508
|
+
*
|
|
509
|
+
* `better-sqlite3` is an optional peer dependency: install it yourself, or
|
|
510
|
+
* inject a `client` (e.g. a fake, or an already-open `Database` instance).
|
|
511
|
+
*/
|
|
512
|
+
declare class SQLiteTransport extends BaseSQLTransport {
|
|
513
|
+
private readonly injectedClient;
|
|
514
|
+
private readonly filename;
|
|
515
|
+
private client;
|
|
516
|
+
constructor(options?: SQLiteTransportOptions);
|
|
517
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
518
|
+
private resolvedClient;
|
|
519
|
+
private importClient;
|
|
520
|
+
protected ensureTable(): Promise<void>;
|
|
521
|
+
protected insertRows(rows: readonly SQLLogRow[]): Promise<void>;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** The subset of a `pg` `Pool`/`Client` that `PostgresTransport` needs. Inject a fake in tests. */
|
|
525
|
+
interface PgClientLike {
|
|
526
|
+
query(text: string, values: unknown[]): Promise<unknown>;
|
|
527
|
+
}
|
|
528
|
+
interface PostgresTransportOptions extends BaseSQLTransportOptions {
|
|
529
|
+
/** Pre-built client/pool, e.g. for tests, or an already-open `pg.Pool`/`pg.Client`. Skips the `pg` auto-import entirely. */
|
|
530
|
+
client?: PgClientLike;
|
|
531
|
+
/** Passed to `pg.Pool` when no `client` is injected, e.g. `"postgres://user:pass@host:5432/db"`. */
|
|
532
|
+
connectionString?: string;
|
|
533
|
+
/** Passed to `pg.Pool` when no `client` is injected and `connectionString` isn't used. */
|
|
534
|
+
connectionConfig?: Record<string, unknown>;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* SQL sink backed by Postgres via `pg`. Builds one parameterized multi-row
|
|
538
|
+
* `INSERT` per batch — never one query per log call — matching the
|
|
539
|
+
* always-batched contract every `BaseSQLTransport` subclass shares.
|
|
540
|
+
*
|
|
541
|
+
* `pg` is an optional peer dependency: install it yourself, or inject a
|
|
542
|
+
* `client` (e.g. a fake, or an already-open `Pool`/`Client` instance).
|
|
543
|
+
*/
|
|
544
|
+
declare class PostgresTransport extends BaseSQLTransport {
|
|
545
|
+
private readonly injectedClient;
|
|
546
|
+
private readonly connectionString;
|
|
547
|
+
private readonly connectionConfig;
|
|
548
|
+
private client;
|
|
549
|
+
constructor(options?: PostgresTransportOptions);
|
|
550
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
551
|
+
private resolvedClient;
|
|
552
|
+
private importClient;
|
|
553
|
+
/** Postgres-correct `CREATE TABLE IF NOT EXISTS`: `SERIAL` primary key, `JSONB` for `meta`. */
|
|
554
|
+
createTableSQL(): string;
|
|
555
|
+
protected ensureTable(): Promise<void>;
|
|
556
|
+
protected insertRows(rows: readonly SQLLogRow[]): Promise<void>;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** The subset of a `mysql2/promise` connection/pool that `MySQLTransport` needs. Inject a fake in tests. */
|
|
560
|
+
interface MySQLClientLike {
|
|
561
|
+
execute(sql: string, values: unknown[]): Promise<unknown>;
|
|
562
|
+
}
|
|
563
|
+
interface MySQLTransportOptions extends BaseSQLTransportOptions {
|
|
564
|
+
/** Pre-built client/pool, e.g. for tests, or an already-open `mysql2/promise` `Pool`/`Connection`. Skips the `mysql2` auto-import entirely. */
|
|
565
|
+
client?: MySQLClientLike;
|
|
566
|
+
/** Passed to `mysql2/promise`'s `createPool` when no `client` is injected, e.g. `"mysql://user:pass@host:3306/db"`. */
|
|
567
|
+
connectionString?: string;
|
|
568
|
+
/** Passed to `mysql2/promise`'s `createPool` when no `client` is injected and `connectionString` isn't used. */
|
|
569
|
+
connectionConfig?: Record<string, unknown>;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* SQL sink backed by MySQL via `mysql2`. Builds one parameterized multi-row
|
|
573
|
+
* `INSERT` per batch — never one query per log call — matching the
|
|
574
|
+
* always-batched contract every `BaseSQLTransport` subclass shares.
|
|
575
|
+
*
|
|
576
|
+
* `mysql2` is an optional peer dependency: install it yourself, or inject a
|
|
577
|
+
* `client` (e.g. a fake, or an already-open pool/connection from
|
|
578
|
+
* `mysql2/promise`).
|
|
579
|
+
*/
|
|
580
|
+
declare class MySQLTransport extends BaseSQLTransport {
|
|
581
|
+
private readonly injectedClient;
|
|
582
|
+
private readonly connectionString;
|
|
583
|
+
private readonly connectionConfig;
|
|
584
|
+
private client;
|
|
585
|
+
constructor(options?: MySQLTransportOptions);
|
|
586
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
587
|
+
private resolvedClient;
|
|
588
|
+
private importClient;
|
|
589
|
+
/** MySQL-correct `CREATE TABLE IF NOT EXISTS`: `AUTO_INCREMENT` primary key, `JSON` column type for `meta`. */
|
|
590
|
+
createTableSQL(): string;
|
|
591
|
+
protected ensureTable(): Promise<void>;
|
|
592
|
+
protected insertRows(rows: readonly SQLLogRow[]): Promise<void>;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** The subset of a `mongodb` `Collection` that `MongoDBTransport` needs. Inject a fake in tests. */
|
|
596
|
+
interface MongoCollectionLike {
|
|
597
|
+
insertMany(docs: readonly unknown[]): Promise<unknown>;
|
|
598
|
+
}
|
|
599
|
+
/** The subset of a `mongodb` `MongoClient` that `MongoDBTransport` needs to lazily connect. */
|
|
600
|
+
interface MongoClientLike {
|
|
601
|
+
connect(): Promise<unknown>;
|
|
602
|
+
db(name: string): {
|
|
603
|
+
collection(name: string): MongoCollectionLike;
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
interface MongoDBTransportOptions extends BatchingTransportOptions {
|
|
607
|
+
/** Pre-built collection, e.g. for tests, or an already-connected app. Skips the `mongodb` auto-import entirely. */
|
|
608
|
+
collection?: MongoCollectionLike;
|
|
609
|
+
/** `mongodb` connection string, used when no `collection` is injected. Required in that case. */
|
|
610
|
+
connectionString?: string;
|
|
611
|
+
/** Database to write into when connecting via `connectionString`. Default `"logquill"`. */
|
|
612
|
+
database?: string;
|
|
613
|
+
/** Collection to write into when connecting via `connectionString`. Default `"logs"`. */
|
|
614
|
+
collectionName?: string;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Sink for MongoDB. Records map 1:1 to documents — no JSON-in-a-column
|
|
618
|
+
* workaround needed, unlike the SQL transports.
|
|
619
|
+
*
|
|
620
|
+
* `mongodb` is an optional peer dependency: install it yourself, or inject a
|
|
621
|
+
* `collection` (e.g. a fake, or a `Collection` from a client your app already
|
|
622
|
+
* manages).
|
|
623
|
+
*/
|
|
624
|
+
declare class MongoDBTransport extends BatchingTransport {
|
|
625
|
+
private readonly injectedCollection;
|
|
626
|
+
private readonly connectionString;
|
|
627
|
+
private readonly database;
|
|
628
|
+
private readonly collectionName;
|
|
629
|
+
private collection;
|
|
630
|
+
constructor(options?: MongoDBTransportOptions);
|
|
631
|
+
/** Synchronously available collection, if one was injected or already connected — avoids an unnecessary microtask hop on the hot path. */
|
|
632
|
+
private resolvedCollection;
|
|
633
|
+
private importCollection;
|
|
634
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/** One item written to DynamoDB: `runId` is the partition key, `timestamp` is the sort key. */
|
|
638
|
+
interface DynamoLogItem {
|
|
639
|
+
runId: string;
|
|
640
|
+
timestamp: string;
|
|
641
|
+
level: string;
|
|
642
|
+
logger: string;
|
|
643
|
+
message: string;
|
|
644
|
+
meta: Record<string, unknown>;
|
|
645
|
+
spanId?: string;
|
|
646
|
+
parentSpanId?: string;
|
|
647
|
+
traceId?: string;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* The subset of DynamoDB write access `DynamoDBTransport` needs, deliberately
|
|
651
|
+
* narrower than the AWS SDK v3 command pattern (`send(command)`): one
|
|
652
|
+
* `BatchWriteItem`-equivalent call per sub-batch, already capped at
|
|
653
|
+
* `DYNAMO_BATCH_LIMIT` items by the caller. This keeps fake-based tests
|
|
654
|
+
* trivial — a fake just needs to record `(tableName, items)` calls, not
|
|
655
|
+
* emulate a `DynamoDBClient`. Inject a fake in tests, or rely on the built-in
|
|
656
|
+
* `@aws-sdk/client-dynamodb` wrapper by not injecting a `client`.
|
|
657
|
+
*/
|
|
658
|
+
interface DynamoClientLike {
|
|
659
|
+
batchWriteItems(tableName: string, items: readonly DynamoLogItem[]): Promise<unknown>;
|
|
660
|
+
}
|
|
661
|
+
interface DynamoDBTransportOptions extends BatchingTransportOptions {
|
|
662
|
+
/** Pre-built client, e.g. for tests, or a custom wrapper. Skips the `@aws-sdk/client-dynamodb` auto-import entirely. */
|
|
663
|
+
client?: DynamoClientLike;
|
|
664
|
+
/** Table to write into. Default `"logs"`. */
|
|
665
|
+
tableName?: string;
|
|
666
|
+
/** AWS region, used when no `client` is injected. Falls back to the SDK's own credential-chain resolution when omitted. */
|
|
667
|
+
region?: string;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Sink for Amazon DynamoDB. Partition key is `runId`/`traceId` (whichever is
|
|
671
|
+
* present on `record.meta`, `runId` taking priority), falling back to the
|
|
672
|
+
* logger name when neither is set, so every record always lands under some
|
|
673
|
+
* partition even before `RunPlugin`/`TraceContextPlugin` are wired up. Sort
|
|
674
|
+
* key is `timestamp`.
|
|
675
|
+
*
|
|
676
|
+
* DynamoDB's actual `BatchWriteItem` API caps a call at 25 items, so
|
|
677
|
+
* `sendBatch()` chunks a larger batch into sub-batches of 25 — matching how
|
|
678
|
+
* `SQSTransport` respects `SendMessageBatch`'s 10-item cap.
|
|
679
|
+
*
|
|
680
|
+
* `@aws-sdk/client-dynamodb` is an optional peer dependency: install it
|
|
681
|
+
* yourself, or inject a `client` (e.g. a fake, or a custom wrapper around a
|
|
682
|
+
* `DynamoDBClient` your app already manages).
|
|
683
|
+
*/
|
|
684
|
+
declare class DynamoDBTransport extends BatchingTransport {
|
|
685
|
+
private readonly injectedClient;
|
|
686
|
+
private readonly tableName;
|
|
687
|
+
private readonly region;
|
|
688
|
+
private client;
|
|
689
|
+
constructor(options?: DynamoDBTransportOptions);
|
|
690
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
691
|
+
private resolvedClient;
|
|
692
|
+
private importClient;
|
|
693
|
+
/** `meta.runId`, else `meta.traceId`, else the logger name — see the class doc for why. */
|
|
694
|
+
private partitionKey;
|
|
695
|
+
private toDynamoItem;
|
|
696
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** The subset of a `redis` (node-redis v4) client that `RedisTransport` needs. Inject a fake in tests. */
|
|
700
|
+
interface RedisClientLike {
|
|
701
|
+
xAdd(stream: string, id: string, fields: Record<string, string>): Promise<unknown>;
|
|
702
|
+
}
|
|
703
|
+
interface RedisTransportOptions extends BatchingTransportOptions {
|
|
704
|
+
/** Pre-built, already-connected client, e.g. for tests. Skips the `redis` auto-import entirely. */
|
|
705
|
+
client?: RedisClientLike;
|
|
706
|
+
/** `redis` connection URL, used when no `client` is injected. Default `"redis://localhost:6379"`. */
|
|
707
|
+
url?: string;
|
|
708
|
+
/** Stream key to `XADD` into. Default `"logquill:logs"`. */
|
|
709
|
+
stream?: string;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Sink for Redis, via Redis Streams (`XADD`) — as CLAUDE.md's spec puts it,
|
|
713
|
+
* "a fast local buffer, a different use case from durable storage, not a
|
|
714
|
+
* replacement for the others." Reach for this when you want a low-latency
|
|
715
|
+
* local tail (e.g. feeding a `redis-cli XREAD`-based live viewer) rather than
|
|
716
|
+
* a system of record.
|
|
717
|
+
*
|
|
718
|
+
* Streams has no true multi-entry `XADD`, so unlike the other batching
|
|
719
|
+
* transports this issues one `XADD` per record within the batch rather than
|
|
720
|
+
* one network call per batch — batching here still bounds memory and reduces
|
|
721
|
+
* GC/buffer churn, but it is honestly not a network-call batch the way
|
|
722
|
+
* `MongoDBTransport.insertMany()` or `DynamoDBTransport`'s `BatchWriteItem`
|
|
723
|
+
* chunks are.
|
|
724
|
+
*
|
|
725
|
+
* `redis` is an optional peer dependency: install it yourself, or inject a
|
|
726
|
+
* `client` (e.g. a fake, or an already-connected client your app manages).
|
|
727
|
+
*/
|
|
728
|
+
declare class RedisTransport extends BatchingTransport {
|
|
729
|
+
private readonly injectedClient;
|
|
730
|
+
private readonly url;
|
|
731
|
+
private readonly stream;
|
|
732
|
+
private client;
|
|
733
|
+
constructor(options?: RedisTransportOptions);
|
|
734
|
+
/** Synchronously available client, if one was injected or already connected — avoids an unnecessary microtask hop on the hot path. */
|
|
735
|
+
private resolvedClient;
|
|
736
|
+
private importClient;
|
|
737
|
+
private toFields;
|
|
738
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
interface BaseQueueTransportOptions extends BatchingTransportOptions {
|
|
742
|
+
/**
|
|
743
|
+
* Destination name on the backend — a Kafka topic, a RabbitMQ queue name,
|
|
744
|
+
* an SQS queue URL, or a GCP Pub/Sub topic. One generic option name so the
|
|
745
|
+
* base class's orchestration stays backend-agnostic; each subclass's own
|
|
746
|
+
* docs use its backend's own vocabulary for what this means.
|
|
747
|
+
*/
|
|
748
|
+
topic: string;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Abstract base for every message-queue transport (`KafkaTransport`,
|
|
752
|
+
* `RabbitMQTransport`, `SQSTransport`, `PubSubTransport`). Owns the "always
|
|
753
|
+
* batch, never publish one message per log call" contract shared across
|
|
754
|
+
* every queue backend — buffering itself is inherited from
|
|
755
|
+
* `BatchingTransport`; each concrete subclass only implements
|
|
756
|
+
* `publishBatch()` against its own driver's publish API.
|
|
757
|
+
*/
|
|
758
|
+
declare abstract class BaseQueueTransport extends BatchingTransport {
|
|
759
|
+
readonly topic: string;
|
|
760
|
+
constructor(options: BaseQueueTransportOptions);
|
|
761
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
762
|
+
/** Deliver one batch of records to the queue backend. Always called with a non-empty batch. */
|
|
763
|
+
protected abstract publishBatch(records: readonly LogRecord[]): Promise<void>;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/** The subset of a `kafkajs` `Producer` that `KafkaTransport` needs. Inject a fake in tests. */
|
|
767
|
+
interface KafkaProducerLike {
|
|
768
|
+
/** Optional: some injected producers (or fakes) are already connected. */
|
|
769
|
+
connect?(): Promise<void>;
|
|
770
|
+
send(record: {
|
|
771
|
+
topic: string;
|
|
772
|
+
messages: {
|
|
773
|
+
key: string | null;
|
|
774
|
+
value: string;
|
|
775
|
+
}[];
|
|
776
|
+
}): Promise<unknown>;
|
|
777
|
+
}
|
|
778
|
+
interface KafkaTransportOptions extends BaseQueueTransportOptions {
|
|
779
|
+
/** Pre-built producer, e.g. for tests, or an already-configured kafkajs `Producer`. Skips the `kafkajs` auto-import entirely. */
|
|
780
|
+
client?: KafkaProducerLike;
|
|
781
|
+
/** Broker addresses passed to `kafkajs`'s `Kafka({ brokers })` when no `client` is injected. Default `["localhost:9092"]`. */
|
|
782
|
+
brokers?: string[];
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Publishes batches to a Kafka topic via `kafkajs`. Each message's `key` is
|
|
786
|
+
* set to `meta.runId` (falling back to `meta.traceId`, then `null`), so
|
|
787
|
+
* kafkajs's default partitioner keeps every message from the same agent
|
|
788
|
+
* run/trace on one partition — preserving per-trace ordering, per the
|
|
789
|
+
* message-queue contract in the project spec.
|
|
790
|
+
*
|
|
791
|
+
* `kafkajs` is an optional peer dependency: install it yourself, or inject a
|
|
792
|
+
* `client` (e.g. a fake, or an already-configured `Producer`).
|
|
793
|
+
*/
|
|
794
|
+
declare class KafkaTransport extends BaseQueueTransport {
|
|
795
|
+
private readonly injectedClient;
|
|
796
|
+
private readonly brokers;
|
|
797
|
+
private client;
|
|
798
|
+
constructor(options: KafkaTransportOptions);
|
|
799
|
+
/** Synchronously available producer, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
800
|
+
private resolvedClient;
|
|
801
|
+
/**
|
|
802
|
+
* Builds the real `kafkajs` producer and connects it. Connection happens
|
|
803
|
+
* here, once, as part of acquiring the driver — not on every
|
|
804
|
+
* `publishBatch()` call — so an injected `client` (tests, or a caller's
|
|
805
|
+
* own already-connected producer) is trusted to already be ready and is
|
|
806
|
+
* never re-connected.
|
|
807
|
+
*/
|
|
808
|
+
private importClient;
|
|
809
|
+
protected publishBatch(records: readonly LogRecord[]): Promise<void>;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/** The subset of an `amqplib` `Channel` that `RabbitMQTransport` needs. Inject a fake in tests. */
|
|
813
|
+
interface AmqpChannelLike {
|
|
814
|
+
/** Optional: only called once, and only when provided, to ensure the queue exists. */
|
|
815
|
+
assertQueue?(queue: string, options?: unknown): Promise<unknown>;
|
|
816
|
+
sendToQueue(queue: string, content: Buffer, options?: unknown): boolean;
|
|
817
|
+
}
|
|
818
|
+
interface RabbitMQTransportOptions extends BaseQueueTransportOptions {
|
|
819
|
+
/** Pre-built channel, e.g. for tests, or an already-open amqplib `Channel`. Skips the `amqplib` auto-import entirely. */
|
|
820
|
+
client?: AmqpChannelLike;
|
|
821
|
+
/** Connection URL passed to `amqplib.connect()` when no `client` is injected. Default `"amqp://localhost"`. */
|
|
822
|
+
url?: string;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Publishes batches to a RabbitMQ queue via `amqplib`. RabbitMQ's core API
|
|
826
|
+
* has no native multi-message batch primitive — there is no
|
|
827
|
+
* `sendToQueue`-equivalent that takes an array — so `publishBatch()` loops
|
|
828
|
+
* one `sendToQueue()` call per record. The "batch" LogQuill promises is at
|
|
829
|
+
* the buffering level: `maxRecords`/`maxBytes` still governs how often that
|
|
830
|
+
* loop runs, so this transport never makes one network round trip per log
|
|
831
|
+
* call; it just can't make one round trip per *batch* either, honestly,
|
|
832
|
+
* since RabbitMQ itself doesn't offer that primitive.
|
|
833
|
+
*
|
|
834
|
+
* `amqplib` is an optional peer dependency: install it yourself, or inject a
|
|
835
|
+
* `client` (e.g. a fake, or an already-open `Channel`).
|
|
836
|
+
*/
|
|
837
|
+
declare class RabbitMQTransport extends BaseQueueTransport {
|
|
838
|
+
private readonly injectedClient;
|
|
839
|
+
private readonly url;
|
|
840
|
+
private client;
|
|
841
|
+
constructor(options: RabbitMQTransportOptions);
|
|
842
|
+
/** Synchronously available channel, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
843
|
+
private resolvedClient;
|
|
844
|
+
/**
|
|
845
|
+
* Opens the real `amqplib` connection/channel and asserts the queue
|
|
846
|
+
* exists. Both happen here, once, as part of acquiring the driver — not on
|
|
847
|
+
* every `publishBatch()` call — so an injected `client` (tests, or a
|
|
848
|
+
* caller's own already-open channel) is trusted to already have its queue
|
|
849
|
+
* set up and is never re-asserted.
|
|
850
|
+
*/
|
|
851
|
+
private importClient;
|
|
852
|
+
protected publishBatch(records: readonly LogRecord[]): Promise<void>;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* The subset of AWS SDK v3's SQS client that `SQSTransport` needs, narrowed
|
|
857
|
+
* to a plain `sendMessageBatch(queueUrl, entries)` method rather than
|
|
858
|
+
* modeling the full `@aws-sdk/client-sqs` command/client pattern — simpler
|
|
859
|
+
* to fake in tests, and the only shape this transport actually calls. Inject
|
|
860
|
+
* a fake in tests, or wrap a real `SQSClient` to match this shape.
|
|
861
|
+
*/
|
|
862
|
+
interface SQSClientLike {
|
|
863
|
+
sendMessageBatch(queueUrl: string, entries: {
|
|
864
|
+
id: string;
|
|
865
|
+
body: string;
|
|
866
|
+
}[]): Promise<unknown>;
|
|
867
|
+
}
|
|
868
|
+
interface SQSTransportOptions extends BaseQueueTransportOptions {
|
|
869
|
+
/** Pre-built client, e.g. for tests, or a thin wrapper around `@aws-sdk/client-sqs`'s `SQSClient`. Skips the `@aws-sdk/client-sqs` auto-import entirely. */
|
|
870
|
+
client?: SQSClientLike;
|
|
871
|
+
/** AWS region passed to `@aws-sdk/client-sqs` when no `client` is injected. */
|
|
872
|
+
region?: string;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Publishes batches to an SQS queue via `@aws-sdk/client-sqs`'s
|
|
876
|
+
* `SendMessageBatch`. That API caps a single request at 10 messages, so
|
|
877
|
+
* `publishBatch()` chunks any larger batch into sub-batches of 10 —
|
|
878
|
+
* LogQuill's own `maxRecords`/`maxBytes` buffering can flush more than 10
|
|
879
|
+
* records at once; this transport is responsible for respecting SQS's own
|
|
880
|
+
* limit underneath, per the message-queue contract in the project spec.
|
|
881
|
+
*
|
|
882
|
+
* `@aws-sdk/client-sqs` is an optional peer dependency: install it
|
|
883
|
+
* yourself, or inject a `client` (e.g. a fake, or a wrapped `SQSClient`).
|
|
884
|
+
*/
|
|
885
|
+
declare class SQSTransport extends BaseQueueTransport {
|
|
886
|
+
private readonly injectedClient;
|
|
887
|
+
private readonly region;
|
|
888
|
+
private client;
|
|
889
|
+
constructor(options: SQSTransportOptions);
|
|
890
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
891
|
+
private resolvedClient;
|
|
892
|
+
private importClient;
|
|
893
|
+
protected publishBatch(records: readonly LogRecord[]): Promise<void>;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* The subset of a `@google-cloud/pubsub` `Topic` that `PubSubTransport`
|
|
898
|
+
* needs. Inject a fake in tests, or an already-resolved `Topic` instance.
|
|
899
|
+
*/
|
|
900
|
+
interface PubSubTopicLike {
|
|
901
|
+
publishMessage(message: {
|
|
902
|
+
data: Buffer;
|
|
903
|
+
}): Promise<string>;
|
|
904
|
+
}
|
|
905
|
+
interface PubSubTransportOptions extends BaseQueueTransportOptions {
|
|
906
|
+
/** Pre-built topic reference, e.g. for tests, or an already-resolved `@google-cloud/pubsub` `Topic`. Skips the `@google-cloud/pubsub` auto-import entirely. */
|
|
907
|
+
client?: PubSubTopicLike;
|
|
908
|
+
/** GCP project ID passed to `@google-cloud/pubsub` when no `client` is injected. */
|
|
909
|
+
projectId?: string;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Publishes batches to a GCP Pub/Sub topic via `@google-cloud/pubsub`. The
|
|
913
|
+
* real client already does its own internal batching/flow-control underneath
|
|
914
|
+
* `publishMessage()` — this transport doesn't reimplement that. What it does
|
|
915
|
+
* own is LogQuill's own bounded-memory contract: `publishBatch()` only runs
|
|
916
|
+
* once per buffer flush (`maxRecords`/`maxBytes`), so the driver is never
|
|
917
|
+
* invoked once per individual log call.
|
|
918
|
+
*
|
|
919
|
+
* `@google-cloud/pubsub` is an optional peer dependency: install it
|
|
920
|
+
* yourself, or inject a `client` (e.g. a fake, or a resolved `Topic`).
|
|
921
|
+
*/
|
|
922
|
+
declare class PubSubTransport extends BaseQueueTransport {
|
|
923
|
+
private readonly injectedClient;
|
|
924
|
+
private readonly projectId;
|
|
925
|
+
private client;
|
|
926
|
+
constructor(options: PubSubTransportOptions);
|
|
927
|
+
/** Synchronously available topic reference, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
928
|
+
private resolvedClient;
|
|
929
|
+
private importClient;
|
|
930
|
+
protected publishBatch(records: readonly LogRecord[]): Promise<void>;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** One CloudWatch Logs event: epoch-milliseconds timestamp plus a single log line. */
|
|
934
|
+
interface CloudWatchLogEvent {
|
|
935
|
+
timestamp: number;
|
|
936
|
+
message: string;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* The subset of the AWS SDK v3 CloudWatch Logs client that `CloudWatchTransport`
|
|
940
|
+
* needs. Deliberately narrower than the SDK's full command pattern (build a
|
|
941
|
+
* `PutLogEventsCommand`, `.send()` it, track the deprecated sequence-token
|
|
942
|
+
* dance) — modeling just this one operation keeps the injectable-fake surface
|
|
943
|
+
* small for tests; `importClient()` builds the real adapter around it.
|
|
944
|
+
*/
|
|
945
|
+
interface CloudWatchClientLike {
|
|
946
|
+
putLogEvents(logGroupName: string, logStreamName: string, events: readonly CloudWatchLogEvent[]): Promise<unknown>;
|
|
947
|
+
}
|
|
948
|
+
interface CloudWatchTransportOptions extends BatchingTransportOptions {
|
|
949
|
+
/** CloudWatch Logs log group to write into. */
|
|
950
|
+
logGroupName: string;
|
|
951
|
+
/** CloudWatch Logs log stream, within `logGroupName`, to write into. */
|
|
952
|
+
logStreamName: string;
|
|
953
|
+
/** AWS region, e.g. `"us-east-1"`. Passed to the real SDK client; ignored when `client` is injected. */
|
|
954
|
+
region?: string;
|
|
955
|
+
/** Pre-built client, e.g. for tests. Skips the `@aws-sdk/client-cloudwatch-logs` auto-import entirely. */
|
|
956
|
+
client?: CloudWatchClientLike;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Ships batched records to AWS CloudWatch Logs via the AWS SDK v3
|
|
960
|
+
* (`@aws-sdk/client-cloudwatch-logs`). Each buffered record becomes one log
|
|
961
|
+
* event; CloudWatch requires events within a single `PutLogEvents` call to be
|
|
962
|
+
* sorted by timestamp ascending, which `sendBatch()` does before sending.
|
|
963
|
+
*
|
|
964
|
+
* `@aws-sdk/client-cloudwatch-logs` is an optional peer dependency: install
|
|
965
|
+
* it yourself, or inject a `client` (e.g. a fake, or an already-configured
|
|
966
|
+
* `CloudWatchLogsClient` wrapped to match `CloudWatchClientLike`).
|
|
967
|
+
*/
|
|
968
|
+
declare class CloudWatchTransport extends BatchingTransport {
|
|
969
|
+
readonly logGroupName: string;
|
|
970
|
+
readonly logStreamName: string;
|
|
971
|
+
readonly region: string | undefined;
|
|
972
|
+
private readonly injectedClient;
|
|
973
|
+
private client;
|
|
974
|
+
constructor(options: CloudWatchTransportOptions);
|
|
975
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
976
|
+
private resolvedClient;
|
|
977
|
+
private importClient;
|
|
978
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
/** One GCP Cloud Logging entry, shaped for `Log#write`. */
|
|
982
|
+
interface CloudLoggingEntry {
|
|
983
|
+
severity: string;
|
|
984
|
+
timestamp: string;
|
|
985
|
+
jsonPayload: Record<string, unknown>;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* The subset of the `@google-cloud/logging` client that `CloudLoggingTransport`
|
|
989
|
+
* needs. Deliberately narrower than the SDK's full `Log`/`Entry` object model —
|
|
990
|
+
* `importClient()` builds the real adapter around `logging.log(name).write(entries)`.
|
|
991
|
+
*/
|
|
992
|
+
interface CloudLoggingClientLike {
|
|
993
|
+
writeLogEntries(entries: readonly CloudLoggingEntry[]): Promise<unknown>;
|
|
994
|
+
}
|
|
995
|
+
interface CloudLoggingTransportOptions extends BatchingTransportOptions {
|
|
996
|
+
/** GCP log name (the last segment of the log resource path). Default `"logquill"`. */
|
|
997
|
+
logName?: string;
|
|
998
|
+
/** GCP project ID. Passed to the real SDK client; ignored when `client` is injected — omit to use Application Default Credentials' project. */
|
|
999
|
+
projectId?: string;
|
|
1000
|
+
/** Pre-built client, e.g. for tests. Skips the `@google-cloud/logging` auto-import entirely. */
|
|
1001
|
+
client?: CloudLoggingClientLike;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Ships batched records to Google Cloud Logging via `@google-cloud/logging`.
|
|
1005
|
+
* Each record becomes one structured entry (`jsonPayload`), with `level`
|
|
1006
|
+
* mapped onto Cloud Logging's `severity` enum.
|
|
1007
|
+
*
|
|
1008
|
+
* `@google-cloud/logging` is an optional peer dependency: install it
|
|
1009
|
+
* yourself, or inject a `client` (e.g. a fake, or an already-configured
|
|
1010
|
+
* `Log` wrapped to match `CloudLoggingClientLike`).
|
|
1011
|
+
*/
|
|
1012
|
+
declare class CloudLoggingTransport extends BatchingTransport {
|
|
1013
|
+
readonly logName: string;
|
|
1014
|
+
readonly projectId: string | undefined;
|
|
1015
|
+
private readonly injectedClient;
|
|
1016
|
+
private client;
|
|
1017
|
+
constructor(options?: CloudLoggingTransportOptions);
|
|
1018
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
1019
|
+
private resolvedClient;
|
|
1020
|
+
private importClient;
|
|
1021
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/** One Application Insights trace: a message plus its `SeverityLevel`. */
|
|
1025
|
+
interface AppInsightsTrace {
|
|
1026
|
+
message: string;
|
|
1027
|
+
severity: number;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* The subset of Application Insights telemetry that `AppInsightsTransport`
|
|
1031
|
+
* needs.
|
|
1032
|
+
*
|
|
1033
|
+
* `trackTraceBatch` is LogQuill's own batching contract, not a native
|
|
1034
|
+
* `applicationinsights` SDK method — the real SDK only exposes a single-record
|
|
1035
|
+
* `trackTrace()` plus an async `flush()`. The real-client adapter built by
|
|
1036
|
+
* `importClient()` loops `trackTrace()` calls inside `trackTraceBatch` and
|
|
1037
|
+
* flushes once at the end, so batching happens at the LogQuill buffering
|
|
1038
|
+
* level (bounded by `maxRecords`/`maxBytes`), not as a real network-level
|
|
1039
|
+
* batch call — Application Insights doesn't offer one.
|
|
1040
|
+
*/
|
|
1041
|
+
interface AppInsightsClientLike {
|
|
1042
|
+
trackTraceBatch(traces: readonly AppInsightsTrace[]): Promise<unknown>;
|
|
1043
|
+
}
|
|
1044
|
+
interface AppInsightsTransportOptions extends BatchingTransportOptions {
|
|
1045
|
+
/** Azure Application Insights connection string. Passed to the real SDK client; ignored when `client` is injected. */
|
|
1046
|
+
connectionString?: string;
|
|
1047
|
+
/** Pre-built client, e.g. for tests. Skips the `applicationinsights` auto-import entirely. */
|
|
1048
|
+
client?: AppInsightsClientLike;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Ships batched records to Azure Application Insights via `applicationinsights`,
|
|
1052
|
+
* as trace telemetry with `level` mapped onto Application Insights' severity
|
|
1053
|
+
* scale. See `AppInsightsClientLike` for why "batch" means LogQuill-side
|
|
1054
|
+
* buffering plus a loop of single `trackTrace()` calls, not one network batch
|
|
1055
|
+
* request — the underlying SDK has no batch-track API.
|
|
1056
|
+
*
|
|
1057
|
+
* `applicationinsights` is an optional peer dependency: install it yourself,
|
|
1058
|
+
* or inject a `client` (e.g. a fake, or an already-configured `TelemetryClient`
|
|
1059
|
+
* wrapped to match `AppInsightsClientLike`).
|
|
1060
|
+
*/
|
|
1061
|
+
declare class AppInsightsTransport extends BatchingTransport {
|
|
1062
|
+
readonly connectionString: string | undefined;
|
|
1063
|
+
private readonly injectedClient;
|
|
1064
|
+
private client;
|
|
1065
|
+
constructor(options?: AppInsightsTransportOptions);
|
|
1066
|
+
/** Synchronously available client, if one was injected or already imported — avoids an unnecessary microtask hop on the hot path. */
|
|
1067
|
+
private resolvedClient;
|
|
1068
|
+
private importClient;
|
|
1069
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/** Sends one batch of formatted lines to Datadog's Logs intake API at `url`. Swap in a fake for tests. */
|
|
1073
|
+
type DatadogSender = (url: string, apiKey: string, batch: readonly string[]) => Promise<void> | void;
|
|
1074
|
+
interface DatadogTransportOptions extends BatchingTransportOptions {
|
|
1075
|
+
/** Datadog API key, sent in the `DD-API-KEY` header. */
|
|
1076
|
+
apiKey: string;
|
|
1077
|
+
/**
|
|
1078
|
+
* Datadog site (region), e.g. `"datadoghq.com"` (US1, default),
|
|
1079
|
+
* `"datadoghq.eu"` (EU), `"us3.datadoghq.com"`, `"us5.datadoghq.com"`,
|
|
1080
|
+
* `"ap1.datadoghq.com"`. Never hardcode this — sending to the wrong
|
|
1081
|
+
* region's intake host silently fails to deliver logs to your account.
|
|
1082
|
+
*/
|
|
1083
|
+
site?: string;
|
|
1084
|
+
sender?: DatadogSender;
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Batches records and POSTs them as a JSON array to Datadog's Logs intake
|
|
1088
|
+
* API (`https://http-intake.logs.<site>/api/v2/logs`) via `fetch`. Pass
|
|
1089
|
+
* `sender` to swap in a fake for tests, or a different delivery mechanism.
|
|
1090
|
+
*/
|
|
1091
|
+
declare class DatadogTransport extends BatchingTransport {
|
|
1092
|
+
readonly url: string;
|
|
1093
|
+
readonly apiKey: string;
|
|
1094
|
+
readonly site: string;
|
|
1095
|
+
private readonly sender;
|
|
1096
|
+
constructor(options: DatadogTransportOptions);
|
|
1097
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void> | void;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/** Sends one pre-built NDJSON `_bulk` body to `url` with the given headers. Swap in a fake for tests. */
|
|
1101
|
+
type ElasticsearchSender = (url: string, headers: Readonly<Record<string, string>>, body: string) => Promise<void> | void;
|
|
1102
|
+
interface ElasticsearchTransportOptions extends BatchingTransportOptions {
|
|
1103
|
+
/** Cluster base URL, e.g. `"https://localhost:9200"`. */
|
|
1104
|
+
node: string;
|
|
1105
|
+
/** Index to write into. Default `"logs"`. */
|
|
1106
|
+
index?: string;
|
|
1107
|
+
/** Elasticsearch API key (base64 `id:api_key`), sent as `Authorization: ApiKey <apiKey>`. Omit to send no auth header (e.g. behind a proxy that adds its own). */
|
|
1108
|
+
apiKey?: string;
|
|
1109
|
+
sender?: ElasticsearchSender;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Batches records and POSTs them to Elasticsearch's `_bulk` API
|
|
1113
|
+
* (`<node>/_bulk`) via `fetch`, as newline-delimited action+source pairs —
|
|
1114
|
+
* no client dependency needed, just NDJSON body construction. Pass `sender`
|
|
1115
|
+
* to swap in a fake for tests, or a different delivery mechanism.
|
|
1116
|
+
*/
|
|
1117
|
+
declare class ElasticsearchTransport extends BatchingTransport {
|
|
1118
|
+
readonly url: string;
|
|
1119
|
+
readonly index: string;
|
|
1120
|
+
private readonly apiKey;
|
|
1121
|
+
private readonly sender;
|
|
1122
|
+
constructor(options: ElasticsearchTransportOptions);
|
|
1123
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void> | void;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/** Which New Relic ingest region to send to — determines the Log API host. */
|
|
1127
|
+
type NewRelicRegion = "US" | "EU";
|
|
1128
|
+
/** What `NewRelicSender` reports back about one delivery attempt, so the transport can drive its own 429 backoff logic. */
|
|
1129
|
+
interface NewRelicSenderResult {
|
|
1130
|
+
ok: boolean;
|
|
1131
|
+
status: number;
|
|
1132
|
+
/** The raw `Retry-After` response header value, if present — either a number of seconds or an HTTP-date, per RFC 9110. */
|
|
1133
|
+
retryAfter: string | null;
|
|
1134
|
+
}
|
|
1135
|
+
/** Sends one gzip-compressed batch to New Relic's Log API at `url`. Swap in a fake for tests. */
|
|
1136
|
+
type NewRelicSender = (url: string, headers: Readonly<Record<string, string>>, body: Buffer) => Promise<NewRelicSenderResult> | NewRelicSenderResult;
|
|
1137
|
+
interface NewRelicTransportOptions extends BatchingTransportOptions {
|
|
1138
|
+
/** New Relic license key, sent in the `Api-Key` header. */
|
|
1139
|
+
licenseKey: string;
|
|
1140
|
+
/**
|
|
1141
|
+
* New Relic account region — selects the ingest host
|
|
1142
|
+
* (`log-api.newrelic.com` for US, `log-api.eu.newrelic.com` for EU).
|
|
1143
|
+
* Never hardcode this: an EU-region license key sent to the US host (or
|
|
1144
|
+
* vice versa) is rejected. Default `"US"`.
|
|
1145
|
+
*/
|
|
1146
|
+
region?: NewRelicRegion;
|
|
1147
|
+
sender?: NewRelicSender;
|
|
1148
|
+
/** Injectable clock for the 429 backoff window, matching `SamplingPlugin`'s injectable `rng`. Default `Date.now`. */
|
|
1149
|
+
clock?: () => number;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Batches records and POSTs them, gzip-compressed, to New Relic's Log API
|
|
1153
|
+
* (`log-api.newrelic.com` / `log-api.eu.newrelic.com`, region-configurable)
|
|
1154
|
+
* via `fetch`. Strips the reserved `meta.eventType` key (New Relic drops
|
|
1155
|
+
* records carrying it) and honors 429 responses by reading `Retry-After`
|
|
1156
|
+
* and pausing further sends until it elapses, rather than hammering an
|
|
1157
|
+
* account that's already been rate-limited for the rest of the minute.
|
|
1158
|
+
*
|
|
1159
|
+
* Pass `sender` to swap in a fake for tests, and `clock` to control time in
|
|
1160
|
+
* backoff tests without waiting on a real clock.
|
|
1161
|
+
*/
|
|
1162
|
+
declare class NewRelicTransport extends BatchingTransport {
|
|
1163
|
+
readonly url: string;
|
|
1164
|
+
readonly region: NewRelicRegion;
|
|
1165
|
+
private readonly licenseKey;
|
|
1166
|
+
private readonly sender;
|
|
1167
|
+
private readonly clock;
|
|
1168
|
+
private pausedUntil;
|
|
1169
|
+
constructor(options: NewRelicTransportOptions);
|
|
1170
|
+
protected sendBatch(batch: readonly LogRecord[]): Promise<void>;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
113
1173
|
/** The subset of `console` this transport needs — swap in a fake for tests. */
|
|
114
1174
|
interface ConsoleLike {
|
|
115
1175
|
log(message: string): void;
|
|
@@ -176,7 +1236,7 @@ declare class HTTPTransport extends Transport {
|
|
|
176
1236
|
interface LoggerOptions {
|
|
177
1237
|
level?: LevelInput;
|
|
178
1238
|
transports?: Transport[];
|
|
179
|
-
plugins?: Plugin[];
|
|
1239
|
+
plugins?: (Plugin | MiddlewareFunc)[];
|
|
180
1240
|
meta?: Record<string, unknown>;
|
|
181
1241
|
}
|
|
182
1242
|
declare class Logger {
|
|
@@ -188,8 +1248,13 @@ declare class Logger {
|
|
|
188
1248
|
constructor(name: string, options?: LoggerOptions);
|
|
189
1249
|
get level(): Level;
|
|
190
1250
|
setLevel(level: LevelInput): void;
|
|
191
|
-
/**
|
|
192
|
-
|
|
1251
|
+
/**
|
|
1252
|
+
* Register a plugin, or a plain `beforeLog`-style function. A function is
|
|
1253
|
+
* wrapped internally as an anonymous `Plugin` (`FunctionPlugin`) — the
|
|
1254
|
+
* same middleware ergonomics as Express/Koa, without needing to read the
|
|
1255
|
+
* `Plugin` interface first. Returns `this` so calls can be chained.
|
|
1256
|
+
*/
|
|
1257
|
+
use(plugin: Plugin | MiddlewareFunc): this;
|
|
193
1258
|
/** Close every attached transport. Call on shutdown to flush buffered writes. */
|
|
194
1259
|
close(): void;
|
|
195
1260
|
/** A logger scoped under this one, inheriting its level, transports, and plugins. */
|
|
@@ -204,6 +1269,6 @@ declare class Logger {
|
|
|
204
1269
|
fatal(message: string, meta?: Record<string, unknown>): LogRecord | null;
|
|
205
1270
|
}
|
|
206
1271
|
|
|
207
|
-
declare const VERSION = "0.
|
|
1272
|
+
declare const VERSION = "0.3.0";
|
|
208
1273
|
|
|
209
|
-
export { CollectingTransport, type ConsoleLike, ConsoleTransport, type ConsoleTransportOptions, ContextPlugin, DEFAULT_REDACTED_KEYS, FileTransport, type FileTransportOptions, type Formatter, HTTPTransport, type HTTPTransportOptions, JSONFormatter, Level, type LevelInput, type LogRecord, Logger, type LoggerOptions, type Plugin, RedactPlugin, type RedactPluginOptions, SamplingPlugin, type SamplingPluginOptions, type Sender, Transport, VERSION, createRecord, levelName, parseLevel, utcTimestamp };
|
|
1274
|
+
export { AlertingPlugin, type AlertingPluginOptions, type AmqpChannelLike, type AppInsightsClientLike, type AppInsightsTrace, AppInsightsTransport, type AppInsightsTransportOptions, BaseQueueTransport, type BaseQueueTransportOptions, BaseSQLTransport, type BaseSQLTransportOptions, BatchingTransport, type BatchingTransportOptions, type CloudLoggingClientLike, type CloudLoggingEntry, CloudLoggingTransport, type CloudLoggingTransportOptions, type CloudWatchClientLike, type CloudWatchLogEvent, CloudWatchTransport, type CloudWatchTransportOptions, CollectingTransport, type ConsoleLike, ConsoleTransport, type ConsoleTransportOptions, ContextPlugin, DEFAULT_PII_PATTERNS, DEFAULT_REDACTED_KEYS, type DatadogSender, DatadogTransport, type DatadogTransportOptions, type DynamoClientLike, DynamoDBTransport, type DynamoDBTransportOptions, type DynamoLogItem, type ElasticsearchSender, ElasticsearchTransport, type ElasticsearchTransportOptions, EmailAlertPlugin, type EmailAlertPluginOptions, type EmailMessage, type EmailSender, FileTransport, type FileTransportOptions, type Formatter, FunctionPlugin, GENESIS_HASH, HTTPTransport, type HTTPTransportOptions, JSONFormatter, type KafkaProducerLike, KafkaTransport, type KafkaTransportOptions, Level, type LevelInput, type LogRecord, Logger, type LoggerOptions, type MiddlewareFunc, type MongoClientLike, type MongoCollectionLike, MongoDBTransport, type MongoDBTransportOptions, type MySQLClientLike, MySQLTransport, type MySQLTransportOptions, type NewRelicRegion, type NewRelicSender, type NewRelicSenderResult, NewRelicTransport, type NewRelicTransportOptions, type NodemailerTransporterLike, PIIRedactPlugin, type PIIRedactPluginOptions, PagerDutyAlertPlugin, type PagerDutyAlertPluginOptions, type PagerDutySender, type PgClientLike, type Plugin, PostgresTransport, type PostgresTransportOptions, type PubSubTopicLike, PubSubTransport, type PubSubTransportOptions, RabbitMQTransport, type RabbitMQTransportOptions, RedactPlugin, type RedactPluginOptions, type RedisClientLike, RedisTransport, type RedisTransportOptions, type SQLLogRow, type SQLiteClientLike, type SQLiteStatementLike, SQLiteTransport, type SQLiteTransportOptions, type SQSClientLike, SQSTransport, type SQSTransportOptions, SamplingPlugin, type SamplingPluginOptions, type Sender, SlackAlertPlugin, type SlackAlertPluginOptions, type SlackSender, TamperEvidentPlugin, type TamperEvidentPluginOptions, Transport, VERSION, createRecord, levelName, parseLevel, utcTimestamp };
|