@decohere/scribe-console 0.0.1-dev.125 → 0.0.1-dev.5
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/package.json +2 -11
- package/src/json-console.adapter.ts +97 -0
- package/tests/json-console.adapter.integration.spec.ts +156 -0
- package/LICENSE +0 -15
- package/commonjs/src/index.js +0 -18
- package/commonjs/src/json-console.adapter.js +0 -85
- package/es6/src/json-console.adapter.js +0 -80
- package/types/src/index.d.ts +0 -1
- package/types/src/json-console.adapter.d.ts +0 -19
- /package/{es6/src/index.js → src/index.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decohere/scribe-console",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.5",
|
|
4
4
|
"description": "JSON console adapter for @decohere/scribe: one JSON line per entry, for Workers, browsers, Deno, Bun and Node",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -10,15 +10,6 @@
|
|
|
10
10
|
"cloudflare-workers",
|
|
11
11
|
"decohere"
|
|
12
12
|
],
|
|
13
|
-
"homepage": "https://github.com/typesmiths/decohere/tree/main/projects/scribe/scribe-console#readme",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/typesmiths/decohere/issues"
|
|
16
|
-
},
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/typesmiths/decohere.git",
|
|
20
|
-
"directory": "projects/scribe/scribe-console"
|
|
21
|
-
},
|
|
22
13
|
"license": "ISC",
|
|
23
14
|
"author": "Typesmiths",
|
|
24
15
|
"type": "module",
|
|
@@ -34,7 +25,7 @@
|
|
|
34
25
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
35
26
|
},
|
|
36
27
|
"dependencies": {
|
|
37
|
-
"@decohere/scribe": "0.0.1-dev.
|
|
28
|
+
"@decohere/scribe": "0.0.1-dev.5"
|
|
38
29
|
},
|
|
39
30
|
"devDependencies": {}
|
|
40
31
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type LogEntry, LogEntryAdapter, LogLevels, serializeError } from "@decohere/scribe";
|
|
2
|
+
|
|
3
|
+
export type JsonConsoleAdapterOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Add `time` (ISO 8601) to every line. Off by default: hosts such as Workers
|
|
6
|
+
* Logs and browser devtools stamp each line themselves.
|
|
7
|
+
*/
|
|
8
|
+
timestamp?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type ConsoleMethod = "error" | "warn" | "info" | "debug";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Writes one JSON object per entry, `{ level, name, msg, ...fields, err }`,
|
|
15
|
+
* through the console method of its level, so hosts that index JSON console
|
|
16
|
+
* lines (Workers Logs, browser devtools) see the level too. Uses nothing but
|
|
17
|
+
* `console` and `JSON`: it runs on Workers, in browsers, Deno, Bun and Node.
|
|
18
|
+
*/
|
|
19
|
+
export class JsonConsoleAdapter extends LogEntryAdapter {
|
|
20
|
+
private readonly timestamp: boolean;
|
|
21
|
+
|
|
22
|
+
public constructor(options: JsonConsoleAdapterOptions = {}) {
|
|
23
|
+
super();
|
|
24
|
+
this.timestamp = options.timestamp ?? false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected write(entry: LogEntry): void {
|
|
28
|
+
const method = methodFor(this.levels[entry.level] ?? LogLevels.info);
|
|
29
|
+
console[method](stringify(toLine(entry, this.timestamp)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Custom levels pick the method of the default level at or below them:
|
|
35
|
+
* `fatal` and `error` → `console.error`, `warn` → `console.warn`, `info` →
|
|
36
|
+
* `console.info`, `debug` and `trace` → `console.debug` (`console.trace` would
|
|
37
|
+
* print a stack).
|
|
38
|
+
*/
|
|
39
|
+
function methodFor(value: number): ConsoleMethod {
|
|
40
|
+
if (value >= LogLevels.error) {
|
|
41
|
+
return "error";
|
|
42
|
+
}
|
|
43
|
+
if (value >= LogLevels.warn) {
|
|
44
|
+
return "warn";
|
|
45
|
+
}
|
|
46
|
+
if (value >= LogLevels.info) {
|
|
47
|
+
return "info";
|
|
48
|
+
}
|
|
49
|
+
return "debug";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `level`, `name`, `time` and `msg` are the entry's own; a field cannot replace them. */
|
|
53
|
+
function toLine(entry: LogEntry, timestamp: boolean): Record<string, unknown> {
|
|
54
|
+
const line: Record<string, unknown> = { level: entry.level, name: entry.name };
|
|
55
|
+
if (timestamp) {
|
|
56
|
+
line.time = new Date().toISOString();
|
|
57
|
+
}
|
|
58
|
+
if (entry.msg !== undefined) {
|
|
59
|
+
line.msg = entry.msg;
|
|
60
|
+
}
|
|
61
|
+
for (const [key, value] of Object.entries(entry.fields)) {
|
|
62
|
+
if (!(key in line)) {
|
|
63
|
+
line[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (entry.err != null) {
|
|
67
|
+
line.err = entry.err;
|
|
68
|
+
}
|
|
69
|
+
return line;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `JSON.stringify` that survives cycles and bigints, and serializes nested errors. */
|
|
73
|
+
function stringify(line: Record<string, unknown>): string {
|
|
74
|
+
const ancestors: object[] = [];
|
|
75
|
+
return JSON.stringify(line, function (this: unknown, _key: string, value: unknown) {
|
|
76
|
+
if (typeof value === "bigint") {
|
|
77
|
+
return value.toString();
|
|
78
|
+
}
|
|
79
|
+
if (value == null || typeof value !== "object") {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
while (ancestors.length > 0 && ancestors.at(-1) !== this) {
|
|
83
|
+
ancestors.pop();
|
|
84
|
+
}
|
|
85
|
+
if (ancestors.includes(value)) {
|
|
86
|
+
return "[Circular]";
|
|
87
|
+
}
|
|
88
|
+
if (value instanceof Error) {
|
|
89
|
+
// Track the error too: its serialized properties may point back at it.
|
|
90
|
+
const serialized = serializeError(value);
|
|
91
|
+
ancestors.push(value, serialized);
|
|
92
|
+
return serialized;
|
|
93
|
+
}
|
|
94
|
+
ancestors.push(value);
|
|
95
|
+
return value;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON console adapter through the public API (`scribe.configure` / `scribe.logger`):
|
|
3
|
+
* one JSON line per entry, through the console method of its level.
|
|
4
|
+
*/
|
|
5
|
+
import { scribe } from "@decohere/scribe";
|
|
6
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { JsonConsoleAdapter } from "../src/json-console.adapter.js";
|
|
8
|
+
|
|
9
|
+
afterEach(async () => {
|
|
10
|
+
vi.unstubAllGlobals();
|
|
11
|
+
vi.restoreAllMocks();
|
|
12
|
+
await scribe.stop();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
type Method = "error" | "warn" | "info" | "debug" | "log";
|
|
16
|
+
|
|
17
|
+
/** Lines per console method, parsed. */
|
|
18
|
+
function captureConsole(): Record<Method, Record<string, unknown>[]> {
|
|
19
|
+
const lines: Record<Method, Record<string, unknown>[]> = {
|
|
20
|
+
error: [],
|
|
21
|
+
warn: [],
|
|
22
|
+
info: [],
|
|
23
|
+
debug: [],
|
|
24
|
+
log: [],
|
|
25
|
+
};
|
|
26
|
+
for (const method of Object.keys(lines) as Method[]) {
|
|
27
|
+
vi.spyOn(console, method).mockImplementation((line: string) => {
|
|
28
|
+
lines[method].push(JSON.parse(line) as Record<string, unknown>);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("JsonConsoleAdapter", () => {
|
|
35
|
+
it("writes { level, name, msg, ...fields } through the method of each level", () => {
|
|
36
|
+
const lines = captureConsole();
|
|
37
|
+
scribe.configure({ adapter: new JsonConsoleAdapter(), options: { logLevel: "trace" } });
|
|
38
|
+
const log = scribe.logger("Worker");
|
|
39
|
+
log.fatal("down");
|
|
40
|
+
log.error("failed");
|
|
41
|
+
log.warn("slow", { ms: 900 });
|
|
42
|
+
log.info("ready");
|
|
43
|
+
log.debug("cache miss");
|
|
44
|
+
log.trace("step");
|
|
45
|
+
|
|
46
|
+
expect(lines.error).toEqual([
|
|
47
|
+
{ level: "fatal", name: "Worker", msg: "down" },
|
|
48
|
+
{ level: "error", name: "Worker", msg: "failed" },
|
|
49
|
+
]);
|
|
50
|
+
expect(lines.warn).toEqual([{ level: "warn", name: "Worker", msg: "slow", ms: 900 }]);
|
|
51
|
+
expect(lines.info).toEqual([{ level: "info", name: "Worker", msg: "ready" }]);
|
|
52
|
+
expect(lines.debug).toEqual([
|
|
53
|
+
{ level: "debug", name: "Worker", msg: "cache miss" },
|
|
54
|
+
{ level: "trace", name: "Worker", msg: "step" },
|
|
55
|
+
]);
|
|
56
|
+
expect(lines.log).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("filters below the configured level", () => {
|
|
60
|
+
const lines = captureConsole();
|
|
61
|
+
scribe.configure({ adapter: new JsonConsoleAdapter(), options: { logLevel: "warn" } });
|
|
62
|
+
scribe.logger("Worker").info("hidden");
|
|
63
|
+
expect(lines.info).toEqual([]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("routes custom levels by their value", () => {
|
|
67
|
+
const lines = captureConsole();
|
|
68
|
+
scribe.configure({
|
|
69
|
+
adapter: new JsonConsoleAdapter(),
|
|
70
|
+
options: { logLevel: "trace", customLevels: { audit: 45, metric: 25 } },
|
|
71
|
+
});
|
|
72
|
+
const log = scribe.logger<"audit" | "metric">("Billing");
|
|
73
|
+
log.audit("sealed");
|
|
74
|
+
log.metric("latency", { ms: 12 });
|
|
75
|
+
expect(lines.warn).toEqual([{ level: "audit", name: "Billing", msg: "sealed" }]);
|
|
76
|
+
expect(lines.debug).toEqual([
|
|
77
|
+
{ level: "metric", name: "Billing", msg: "latency", ms: 12 },
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("writes errors with their causes, and child fields", () => {
|
|
82
|
+
const lines = captureConsole();
|
|
83
|
+
scribe.configure({ adapter: new JsonConsoleAdapter() });
|
|
84
|
+
const err = new Error("fetch failed", { cause: new Error("connection reset") });
|
|
85
|
+
scribe.logger("Http").child({ requestId: "r-1" }).error("upstream failed", err);
|
|
86
|
+
|
|
87
|
+
expect(lines.error).toEqual([
|
|
88
|
+
{
|
|
89
|
+
level: "error",
|
|
90
|
+
name: "Http",
|
|
91
|
+
msg: "upstream failed",
|
|
92
|
+
requestId: "r-1",
|
|
93
|
+
err: {
|
|
94
|
+
name: "Error",
|
|
95
|
+
message: "fetch failed",
|
|
96
|
+
stack: expect.stringContaining("Error: fetch failed"),
|
|
97
|
+
cause: {
|
|
98
|
+
name: "Error",
|
|
99
|
+
message: "connection reset",
|
|
100
|
+
stack: expect.stringContaining("Error: connection reset"),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("keeps level and name from fields, and survives cycles and bigints", () => {
|
|
108
|
+
const lines = captureConsole();
|
|
109
|
+
scribe.configure({ adapter: new JsonConsoleAdapter() });
|
|
110
|
+
const loop: Record<string, unknown> = { id: 1 };
|
|
111
|
+
loop.self = loop;
|
|
112
|
+
const err = Object.assign(new Error("odd"), { self: undefined as unknown });
|
|
113
|
+
err.self = err;
|
|
114
|
+
scribe.logger("Odd").info("probe", { level: "fake", name: "fake", loop, big: 10n, err });
|
|
115
|
+
|
|
116
|
+
expect(lines.info).toEqual([
|
|
117
|
+
{
|
|
118
|
+
level: "info",
|
|
119
|
+
name: "Odd",
|
|
120
|
+
msg: "probe",
|
|
121
|
+
loop: { id: 1, self: "[Circular]" },
|
|
122
|
+
big: "10",
|
|
123
|
+
err: expect.objectContaining({ message: "odd" }),
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("adds time when asked", () => {
|
|
129
|
+
const lines = captureConsole();
|
|
130
|
+
scribe.configure({ adapter: new JsonConsoleAdapter({ timestamp: true }) });
|
|
131
|
+
scribe.logger("Clock").info("tick");
|
|
132
|
+
expect(lines.info[0].time).toEqual(expect.stringMatching(/^\d{4}-\d{2}-\d{2}T/));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("censors the redaction keys", () => {
|
|
136
|
+
const lines = captureConsole();
|
|
137
|
+
scribe.configure({ adapter: new JsonConsoleAdapter() });
|
|
138
|
+
scribe.logger("Auth").info("login", { auth: { token: "t-659" } });
|
|
139
|
+
expect(lines.info).toEqual([
|
|
140
|
+
{ level: "info", name: "Auth", msg: "login", auth: { token: "[Redacted]" } },
|
|
141
|
+
]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/** #659 — the point of the package: no Node globals at all. */
|
|
145
|
+
it("runs where there is no process global", () => {
|
|
146
|
+
const lines = captureConsole();
|
|
147
|
+
vi.stubGlobal("process", undefined);
|
|
148
|
+
try {
|
|
149
|
+
scribe.configure({ adapter: new JsonConsoleAdapter() });
|
|
150
|
+
scribe.logger("Browser").info("ready");
|
|
151
|
+
} finally {
|
|
152
|
+
vi.unstubAllGlobals();
|
|
153
|
+
}
|
|
154
|
+
expect(lines.info).toEqual([{ level: "info", name: "Browser", msg: "ready" }]);
|
|
155
|
+
});
|
|
156
|
+
});
|
package/LICENSE
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
ISC License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-2026 Typesmiths
|
|
4
|
-
|
|
5
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
-
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
-
copyright notice and this permission notice appear in all copies.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
-
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/commonjs/src/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./json-console.adapter.js"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "JsonConsoleAdapter", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return JsonConsoleAdapter;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _scribe = require("@decohere/scribe");
|
|
12
|
-
class JsonConsoleAdapter extends _scribe.LogEntryAdapter {
|
|
13
|
-
timestamp;
|
|
14
|
-
constructor(options = {}){
|
|
15
|
-
super();
|
|
16
|
-
this.timestamp = options.timestamp ?? false;
|
|
17
|
-
}
|
|
18
|
-
write(entry) {
|
|
19
|
-
const method = methodFor(this.levels[entry.level] ?? _scribe.LogLevels.info);
|
|
20
|
-
console[method](stringify(toLine(entry, this.timestamp)));
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Custom levels pick the method of the default level at or below them:
|
|
25
|
-
* `fatal` and `error` → `console.error`, `warn` → `console.warn`, `info` →
|
|
26
|
-
* `console.info`, `debug` and `trace` → `console.debug` (`console.trace` would
|
|
27
|
-
* print a stack).
|
|
28
|
-
*/ function methodFor(value) {
|
|
29
|
-
if (value >= _scribe.LogLevels.error) {
|
|
30
|
-
return "error";
|
|
31
|
-
}
|
|
32
|
-
if (value >= _scribe.LogLevels.warn) {
|
|
33
|
-
return "warn";
|
|
34
|
-
}
|
|
35
|
-
if (value >= _scribe.LogLevels.info) {
|
|
36
|
-
return "info";
|
|
37
|
-
}
|
|
38
|
-
return "debug";
|
|
39
|
-
}
|
|
40
|
-
/** `level`, `name`, `time` and `msg` are the entry's own; a field cannot replace them. */ function toLine(entry, timestamp) {
|
|
41
|
-
const line = {
|
|
42
|
-
level: entry.level,
|
|
43
|
-
name: entry.name
|
|
44
|
-
};
|
|
45
|
-
if (timestamp) {
|
|
46
|
-
line.time = new Date().toISOString();
|
|
47
|
-
}
|
|
48
|
-
if (entry.msg !== undefined) {
|
|
49
|
-
line.msg = entry.msg;
|
|
50
|
-
}
|
|
51
|
-
for (const [key, value] of Object.entries(entry.fields)){
|
|
52
|
-
if (!(key in line)) {
|
|
53
|
-
line[key] = value;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (entry.err != null) {
|
|
57
|
-
line.err = entry.err;
|
|
58
|
-
}
|
|
59
|
-
return line;
|
|
60
|
-
}
|
|
61
|
-
/** `JSON.stringify` that survives cycles and bigints, and serializes nested errors. */ function stringify(line) {
|
|
62
|
-
const ancestors = [];
|
|
63
|
-
return JSON.stringify(line, function(_key, value) {
|
|
64
|
-
if (typeof value === "bigint") {
|
|
65
|
-
return value.toString();
|
|
66
|
-
}
|
|
67
|
-
if (value == null || typeof value !== "object") {
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
while(ancestors.length > 0 && ancestors.at(-1) !== this){
|
|
71
|
-
ancestors.pop();
|
|
72
|
-
}
|
|
73
|
-
if (ancestors.includes(value)) {
|
|
74
|
-
return "[Circular]";
|
|
75
|
-
}
|
|
76
|
-
if (value instanceof Error) {
|
|
77
|
-
// Track the error too: its serialized properties may point back at it.
|
|
78
|
-
const serialized = (0, _scribe.serializeError)(value);
|
|
79
|
-
ancestors.push(value, serialized);
|
|
80
|
-
return serialized;
|
|
81
|
-
}
|
|
82
|
-
ancestors.push(value);
|
|
83
|
-
return value;
|
|
84
|
-
});
|
|
85
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { LogEntryAdapter, LogLevels, serializeError } from "@decohere/scribe";
|
|
2
|
-
/**
|
|
3
|
-
* Writes one JSON object per entry, `{ level, name, msg, ...fields, err }`,
|
|
4
|
-
* through the console method of its level, so hosts that index JSON console
|
|
5
|
-
* lines (Workers Logs, browser devtools) see the level too. Uses nothing but
|
|
6
|
-
* `console` and `JSON`: it runs on Workers, in browsers, Deno, Bun and Node.
|
|
7
|
-
*/ export class JsonConsoleAdapter extends LogEntryAdapter {
|
|
8
|
-
timestamp;
|
|
9
|
-
constructor(options = {}){
|
|
10
|
-
super();
|
|
11
|
-
this.timestamp = options.timestamp ?? false;
|
|
12
|
-
}
|
|
13
|
-
write(entry) {
|
|
14
|
-
const method = methodFor(this.levels[entry.level] ?? LogLevels.info);
|
|
15
|
-
console[method](stringify(toLine(entry, this.timestamp)));
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Custom levels pick the method of the default level at or below them:
|
|
20
|
-
* `fatal` and `error` → `console.error`, `warn` → `console.warn`, `info` →
|
|
21
|
-
* `console.info`, `debug` and `trace` → `console.debug` (`console.trace` would
|
|
22
|
-
* print a stack).
|
|
23
|
-
*/ function methodFor(value) {
|
|
24
|
-
if (value >= LogLevels.error) {
|
|
25
|
-
return "error";
|
|
26
|
-
}
|
|
27
|
-
if (value >= LogLevels.warn) {
|
|
28
|
-
return "warn";
|
|
29
|
-
}
|
|
30
|
-
if (value >= LogLevels.info) {
|
|
31
|
-
return "info";
|
|
32
|
-
}
|
|
33
|
-
return "debug";
|
|
34
|
-
}
|
|
35
|
-
/** `level`, `name`, `time` and `msg` are the entry's own; a field cannot replace them. */ function toLine(entry, timestamp) {
|
|
36
|
-
const line = {
|
|
37
|
-
level: entry.level,
|
|
38
|
-
name: entry.name
|
|
39
|
-
};
|
|
40
|
-
if (timestamp) {
|
|
41
|
-
line.time = new Date().toISOString();
|
|
42
|
-
}
|
|
43
|
-
if (entry.msg !== undefined) {
|
|
44
|
-
line.msg = entry.msg;
|
|
45
|
-
}
|
|
46
|
-
for (const [key, value] of Object.entries(entry.fields)){
|
|
47
|
-
if (!(key in line)) {
|
|
48
|
-
line[key] = value;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (entry.err != null) {
|
|
52
|
-
line.err = entry.err;
|
|
53
|
-
}
|
|
54
|
-
return line;
|
|
55
|
-
}
|
|
56
|
-
/** `JSON.stringify` that survives cycles and bigints, and serializes nested errors. */ function stringify(line) {
|
|
57
|
-
const ancestors = [];
|
|
58
|
-
return JSON.stringify(line, function(_key, value) {
|
|
59
|
-
if (typeof value === "bigint") {
|
|
60
|
-
return value.toString();
|
|
61
|
-
}
|
|
62
|
-
if (value == null || typeof value !== "object") {
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
65
|
-
while(ancestors.length > 0 && ancestors.at(-1) !== this){
|
|
66
|
-
ancestors.pop();
|
|
67
|
-
}
|
|
68
|
-
if (ancestors.includes(value)) {
|
|
69
|
-
return "[Circular]";
|
|
70
|
-
}
|
|
71
|
-
if (value instanceof Error) {
|
|
72
|
-
// Track the error too: its serialized properties may point back at it.
|
|
73
|
-
const serialized = serializeError(value);
|
|
74
|
-
ancestors.push(value, serialized);
|
|
75
|
-
return serialized;
|
|
76
|
-
}
|
|
77
|
-
ancestors.push(value);
|
|
78
|
-
return value;
|
|
79
|
-
});
|
|
80
|
-
}
|
package/types/src/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./json-console.adapter.js";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type LogEntry, LogEntryAdapter } from "@decohere/scribe";
|
|
2
|
-
export type JsonConsoleAdapterOptions = {
|
|
3
|
-
/**
|
|
4
|
-
* Add `time` (ISO 8601) to every line. Off by default: hosts such as Workers
|
|
5
|
-
* Logs and browser devtools stamp each line themselves.
|
|
6
|
-
*/
|
|
7
|
-
timestamp?: boolean;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Writes one JSON object per entry, `{ level, name, msg, ...fields, err }`,
|
|
11
|
-
* through the console method of its level, so hosts that index JSON console
|
|
12
|
-
* lines (Workers Logs, browser devtools) see the level too. Uses nothing but
|
|
13
|
-
* `console` and `JSON`: it runs on Workers, in browsers, Deno, Bun and Node.
|
|
14
|
-
*/
|
|
15
|
-
export declare class JsonConsoleAdapter extends LogEntryAdapter {
|
|
16
|
-
private readonly timestamp;
|
|
17
|
-
constructor(options?: JsonConsoleAdapterOptions);
|
|
18
|
-
protected write(entry: LogEntry): void;
|
|
19
|
-
}
|
|
File without changes
|