logquill 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -2
- package/dist/index.cjs +383 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +208 -2
- package/dist/index.d.ts +208 -2
- package/dist/index.mjs +368 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,12 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/nikhilvdev/logquill-js/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/logquill)
|
|
5
|
+
[](https://www.npmjs.com/package/logquill)
|
|
5
6
|
[](./LICENSE)
|
|
6
7
|
|
|
7
8
|
A logging framework for Node/TypeScript that shares one mental model and one
|
|
8
9
|
JSON log shape with its Python sibling, [`logquill`](https://pypi.org/project/logquill/)
|
|
9
10
|
(repo: `logquill-python`).
|
|
10
11
|
|
|
12
|
+
Status: pre-release, under active development. The core `Logger`, level
|
|
13
|
+
filtering, the plugin pipeline (with `ContextPlugin`/`RedactPlugin`/
|
|
14
|
+
`SamplingPlugin` built in), `JSONFormatter`, and the built-in transports are
|
|
15
|
+
implemented; non-blocking async dispatch is not yet — see `CHANGELOG.md`
|
|
16
|
+
for what's landed so far.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Structured by default** — every call carries a `meta` object, not just a message string
|
|
21
|
+
- **Cross-language record shape** — identical JSON shape and level names/weights as [`logquill` on PyPI](https://pypi.org/project/logquill/)
|
|
22
|
+
- **Pluggable formatters** — `JSONFormatter` out of the box; implement `format(record) -> string` for your own
|
|
23
|
+
- **Pluggable transports** — `ConsoleTransport` (colorized, respects `NO_COLOR`, isomorphic), `FileTransport` (rotation), `HTTPTransport` (batched, `fetch`-based); write your own by subclassing `Transport`; `CollectingTransport` ships as an in-memory sink, handy for tests
|
|
24
|
+
- **Plugin pipeline** — `ContextPlugin`, `RedactPlugin`, `SamplingPlugin` out of the box; `beforeLog`/`afterLog`/`onError` hooks; a throwing plugin can't crash logging
|
|
25
|
+
- **Child loggers** — `.child()` inherits level, transports, and plugins, and merges its own `meta` on top
|
|
26
|
+
- **Typed throughout** — TypeScript strict mode, no `any` in the public API
|
|
27
|
+
- **Dual package** — works via both `require()` (CJS) and `import` (ESM) from the same published package
|
|
28
|
+
- *(planned)* non-blocking async dispatch, `AsyncLocalStorage`-based context propagation — see `CHANGELOG.md`
|
|
29
|
+
|
|
11
30
|
## Install
|
|
12
31
|
|
|
13
32
|
```sh
|
|
@@ -17,14 +36,122 @@ npm install logquill
|
|
|
17
36
|
## Usage
|
|
18
37
|
|
|
19
38
|
```ts
|
|
20
|
-
import {
|
|
39
|
+
import { Level, Logger } from "logquill";
|
|
40
|
+
|
|
41
|
+
const logger = new Logger("app", { level: Level.INFO });
|
|
42
|
+
|
|
43
|
+
const record = logger.info("user signed up", { user_id: 42, plan: "pro" });
|
|
44
|
+
console.log(record);
|
|
45
|
+
// { timestamp: '2026-08-29T07:12:55.968Z', level: 'INFO', logger: 'app',
|
|
46
|
+
// message: 'user signed up', meta: { user_id: 42, plan: 'pro' } }
|
|
47
|
+
|
|
48
|
+
logger.debug("below threshold, dropped"); // -> null, filtered by level
|
|
49
|
+
logger.setLevel("debug");
|
|
50
|
+
logger.debug("now visible"); // -> a record
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every log call returns the record (or `null` if filtered by level) —
|
|
54
|
+
`{"timestamp": ISO8601, "level": string, "logger": string, "message": string, "meta": object}`,
|
|
55
|
+
the same shape shared with [`logquill` on PyPI](https://pypi.org/project/logquill/).
|
|
56
|
+
Use `JSONFormatter` to serialize a record to the canonical JSON line:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { JSONFormatter } from "logquill";
|
|
21
60
|
|
|
22
|
-
console.log(
|
|
61
|
+
console.log(new JSONFormatter().format(record));
|
|
62
|
+
// '{"timestamp":"2026-08-29T07:12:55.968Z","level":"INFO","logger":"app","message":"user signed up","meta":{"user_id":42,"plan":"pro"}}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`.child()` creates a logger scoped under this one, inheriting its level,
|
|
66
|
+
transports, and plugins, and merging its own `meta` on top:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const dbLogger = logger.child("db", { component: "pool" });
|
|
70
|
+
dbLogger.warn("connection lost");
|
|
71
|
+
// logger: "app.db", meta: { component: "pool" }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`.use()` registers a plugin implementing `beforeLog`/`afterLog`/`onError`
|
|
75
|
+
hooks (all optional; a throwing hook is caught and routed to `onError`
|
|
76
|
+
rather than crashing logging):
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
logger.use({
|
|
80
|
+
beforeLog(record) {
|
|
81
|
+
return { ...record, message: record.message.toUpperCase() };
|
|
82
|
+
},
|
|
83
|
+
});
|
|
23
84
|
```
|
|
24
85
|
|
|
25
86
|
Works from both ESM (`import`) and CommonJS (`require`) — the package ships
|
|
26
87
|
a dual build with full TypeScript types.
|
|
27
88
|
|
|
89
|
+
## Transports
|
|
90
|
+
|
|
91
|
+
Attach transports to a `Logger` to actually write records somewhere. Each
|
|
92
|
+
record is dispatched to every attached transport synchronously
|
|
93
|
+
(non-blocking dispatch isn't implemented yet):
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { ConsoleTransport, FileTransport, HTTPTransport, Logger } from "logquill";
|
|
97
|
+
|
|
98
|
+
const logger = new Logger("app", {
|
|
99
|
+
transports: [
|
|
100
|
+
new ConsoleTransport(), // console.log; ERROR/FATAL to console.error, colorized
|
|
101
|
+
new FileTransport("app.log", { maxBytes: 10 * 1024 * 1024, backupCount: 5 }),
|
|
102
|
+
new HTTPTransport("https://logs.example.com/ingest", { batchSize: 50 }),
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
logger.info("user signed up", { user_id: 42 });
|
|
107
|
+
logger.close(); // flushes the HTTPTransport's pending batch, closes the FileTransport's fd
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`ConsoleTransport` writes via `console.log`/`console.error` (not
|
|
111
|
+
`process.stdout`/`stderr`), so it works unmodified in a browser bundle.
|
|
112
|
+
Colorizing defaults to on, unless the [`NO_COLOR`](https://no-color.org)
|
|
113
|
+
environment variable is set. `FileTransport` rotates the file once it
|
|
114
|
+
exceeds `maxBytes`, keeping `backupCount` numbered backups (`.1`, `.2`, …).
|
|
115
|
+
`HTTPTransport` batches formatted lines and POSTs them as
|
|
116
|
+
newline-delimited JSON once `batchSize` is reached, or on `.close()`/
|
|
117
|
+
`.flush()`; pass `sender` to swap in a fake for tests or a different
|
|
118
|
+
backend.
|
|
119
|
+
|
|
120
|
+
Write your own by subclassing `Transport` (implement `write(formatted,
|
|
121
|
+
record)`; `format()` and `close()` have defaults), or use
|
|
122
|
+
`CollectingTransport` — an in-memory sink included for tests:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { CollectingTransport } from "logquill";
|
|
126
|
+
|
|
127
|
+
const sink = new CollectingTransport();
|
|
128
|
+
const logger2 = new Logger("app", { transports: [sink] });
|
|
129
|
+
logger2.info("hello");
|
|
130
|
+
console.log(sink.formatted); // ['{"timestamp":...,"message":"hello",...}']
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Plugins
|
|
134
|
+
|
|
135
|
+
Plugins hook into the pipeline around each log call: `beforeLog(record)` can
|
|
136
|
+
transform a record or return `null` to drop it, `afterLog(record)` runs once
|
|
137
|
+
it's been dispatched to every transport, and `onError(error, record)` catches
|
|
138
|
+
anything a plugin's own hooks throw — a broken plugin can't take down logging.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { ContextPlugin, Logger, RedactPlugin, SamplingPlugin } from "logquill";
|
|
142
|
+
|
|
143
|
+
const logger = new Logger("app");
|
|
144
|
+
logger.use(new ContextPlugin({ service: "api", env: "prod" })); // merged into every record's meta
|
|
145
|
+
logger.use(new RedactPlugin({ keys: ["password", "token"] })); // replaces matching meta values
|
|
146
|
+
logger.use(new SamplingPlugin(0.1)); // keep ~10% of records that reach this point
|
|
147
|
+
|
|
148
|
+
logger.info("login attempt", { user_id: 42, password: "hunter2" });
|
|
149
|
+
// meta: { service: "api", env: "prod", user_id: 42, password: "***" }
|
|
150
|
+
// (unless this call was one of the ~90% sampling dropped, in which case it's null)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Write your own by implementing `Plugin`; every hook is optional.
|
|
154
|
+
|
|
28
155
|
## Development
|
|
29
156
|
|
|
30
157
|
```sh
|
|
@@ -35,6 +162,10 @@ npm run typecheck # tsc --noEmit
|
|
|
35
162
|
npm run coverage # vitest run --coverage
|
|
36
163
|
```
|
|
37
164
|
|
|
165
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the PR workflow, the
|
|
166
|
+
[Code of Conduct](CODE_OF_CONDUCT.md) for community standards, and
|
|
167
|
+
[SECURITY.md](.github/SECURITY.md) for how to report a vulnerability.
|
|
168
|
+
|
|
38
169
|
## License
|
|
39
170
|
|
|
40
171
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,390 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
// src/levels.ts
|
|
7
|
+
var Level = /* @__PURE__ */ ((Level2) => {
|
|
8
|
+
Level2[Level2["TRACE"] = 5] = "TRACE";
|
|
9
|
+
Level2[Level2["DEBUG"] = 10] = "DEBUG";
|
|
10
|
+
Level2[Level2["INFO"] = 20] = "INFO";
|
|
11
|
+
Level2[Level2["WARN"] = 30] = "WARN";
|
|
12
|
+
Level2[Level2["ERROR"] = 40] = "ERROR";
|
|
13
|
+
Level2[Level2["FATAL"] = 50] = "FATAL";
|
|
14
|
+
return Level2;
|
|
15
|
+
})(Level || {});
|
|
16
|
+
var NAME_TO_LEVEL = {
|
|
17
|
+
TRACE: 5 /* TRACE */,
|
|
18
|
+
DEBUG: 10 /* DEBUG */,
|
|
19
|
+
INFO: 20 /* INFO */,
|
|
20
|
+
WARN: 30 /* WARN */,
|
|
21
|
+
ERROR: 40 /* ERROR */,
|
|
22
|
+
FATAL: 50 /* FATAL */
|
|
23
|
+
};
|
|
24
|
+
function levelName(level) {
|
|
25
|
+
return Level[level];
|
|
26
|
+
}
|
|
27
|
+
function parseLevel(level) {
|
|
28
|
+
if (typeof level === "string") {
|
|
29
|
+
const parsed = NAME_TO_LEVEL[level.toUpperCase()];
|
|
30
|
+
if (parsed === void 0) {
|
|
31
|
+
throw new Error(`Unknown log level: ${level}`);
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
if (Level[level] === void 0) {
|
|
36
|
+
throw new Error(`Unknown log level: ${String(level)}`);
|
|
37
|
+
}
|
|
38
|
+
return level;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/records.ts
|
|
42
|
+
function utcTimestamp() {
|
|
43
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
44
|
+
}
|
|
45
|
+
function createRecord(params) {
|
|
46
|
+
return {
|
|
47
|
+
timestamp: utcTimestamp(),
|
|
48
|
+
level: levelName(params.level),
|
|
49
|
+
logger: params.logger,
|
|
50
|
+
message: params.message,
|
|
51
|
+
meta: params.meta
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/formatter.ts
|
|
56
|
+
var JSONFormatter = class {
|
|
57
|
+
format(record) {
|
|
58
|
+
return JSON.stringify(record);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// src/context-plugin.ts
|
|
63
|
+
var ContextPlugin = class {
|
|
64
|
+
context;
|
|
65
|
+
constructor(context) {
|
|
66
|
+
this.context = context;
|
|
67
|
+
}
|
|
68
|
+
beforeLog(record) {
|
|
69
|
+
return { ...record, meta: { ...this.context, ...record.meta } };
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/redact-plugin.ts
|
|
74
|
+
var DEFAULT_REDACTED_KEYS = ["password", "token", "secret", "api_key", "authorization"];
|
|
75
|
+
var RedactPlugin = class {
|
|
76
|
+
keys;
|
|
77
|
+
replacement;
|
|
78
|
+
constructor(options = {}) {
|
|
79
|
+
this.keys = new Set((options.keys ?? DEFAULT_REDACTED_KEYS).map((key) => key.toLowerCase()));
|
|
80
|
+
this.replacement = options.replacement ?? "***";
|
|
81
|
+
}
|
|
82
|
+
beforeLog(record) {
|
|
83
|
+
const meta = {};
|
|
84
|
+
for (const [key, value] of Object.entries(record.meta)) {
|
|
85
|
+
meta[key] = this.keys.has(key.toLowerCase()) ? this.replacement : value;
|
|
86
|
+
}
|
|
87
|
+
return { ...record, meta };
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/sampling-plugin.ts
|
|
92
|
+
var SamplingPlugin = class {
|
|
93
|
+
rate;
|
|
94
|
+
rng;
|
|
95
|
+
constructor(rate, options = {}) {
|
|
96
|
+
if (rate < 0 || rate > 1) {
|
|
97
|
+
throw new Error(`rate must be between 0 and 1, got ${String(rate)}`);
|
|
98
|
+
}
|
|
99
|
+
this.rate = rate;
|
|
100
|
+
this.rng = options.rng ?? Math.random;
|
|
101
|
+
}
|
|
102
|
+
beforeLog(record) {
|
|
103
|
+
return this.rng() < this.rate ? record : null;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// src/transport.ts
|
|
108
|
+
var Transport = class {
|
|
109
|
+
formatter;
|
|
110
|
+
constructor(formatter = new JSONFormatter()) {
|
|
111
|
+
this.formatter = formatter;
|
|
112
|
+
}
|
|
113
|
+
format(record) {
|
|
114
|
+
return this.formatter.format(record);
|
|
115
|
+
}
|
|
116
|
+
/** Flush/release resources on shutdown. No-op unless a transport overrides it. */
|
|
117
|
+
close() {
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var CollectingTransport = class extends Transport {
|
|
121
|
+
formatted = [];
|
|
122
|
+
records = [];
|
|
123
|
+
closed = false;
|
|
124
|
+
write(formatted, record) {
|
|
125
|
+
this.formatted.push(formatted);
|
|
126
|
+
this.records.push(record);
|
|
127
|
+
}
|
|
128
|
+
close() {
|
|
129
|
+
this.closed = true;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// src/console-transport.ts
|
|
134
|
+
var COLORS = {
|
|
135
|
+
[5 /* TRACE */]: "\x1B[90m",
|
|
136
|
+
// gray
|
|
137
|
+
[10 /* DEBUG */]: "\x1B[36m",
|
|
138
|
+
// cyan
|
|
139
|
+
[20 /* INFO */]: "\x1B[32m",
|
|
140
|
+
// green
|
|
141
|
+
[30 /* WARN */]: "\x1B[33m",
|
|
142
|
+
// yellow
|
|
143
|
+
[40 /* ERROR */]: "\x1B[31m",
|
|
144
|
+
// red
|
|
145
|
+
[50 /* FATAL */]: "\x1B[35m"
|
|
146
|
+
// magenta
|
|
147
|
+
};
|
|
148
|
+
var RESET = "\x1B[0m";
|
|
149
|
+
function defaultColorize() {
|
|
150
|
+
const env = typeof process === "undefined" ? void 0 : process.env;
|
|
151
|
+
return env?.NO_COLOR === void 0;
|
|
152
|
+
}
|
|
153
|
+
var ConsoleTransport = class extends Transport {
|
|
154
|
+
colorize;
|
|
155
|
+
out;
|
|
156
|
+
constructor(options = {}) {
|
|
157
|
+
super(options.formatter);
|
|
158
|
+
this.colorize = options.colorize ?? defaultColorize();
|
|
159
|
+
this.out = options.console ?? console;
|
|
160
|
+
}
|
|
161
|
+
write(formatted, record) {
|
|
162
|
+
const level = parseLevel(record.level);
|
|
163
|
+
const line = this.colorize ? this.applyColor(formatted, level) : formatted;
|
|
164
|
+
if (level >= 40 /* ERROR */) {
|
|
165
|
+
this.out.error(line);
|
|
166
|
+
} else {
|
|
167
|
+
this.out.log(line);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
applyColor(formatted, level) {
|
|
171
|
+
return `${COLORS[level]}${formatted}${RESET}`;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var FileTransport = class extends Transport {
|
|
175
|
+
path;
|
|
176
|
+
maxBytes;
|
|
177
|
+
backupCount;
|
|
178
|
+
fd;
|
|
179
|
+
constructor(path$1, options = {}) {
|
|
180
|
+
super(options.formatter);
|
|
181
|
+
this.path = path$1;
|
|
182
|
+
this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;
|
|
183
|
+
this.backupCount = options.backupCount ?? 5;
|
|
184
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true });
|
|
185
|
+
this.fd = fs.openSync(this.path, "a");
|
|
186
|
+
}
|
|
187
|
+
write(formatted) {
|
|
188
|
+
fs.writeSync(this.fd, formatted + "\n");
|
|
189
|
+
if (this.maxBytes > 0 && fs.fstatSync(this.fd).size >= this.maxBytes) {
|
|
190
|
+
this.rotate();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
rotate() {
|
|
194
|
+
fs.closeSync(this.fd);
|
|
195
|
+
if (this.backupCount > 0) {
|
|
196
|
+
for (let index = this.backupCount - 1; index > 0; index--) {
|
|
197
|
+
const src = `${this.path}.${String(index)}`;
|
|
198
|
+
const dst = `${this.path}.${String(index + 1)}`;
|
|
199
|
+
if (fs.existsSync(src)) {
|
|
200
|
+
if (fs.existsSync(dst)) {
|
|
201
|
+
fs.unlinkSync(dst);
|
|
202
|
+
}
|
|
203
|
+
fs.renameSync(src, dst);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const backup = `${this.path}.1`;
|
|
207
|
+
if (fs.existsSync(backup)) {
|
|
208
|
+
fs.unlinkSync(backup);
|
|
209
|
+
}
|
|
210
|
+
fs.renameSync(this.path, backup);
|
|
211
|
+
} else {
|
|
212
|
+
fs.unlinkSync(this.path);
|
|
213
|
+
}
|
|
214
|
+
this.fd = fs.openSync(this.path, "a");
|
|
215
|
+
}
|
|
216
|
+
close() {
|
|
217
|
+
fs.closeSync(this.fd);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/http-transport.ts
|
|
222
|
+
async function fetchSender(url, batch) {
|
|
223
|
+
const response = await fetch(url, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: { "Content-Type": "application/x-ndjson" },
|
|
226
|
+
body: batch.join("\n")
|
|
227
|
+
});
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
throw new Error(`HTTPTransport: request to ${url} failed with status ${String(response.status)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
var HTTPTransport = class extends Transport {
|
|
233
|
+
url;
|
|
234
|
+
batchSize;
|
|
235
|
+
sender;
|
|
236
|
+
batch = [];
|
|
237
|
+
constructor(url, options = {}) {
|
|
238
|
+
super(options.formatter);
|
|
239
|
+
this.url = url;
|
|
240
|
+
this.batchSize = options.batchSize ?? 50;
|
|
241
|
+
this.sender = options.sender ?? fetchSender;
|
|
242
|
+
}
|
|
243
|
+
write(formatted) {
|
|
244
|
+
this.batch.push(formatted);
|
|
245
|
+
if (this.batch.length >= this.batchSize) {
|
|
246
|
+
this.flush();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Send the current batch now, even if it hasn't reached `batchSize`. */
|
|
250
|
+
flush() {
|
|
251
|
+
if (this.batch.length === 0) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const batch = this.batch;
|
|
255
|
+
this.batch = [];
|
|
256
|
+
const result = this.sender(this.url, batch);
|
|
257
|
+
if (result) {
|
|
258
|
+
result.catch((error) => {
|
|
259
|
+
console.error("HTTPTransport: failed to send log batch", error);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
close() {
|
|
264
|
+
this.flush();
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/logger.ts
|
|
269
|
+
var Logger = class _Logger {
|
|
270
|
+
name;
|
|
271
|
+
transports;
|
|
272
|
+
plugins;
|
|
273
|
+
currentLevel;
|
|
274
|
+
baseMeta;
|
|
275
|
+
constructor(name, options = {}) {
|
|
276
|
+
this.name = name;
|
|
277
|
+
this.currentLevel = parseLevel(options.level ?? 20 /* INFO */);
|
|
278
|
+
this.transports = options.transports ? [...options.transports] : [];
|
|
279
|
+
this.plugins = options.plugins ? [...options.plugins] : [];
|
|
280
|
+
this.baseMeta = options.meta ? { ...options.meta } : {};
|
|
281
|
+
}
|
|
282
|
+
get level() {
|
|
283
|
+
return this.currentLevel;
|
|
284
|
+
}
|
|
285
|
+
setLevel(level) {
|
|
286
|
+
this.currentLevel = parseLevel(level);
|
|
287
|
+
}
|
|
288
|
+
/** Register a plugin. Returns `this` so calls can be chained. */
|
|
289
|
+
use(plugin) {
|
|
290
|
+
this.plugins.push(plugin);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
/** Close every attached transport. Call on shutdown to flush buffered writes. */
|
|
294
|
+
close() {
|
|
295
|
+
for (const transport of this.transports) {
|
|
296
|
+
transport.close();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/** A logger scoped under this one, inheriting its level, transports, and plugins. */
|
|
300
|
+
child(name, meta = {}) {
|
|
301
|
+
return new _Logger(`${this.name}.${name}`, {
|
|
302
|
+
level: this.currentLevel,
|
|
303
|
+
transports: this.transports,
|
|
304
|
+
plugins: this.plugins,
|
|
305
|
+
meta: { ...this.baseMeta, ...meta }
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
notifyError(plugin, error, record) {
|
|
309
|
+
try {
|
|
310
|
+
plugin.onError?.(error, record);
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
dispatch(level, message, meta) {
|
|
315
|
+
if (level < this.currentLevel) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
let record = createRecord({
|
|
319
|
+
level,
|
|
320
|
+
logger: this.name,
|
|
321
|
+
message,
|
|
322
|
+
meta: { ...this.baseMeta, ...meta }
|
|
323
|
+
});
|
|
324
|
+
for (const plugin of this.plugins) {
|
|
325
|
+
let result;
|
|
326
|
+
try {
|
|
327
|
+
result = plugin.beforeLog ? plugin.beforeLog(record) : record;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
this.notifyError(plugin, error, record);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (result === null) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
record = result;
|
|
336
|
+
}
|
|
337
|
+
for (const transport of this.transports) {
|
|
338
|
+
transport.write(transport.format(record), record);
|
|
339
|
+
}
|
|
340
|
+
for (const plugin of this.plugins) {
|
|
341
|
+
try {
|
|
342
|
+
plugin.afterLog?.(record);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
this.notifyError(plugin, error, record);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return record;
|
|
348
|
+
}
|
|
349
|
+
trace(message, meta = {}) {
|
|
350
|
+
return this.dispatch(5 /* TRACE */, message, meta);
|
|
351
|
+
}
|
|
352
|
+
debug(message, meta = {}) {
|
|
353
|
+
return this.dispatch(10 /* DEBUG */, message, meta);
|
|
354
|
+
}
|
|
355
|
+
info(message, meta = {}) {
|
|
356
|
+
return this.dispatch(20 /* INFO */, message, meta);
|
|
357
|
+
}
|
|
358
|
+
warn(message, meta = {}) {
|
|
359
|
+
return this.dispatch(30 /* WARN */, message, meta);
|
|
360
|
+
}
|
|
361
|
+
error(message, meta = {}) {
|
|
362
|
+
return this.dispatch(40 /* ERROR */, message, meta);
|
|
363
|
+
}
|
|
364
|
+
fatal(message, meta = {}) {
|
|
365
|
+
return this.dispatch(50 /* FATAL */, message, meta);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
3
369
|
// src/index.ts
|
|
4
|
-
var VERSION = "0.1.
|
|
370
|
+
var VERSION = "0.1.2";
|
|
5
371
|
|
|
372
|
+
exports.CollectingTransport = CollectingTransport;
|
|
373
|
+
exports.ConsoleTransport = ConsoleTransport;
|
|
374
|
+
exports.ContextPlugin = ContextPlugin;
|
|
375
|
+
exports.DEFAULT_REDACTED_KEYS = DEFAULT_REDACTED_KEYS;
|
|
376
|
+
exports.FileTransport = FileTransport;
|
|
377
|
+
exports.HTTPTransport = HTTPTransport;
|
|
378
|
+
exports.JSONFormatter = JSONFormatter;
|
|
379
|
+
exports.Level = Level;
|
|
380
|
+
exports.Logger = Logger;
|
|
381
|
+
exports.RedactPlugin = RedactPlugin;
|
|
382
|
+
exports.SamplingPlugin = SamplingPlugin;
|
|
383
|
+
exports.Transport = Transport;
|
|
6
384
|
exports.VERSION = VERSION;
|
|
385
|
+
exports.createRecord = createRecord;
|
|
386
|
+
exports.levelName = levelName;
|
|
387
|
+
exports.parseLevel = parseLevel;
|
|
388
|
+
exports.utcTimestamp = utcTimestamp;
|
|
7
389
|
//# sourceMappingURL=index.cjs.map
|
|
8
390
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAEO,IAAM,OAAA,GAAU","file":"index.cjs","sourcesContent":["// Phase 0 scaffolding placeholder — Logger, levels, formatters, transports,\n// and plugins land in later phases.\nexport const VERSION = \"0.1.0\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/levels.ts","../src/records.ts","../src/formatter.ts","../src/context-plugin.ts","../src/redact-plugin.ts","../src/sampling-plugin.ts","../src/transport.ts","../src/console-transport.ts","../src/file-transport.ts","../src/http-transport.ts","../src/logger.ts","../src/index.ts"],"names":["Level","path","mkdirSync","dirname","openSync","writeSync","fstatSync","closeSync","existsSync","unlinkSync","renameSync"],"mappings":"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","file":"index.cjs","sourcesContent":["/** Log levels, shared by name and numeric weight with the logquill-python contract. */\nexport enum Level {\n TRACE = 5,\n DEBUG = 10,\n INFO = 20,\n WARN = 30,\n ERROR = 40,\n FATAL = 50,\n}\n\nconst NAME_TO_LEVEL: Readonly<Record<string, Level>> = {\n TRACE: Level.TRACE,\n DEBUG: Level.DEBUG,\n INFO: Level.INFO,\n WARN: Level.WARN,\n ERROR: Level.ERROR,\n FATAL: Level.FATAL,\n};\n\n/** A level given as a `Level`, a level name (any case), or its numeric weight. */\nexport type LevelInput = Level | number | string;\n\n/** The level's name, e.g. `levelName(Level.INFO) === \"INFO\"`. */\nexport function levelName(level: Level): string {\n return Level[level];\n}\n\n/** Normalize a level given as a `Level`, level name, or numeric weight. Throws if unknown. */\nexport function parseLevel(level: LevelInput): Level {\n if (typeof level === \"string\") {\n const parsed = NAME_TO_LEVEL[level.toUpperCase()];\n if (parsed === undefined) {\n throw new Error(`Unknown log level: ${level}`);\n }\n return parsed;\n }\n if (Level[level] === undefined) {\n throw new Error(`Unknown log level: ${String(level)}`);\n }\n return level;\n}\n","import { levelName, type Level } from \"./levels.js\";\n\n/** The cross-language record shape shared with logquill-python. */\nexport interface LogRecord {\n timestamp: string;\n level: string;\n logger: string;\n message: string;\n meta: Record<string, unknown>;\n}\n\n/** ISO8601 UTC timestamp with millisecond precision, matching Python's `utc_timestamp()`. */\nexport function utcTimestamp(): string {\n return new Date().toISOString();\n}\n\nexport function createRecord(params: {\n level: Level;\n logger: string;\n message: string;\n meta: Record<string, unknown>;\n}): LogRecord {\n return {\n timestamp: utcTimestamp(),\n level: levelName(params.level),\n logger: params.logger,\n message: params.message,\n meta: params.meta,\n };\n}\n","import type { LogRecord } from \"./records.js\";\n\n/** `format(record) -> string`, per the transport contract shared with logquill-python. */\nexport interface Formatter {\n format(record: LogRecord): string;\n}\n\n/** Serializes a record to the canonical JSON line shape. */\nexport class JSONFormatter implements Formatter {\n format(record: LogRecord): string {\n return JSON.stringify(record);\n }\n}\n","import type { Plugin } from \"./plugin.js\";\nimport type { LogRecord } from \"./records.js\";\n\n/**\n * Injects fixed key/value pairs into every record's `meta`.\n * A value already present in a record's own `meta` wins over the fixed context.\n */\nexport class ContextPlugin implements Plugin {\n readonly context: Record<string, unknown>;\n\n constructor(context: Record<string, unknown>) {\n this.context = context;\n }\n\n beforeLog(record: LogRecord): LogRecord {\n return { ...record, meta: { ...this.context, ...record.meta } };\n }\n}\n","import type { Plugin } from \"./plugin.js\";\nimport type { LogRecord } from \"./records.js\";\n\nexport const DEFAULT_REDACTED_KEYS: readonly string[] = [\"password\", \"token\", \"secret\", \"api_key\", \"authorization\"];\n\nexport interface RedactPluginOptions {\n keys?: readonly string[];\n replacement?: string;\n}\n\n/** Replaces sensitive `meta` values, matched by key (case-insensitive), with a placeholder. */\nexport class RedactPlugin implements Plugin {\n private readonly keys: ReadonlySet<string>;\n readonly replacement: string;\n\n constructor(options: RedactPluginOptions = {}) {\n this.keys = new Set((options.keys ?? DEFAULT_REDACTED_KEYS).map((key) => key.toLowerCase()));\n this.replacement = options.replacement ?? \"***\";\n }\n\n beforeLog(record: LogRecord): LogRecord {\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(record.meta)) {\n meta[key] = this.keys.has(key.toLowerCase()) ? this.replacement : value;\n }\n return { ...record, meta };\n }\n}\n","import type { Plugin } from \"./plugin.js\";\nimport type { LogRecord } from \"./records.js\";\n\nexport interface SamplingPluginOptions {\n rng?: () => number;\n}\n\n/** Keeps roughly `rate` of records (0.0-1.0), dropping the rest. */\nexport class SamplingPlugin implements Plugin {\n readonly rate: number;\n private readonly rng: () => number;\n\n constructor(rate: number, options: SamplingPluginOptions = {}) {\n if (rate < 0 || rate > 1) {\n throw new Error(`rate must be between 0 and 1, got ${String(rate)}`);\n }\n this.rate = rate;\n this.rng = options.rng ?? Math.random;\n }\n\n beforeLog(record: LogRecord): LogRecord | null {\n return this.rng() < this.rate ? record : null;\n }\n}\n","import { JSONFormatter, type Formatter } from \"./formatter.js\";\nimport type { LogRecord } from \"./records.js\";\n\n/**\n * Sink for log records, per the cross-language transport contract:\n * `format(record) -> string`, `write(formatted, record)`, `close()` on shutdown.\n */\nexport abstract class Transport {\n formatter: Formatter;\n\n constructor(formatter: Formatter = new JSONFormatter()) {\n this.formatter = formatter;\n }\n\n format(record: LogRecord): string {\n return this.formatter.format(record);\n }\n\n abstract write(formatted: string, record: LogRecord): void;\n\n /** Flush/release resources on shutdown. No-op unless a transport overrides it. */\n close(): void {}\n}\n\n/** In-memory transport for tests: collects every (formatted, record) pair written to it. */\nexport class CollectingTransport extends Transport {\n readonly formatted: string[] = [];\n readonly records: LogRecord[] = [];\n closed = false;\n\n write(formatted: string, record: LogRecord): void {\n this.formatted.push(formatted);\n this.records.push(record);\n }\n\n override close(): void {\n this.closed = true;\n }\n}\n","import type { Formatter } from \"./formatter.js\";\nimport { Level, parseLevel } from \"./levels.js\";\nimport type { LogRecord } from \"./records.js\";\nimport { Transport } from \"./transport.js\";\n\nconst COLORS: Readonly<Record<Level, string>> = {\n [Level.TRACE]: \"\\x1b[90m\", // gray\n [Level.DEBUG]: \"\\x1b[36m\", // cyan\n [Level.INFO]: \"\\x1b[32m\", // green\n [Level.WARN]: \"\\x1b[33m\", // yellow\n [Level.ERROR]: \"\\x1b[31m\", // red\n [Level.FATAL]: \"\\x1b[35m\", // magenta\n};\nconst RESET = \"\\x1b[0m\";\n\n/** The subset of `console` this transport needs — swap in a fake for tests. */\nexport interface ConsoleLike {\n log(message: string): void;\n error(message: string): void;\n}\n\nexport interface ConsoleTransportOptions {\n formatter?: Formatter;\n colorize?: boolean;\n console?: ConsoleLike;\n}\n\nfunction defaultColorize(): boolean {\n // Isomorphic guard: `process` doesn't exist in a browser bundle.\n const env: NodeJS.ProcessEnv | undefined = typeof process === \"undefined\" ? undefined : process.env;\n return env?.NO_COLOR === undefined;\n}\n\n/**\n * Writes to `console.log`, routing ERROR/FATAL to `console.error`, colorized by\n * level. Uses the global `console` rather than Node's `process.stdout`/`stderr`\n * so this transport works unmodified in a browser bundle.\n */\nexport class ConsoleTransport extends Transport {\n colorize: boolean;\n private readonly out: ConsoleLike;\n\n constructor(options: ConsoleTransportOptions = {}) {\n super(options.formatter);\n this.colorize = options.colorize ?? defaultColorize();\n this.out = options.console ?? console;\n }\n\n write(formatted: string, record: LogRecord): void {\n const level = parseLevel(record.level);\n const line = this.colorize ? this.applyColor(formatted, level) : formatted;\n if (level >= Level.ERROR) {\n this.out.error(line);\n } else {\n this.out.log(line);\n }\n }\n\n private applyColor(formatted: string, level: Level): string {\n return `${COLORS[level]}${formatted}${RESET}`;\n }\n}\n","import { closeSync, existsSync, fstatSync, mkdirSync, openSync, renameSync, unlinkSync, writeSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { Formatter } from \"./formatter.js\";\nimport { Transport } from \"./transport.js\";\n\nexport interface FileTransportOptions {\n formatter?: Formatter;\n maxBytes?: number;\n backupCount?: number;\n}\n\n/** Appends formatted records to a file, rotating when it exceeds `maxBytes`. */\nexport class FileTransport extends Transport {\n readonly path: string;\n readonly maxBytes: number;\n readonly backupCount: number;\n private fd: number;\n\n constructor(path: string, options: FileTransportOptions = {}) {\n super(options.formatter);\n this.path = path;\n this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;\n this.backupCount = options.backupCount ?? 5;\n mkdirSync(dirname(path), { recursive: true });\n this.fd = openSync(this.path, \"a\");\n }\n\n write(formatted: string): void {\n writeSync(this.fd, formatted + \"\\n\");\n if (this.maxBytes > 0 && fstatSync(this.fd).size >= this.maxBytes) {\n this.rotate();\n }\n }\n\n private rotate(): void {\n closeSync(this.fd);\n\n if (this.backupCount > 0) {\n for (let index = this.backupCount - 1; index > 0; index--) {\n const src = `${this.path}.${String(index)}`;\n const dst = `${this.path}.${String(index + 1)}`;\n if (existsSync(src)) {\n if (existsSync(dst)) {\n unlinkSync(dst);\n }\n renameSync(src, dst);\n }\n }\n const backup = `${this.path}.1`;\n if (existsSync(backup)) {\n unlinkSync(backup);\n }\n renameSync(this.path, backup);\n } else {\n unlinkSync(this.path);\n }\n\n this.fd = openSync(this.path, \"a\");\n }\n\n override close(): void {\n closeSync(this.fd);\n }\n}\n","import type { Formatter } from \"./formatter.js\";\nimport { Transport } from \"./transport.js\";\n\n/** Sends one batch of formatted lines to `url`. Swap in a fake for tests. */\nexport type Sender = (url: string, batch: readonly string[]) => Promise<void> | void;\n\nasync function fetchSender(url: string, batch: readonly string[]): Promise<void> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-ndjson\" },\n body: batch.join(\"\\n\"),\n });\n if (!response.ok) {\n throw new Error(`HTTPTransport: request to ${url} failed with status ${String(response.status)}`);\n }\n}\n\nexport interface HTTPTransportOptions {\n formatter?: Formatter;\n batchSize?: number;\n sender?: Sender;\n}\n\n/**\n * Batches formatted records and POSTs them as newline-delimited JSON via `fetch`.\n * Pass `sender` to swap in a fake for tests, or a different backend.\n */\nexport class HTTPTransport extends Transport {\n readonly url: string;\n readonly batchSize: number;\n private readonly sender: Sender;\n private batch: string[] = [];\n\n constructor(url: string, options: HTTPTransportOptions = {}) {\n super(options.formatter);\n this.url = url;\n this.batchSize = options.batchSize ?? 50;\n this.sender = options.sender ?? fetchSender;\n }\n\n write(formatted: string): void {\n this.batch.push(formatted);\n if (this.batch.length >= this.batchSize) {\n this.flush();\n }\n }\n\n /** Send the current batch now, even if it hasn't reached `batchSize`. */\n flush(): void {\n if (this.batch.length === 0) {\n return;\n }\n const batch = this.batch;\n this.batch = [];\n\n const result = this.sender(this.url, batch);\n if (result) {\n result.catch((error: unknown) => {\n // write()/close() are non-blocking by contract, so a send failure can't\n // propagate to the caller — report it the same way a broken plugin would.\n console.error(\"HTTPTransport: failed to send log batch\", error);\n });\n }\n }\n\n override close(): void {\n this.flush();\n }\n}\n","import { Level, parseLevel, type LevelInput } from \"./levels.js\";\nimport type { Plugin } from \"./plugin.js\";\nimport { createRecord, type LogRecord } from \"./records.js\";\nimport type { Transport } from \"./transport.js\";\n\nexport interface LoggerOptions {\n level?: LevelInput;\n transports?: Transport[];\n plugins?: Plugin[];\n meta?: Record<string, unknown>;\n}\n\nexport class Logger {\n readonly name: string;\n readonly transports: Transport[];\n readonly plugins: Plugin[];\n private currentLevel: Level;\n private readonly baseMeta: Record<string, unknown>;\n\n constructor(name: string, options: LoggerOptions = {}) {\n this.name = name;\n this.currentLevel = parseLevel(options.level ?? Level.INFO);\n this.transports = options.transports ? [...options.transports] : [];\n this.plugins = options.plugins ? [...options.plugins] : [];\n this.baseMeta = options.meta ? { ...options.meta } : {};\n }\n\n get level(): Level {\n return this.currentLevel;\n }\n\n setLevel(level: LevelInput): void {\n this.currentLevel = parseLevel(level);\n }\n\n /** Register a plugin. Returns `this` so calls can be chained. */\n use(plugin: Plugin): this {\n this.plugins.push(plugin);\n return this;\n }\n\n /** Close every attached transport. Call on shutdown to flush buffered writes. */\n close(): void {\n for (const transport of this.transports) {\n transport.close();\n }\n }\n\n /** A logger scoped under this one, inheriting its level, transports, and plugins. */\n child(name: string, meta: Record<string, unknown> = {}): Logger {\n return new Logger(`${this.name}.${name}`, {\n level: this.currentLevel,\n transports: this.transports,\n plugins: this.plugins,\n meta: { ...this.baseMeta, ...meta },\n });\n }\n\n private notifyError(plugin: Plugin, error: unknown, record: LogRecord): void {\n // a broken error handler must not crash logging either\n try {\n plugin.onError?.(error, record);\n } catch {\n /* swallowed */\n }\n }\n\n private dispatch(level: Level, message: string, meta: Record<string, unknown>): LogRecord | null {\n if (level < this.currentLevel) {\n return null;\n }\n\n let record = createRecord({\n level,\n logger: this.name,\n message,\n meta: { ...this.baseMeta, ...meta },\n });\n\n for (const plugin of this.plugins) {\n let result: LogRecord | null;\n try {\n result = plugin.beforeLog ? plugin.beforeLog(record) : record;\n } catch (error) {\n this.notifyError(plugin, error, record);\n continue;\n }\n if (result === null) {\n return null;\n }\n record = result;\n }\n\n for (const transport of this.transports) {\n transport.write(transport.format(record), record);\n }\n\n for (const plugin of this.plugins) {\n try {\n plugin.afterLog?.(record);\n } catch (error) {\n this.notifyError(plugin, error, record);\n }\n }\n\n return record;\n }\n\n trace(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.TRACE, message, meta);\n }\n\n debug(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.DEBUG, message, meta);\n }\n\n info(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.INFO, message, meta);\n }\n\n warn(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.WARN, message, meta);\n }\n\n error(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.ERROR, message, meta);\n }\n\n fatal(message: string, meta: Record<string, unknown> = {}): LogRecord | null {\n return this.dispatch(Level.FATAL, message, meta);\n }\n}\n","export { Level, levelName, parseLevel } from \"./levels.js\";\nexport type { LevelInput } from \"./levels.js\";\n\nexport { createRecord, utcTimestamp } from \"./records.js\";\nexport type { LogRecord } from \"./records.js\";\n\nexport { JSONFormatter } from \"./formatter.js\";\nexport type { Formatter } from \"./formatter.js\";\n\nexport type { Plugin } from \"./plugin.js\";\n\nexport { ContextPlugin } from \"./context-plugin.js\";\n\nexport { DEFAULT_REDACTED_KEYS, RedactPlugin } from \"./redact-plugin.js\";\nexport type { RedactPluginOptions } from \"./redact-plugin.js\";\n\nexport { SamplingPlugin } from \"./sampling-plugin.js\";\nexport type { SamplingPluginOptions } from \"./sampling-plugin.js\";\n\nexport { CollectingTransport, Transport } from \"./transport.js\";\n\nexport { ConsoleTransport } from \"./console-transport.js\";\nexport type { ConsoleLike, ConsoleTransportOptions } from \"./console-transport.js\";\n\nexport { FileTransport } from \"./file-transport.js\";\nexport type { FileTransportOptions } from \"./file-transport.js\";\n\nexport { HTTPTransport } from \"./http-transport.js\";\nexport type { HTTPTransportOptions, Sender } from \"./http-transport.js\";\n\nexport { Logger } from \"./logger.js\";\nexport type { LoggerOptions } from \"./logger.js\";\n\nexport const VERSION = \"0.1.2\";\n"]}
|