@ayepi/log 0.4.1 → 0.5.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 +2 -1
- package/ayepi-log-errors-console.md +6 -0
- package/ayepi-log-transports.md +5 -1
- package/dist/index.cjs +5 -2
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -2
- package/dist/internal.cjs +19 -0
- package/dist/internal.js +19 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -29,7 +29,8 @@ Builds a record from mixed arguments:
|
|
|
29
29
|
- object args are **merged** into the record;
|
|
30
30
|
- `Error` args become `error` / `additionalErrors` (serialized with `name`, `message`,
|
|
31
31
|
`stack`, recursive `cause`, own props) — and **any trace context attached to the error
|
|
32
|
-
is merged in** (so a caught error carries the context from where it was thrown);
|
|
32
|
+
is merged in** (so a caught error carries the context from where it was thrown); a string
|
|
33
|
+
`error` field becomes `{ name: 'Error', message }`;
|
|
33
34
|
- emitted only if `level >= ` the configured threshold (`debug < info < warn < error`).
|
|
34
35
|
|
|
35
36
|
```ts
|
|
@@ -35,6 +35,12 @@ The first `Error` arg becomes the record's `error`; any further `Error` args bec
|
|
|
35
35
|
`additionalErrors`. Non‑`Error` values become `{ name: 'NonError', message }` (the message
|
|
36
36
|
is the string itself, or `String(value)`, guarded against unstringifiable values).
|
|
37
37
|
|
|
38
|
+
An object arg's `error` field lands on the same reserved key when no `Error` arg took it, and
|
|
39
|
+
is normalized to a `SerializedError` too: a string becomes `{ name: 'Error', message }` (so
|
|
40
|
+
`log.warn('job.failed', { error: 'timed out' })` renders `error=Error: timed out`), a value
|
|
41
|
+
that already has a string `name` and `message` is kept, and anything else is serialized as
|
|
42
|
+
above. With an `Error` arg present, the field's value moves aside to `error2`.
|
|
43
|
+
|
|
38
44
|
```ts
|
|
39
45
|
const e1 = new Error('first'); (e1 as { code?: string }).code = 'E1'
|
|
40
46
|
const e2 = new TypeError('second')
|
package/ayepi-log-transports.md
CHANGED
|
@@ -63,7 +63,7 @@ const cap: Transport = { name: 'cap', write: (_r, text) => { line = text } }
|
|
|
63
63
|
function consoleTransport(opts?: ConsoleTransportOptions): Transport
|
|
64
64
|
|
|
65
65
|
interface ConsoleTransportOptions {
|
|
66
|
-
/** The console to write through
|
|
66
|
+
/** The console to write through (default the global console); its methods are bound when the transport is made. */
|
|
67
67
|
readonly console?: ConsoleLike
|
|
68
68
|
/** Map a record level to a console method (default: error→error, warn→warn, debug→debug, else→log). */
|
|
69
69
|
readonly method?: (level: Level) => keyof ConsoleLike
|
|
@@ -82,6 +82,10 @@ Writes the pre‑formatted `text` to the level‑mapped console method. The defa
|
|
|
82
82
|
method mapping is `error → error`, `warn → warn`, `debug → debug`, everything else →
|
|
83
83
|
`log`.
|
|
84
84
|
|
|
85
|
+
The console's methods are bound when `consoleTransport()` is called, so a transport made
|
|
86
|
+
before `interceptConsole()` keeps writing to the original methods afterwards, and no line
|
|
87
|
+
loops back into the logger.
|
|
88
|
+
|
|
85
89
|
The default logger's default transport is a `consoleTransport` bound to the **captured
|
|
86
90
|
original** console (taken before any interception is installed), so logs aren't recursed
|
|
87
91
|
even when console interception is on. If `globalThis.console` is absent it falls back to a
|
package/dist/index.cjs
CHANGED
|
@@ -33,9 +33,12 @@ const noopConsole = {
|
|
|
33
33
|
};
|
|
34
34
|
const globalConsole = () => globalThis.console ?? noopConsole;
|
|
35
35
|
const defaultMethod = (level) => level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "log";
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* A {@link Transport} that writes the formatted line to a console. The console's methods are captured when the
|
|
38
|
+
* transport is made, so make it before intercepting the console.
|
|
39
|
+
*/
|
|
37
40
|
function consoleTransport(opts = {}) {
|
|
38
|
-
const target = opts.console ?? globalConsole();
|
|
41
|
+
const target = captureConsole(opts.console ?? globalConsole());
|
|
39
42
|
const pick = opts.method ?? defaultMethod;
|
|
40
43
|
return {
|
|
41
44
|
name: "console",
|
package/dist/index.d.cts
CHANGED
|
@@ -12,12 +12,18 @@ interface ConsoleLike {
|
|
|
12
12
|
}
|
|
13
13
|
/** Options for {@link consoleTransport}. */
|
|
14
14
|
interface ConsoleTransportOptions {
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The console to write through (default the global `console`). Its methods are bound when the transport is made,
|
|
17
|
+
* so a later `interceptConsole()` that replaces them never feeds a line back into the logger.
|
|
18
|
+
*/
|
|
16
19
|
readonly console?: ConsoleLike;
|
|
17
20
|
/** Map a record level to a console method (default: error→error, warn→warn, debug→debug, else→log). */
|
|
18
21
|
readonly method?: (level: Level) => keyof ConsoleLike;
|
|
19
22
|
}
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* A {@link Transport} that writes the formatted line to a console. The console's methods are captured when the
|
|
25
|
+
* transport is made, so make it before intercepting the console.
|
|
26
|
+
*/
|
|
21
27
|
declare function consoleTransport(opts?: ConsoleTransportOptions): Transport;
|
|
22
28
|
/** Configuration for {@link createLogger}. */
|
|
23
29
|
interface LoggerConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -12,12 +12,18 @@ interface ConsoleLike {
|
|
|
12
12
|
}
|
|
13
13
|
/** Options for {@link consoleTransport}. */
|
|
14
14
|
interface ConsoleTransportOptions {
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The console to write through (default the global `console`). Its methods are bound when the transport is made,
|
|
17
|
+
* so a later `interceptConsole()` that replaces them never feeds a line back into the logger.
|
|
18
|
+
*/
|
|
16
19
|
readonly console?: ConsoleLike;
|
|
17
20
|
/** Map a record level to a console method (default: error→error, warn→warn, debug→debug, else→log). */
|
|
18
21
|
readonly method?: (level: Level) => keyof ConsoleLike;
|
|
19
22
|
}
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* A {@link Transport} that writes the formatted line to a console. The console's methods are captured when the
|
|
25
|
+
* transport is made, so make it before intercepting the console.
|
|
26
|
+
*/
|
|
21
27
|
declare function consoleTransport(opts?: ConsoleTransportOptions): Transport;
|
|
22
28
|
/** Configuration for {@link createLogger}. */
|
|
23
29
|
interface LoggerConfig {
|
package/dist/index.js
CHANGED
|
@@ -32,9 +32,12 @@ const noopConsole = {
|
|
|
32
32
|
};
|
|
33
33
|
const globalConsole = () => globalThis.console ?? noopConsole;
|
|
34
34
|
const defaultMethod = (level) => level === "error" ? "error" : level === "warn" ? "warn" : level === "debug" ? "debug" : "log";
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* A {@link Transport} that writes the formatted line to a console. The console's methods are captured when the
|
|
37
|
+
* transport is made, so make it before intercepting the console.
|
|
38
|
+
*/
|
|
36
39
|
function consoleTransport(opts = {}) {
|
|
37
|
-
const target = opts.console ?? globalConsole();
|
|
40
|
+
const target = captureConsole(opts.console ?? globalConsole());
|
|
38
41
|
const pick = opts.method ?? defaultMethod;
|
|
39
42
|
return {
|
|
40
43
|
name: "console",
|
package/dist/internal.cjs
CHANGED
|
@@ -269,8 +269,27 @@ function buildRecord(level, args, opts) {
|
|
|
269
269
|
record = merge(record, getContext());
|
|
270
270
|
for (const o of objects) record = merge(record, o);
|
|
271
271
|
for (const c of errorContexts) record = merge(record, c);
|
|
272
|
+
if (record.error !== void 0) record.error = asSerializedError(record.error);
|
|
272
273
|
return record;
|
|
273
274
|
}
|
|
275
|
+
/** Whether `v` already has the {@link SerializedError} shape (a string `name` and `message`). */
|
|
276
|
+
function isSerializedError(v) {
|
|
277
|
+
if (v === null || typeof v !== "object") return false;
|
|
278
|
+
const o = v;
|
|
279
|
+
return typeof o.name === "string" && typeof o.message === "string";
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Normalize whatever landed on the reserved `error` key (an `Error` arg, or a field such as
|
|
283
|
+
* `{ error: 'timed out' }`) to a {@link SerializedError}: a string becomes `{ name: 'Error', message }`,
|
|
284
|
+
* an already serialized shape is kept, and anything else goes through {@link serializeError}.
|
|
285
|
+
*/
|
|
286
|
+
function asSerializedError(v) {
|
|
287
|
+
if (typeof v === "string") return {
|
|
288
|
+
name: "Error",
|
|
289
|
+
message: v
|
|
290
|
+
};
|
|
291
|
+
return !(v instanceof Error) && isSerializedError(v) ? v : serializeError(v);
|
|
292
|
+
}
|
|
274
293
|
const renderValue = (v) => {
|
|
275
294
|
if (typeof v === "string") return v;
|
|
276
295
|
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
package/dist/internal.js
CHANGED
|
@@ -269,8 +269,27 @@ function buildRecord(level, args, opts) {
|
|
|
269
269
|
record = merge(record, getContext());
|
|
270
270
|
for (const o of objects) record = merge(record, o);
|
|
271
271
|
for (const c of errorContexts) record = merge(record, c);
|
|
272
|
+
if (record.error !== void 0) record.error = asSerializedError(record.error);
|
|
272
273
|
return record;
|
|
273
274
|
}
|
|
275
|
+
/** Whether `v` already has the {@link SerializedError} shape (a string `name` and `message`). */
|
|
276
|
+
function isSerializedError(v) {
|
|
277
|
+
if (v === null || typeof v !== "object") return false;
|
|
278
|
+
const o = v;
|
|
279
|
+
return typeof o.name === "string" && typeof o.message === "string";
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Normalize whatever landed on the reserved `error` key (an `Error` arg, or a field such as
|
|
283
|
+
* `{ error: 'timed out' }`) to a {@link SerializedError}: a string becomes `{ name: 'Error', message }`,
|
|
284
|
+
* an already serialized shape is kept, and anything else goes through {@link serializeError}.
|
|
285
|
+
*/
|
|
286
|
+
function asSerializedError(v) {
|
|
287
|
+
if (typeof v === "string") return {
|
|
288
|
+
name: "Error",
|
|
289
|
+
message: v
|
|
290
|
+
};
|
|
291
|
+
return !(v instanceof Error) && isSerializedError(v) ? v : serializeError(v);
|
|
292
|
+
}
|
|
274
293
|
const renderValue = (v) => {
|
|
275
294
|
if (typeof v === "string") return v;
|
|
276
295
|
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") return String(v);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ayepi/log",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Structured logging for ayepi — AsyncLocalStorage trace context, console interception, console/file transports, error serialization, middleware",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"node": ">=18"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@ayepi/core": "^0.
|
|
71
|
+
"@ayepi/core": "^0.5.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
74
|
"@ayepi/core": {
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"publint": "^0.3.0",
|
|
81
81
|
"tsdown": "^0.12.0",
|
|
82
82
|
"vitest": "^2.1.8",
|
|
83
|
-
"zod": "^4.
|
|
84
|
-
"@ayepi/core": "0.
|
|
83
|
+
"zod": "^4.6.7",
|
|
84
|
+
"@ayepi/core": "0.5.0"
|
|
85
85
|
},
|
|
86
86
|
"keywords": [
|
|
87
87
|
"ayepi",
|