@logtape/logtape 0.1.0-dev.10
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/LICENSE +20 -0
- package/README.md +194 -0
- package/esm/_dnt.shims.js +61 -0
- package/esm/config.js +108 -0
- package/esm/filter.js +42 -0
- package/esm/formatter.js +86 -0
- package/esm/logger.js +285 -0
- package/esm/mod.js +5 -0
- package/esm/package.json +3 -0
- package/esm/record.js +1 -0
- package/esm/sink.js +59 -0
- package/package.json +49 -0
- package/script/_dnt.shims.js +65 -0
- package/script/config.js +114 -0
- package/script/filter.js +47 -0
- package/script/formatter.js +91 -0
- package/script/logger.js +292 -0
- package/script/mod.js +15 -0
- package/script/package.json +3 -0
- package/script/record.js +2 -0
- package/script/sink.js +64 -0
- package/types/_dnt.shims.d.ts +10 -0
- package/types/_dnt.shims.d.ts.map +1 -0
- package/types/_dnt.test_shims.d.ts.map +1 -0
- package/types/config.d.ts +101 -0
- package/types/config.d.ts.map +1 -0
- package/types/config.test.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/_constants.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/_diff.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/_format.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_equals.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_false.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_greater_or_equal.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_is_error.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_less_or_equal.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_strict_equals.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assert_throws.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/assertion_error.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/assert/0.222.1/equal.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/async/0.222.1/delay.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/fmt/0.222.1/colors.d.ts.map +1 -0
- package/types/filter.d.ts +30 -0
- package/types/filter.d.ts.map +1 -0
- package/types/filter.test.d.ts.map +1 -0
- package/types/fixtures.d.ts.map +1 -0
- package/types/formatter.d.ts +38 -0
- package/types/formatter.d.ts.map +1 -0
- package/types/formatter.test.d.ts.map +1 -0
- package/types/logger.d.ts +363 -0
- package/types/logger.d.ts.map +1 -0
- package/types/logger.test.d.ts.map +1 -0
- package/types/mod.d.ts +7 -0
- package/types/mod.d.ts.map +1 -0
- package/types/record.d.ts +33 -0
- package/types/record.d.ts.map +1 -0
- package/types/sink.d.ts +55 -0
- package/types/sink.d.ts.map +1 -0
- package/types/sink.test.d.ts.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2024 Hong Minhee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<!-- deno-fmt-ignore-file -->
|
|
2
|
+
|
|
3
|
+
LogTape
|
|
4
|
+
=======
|
|
5
|
+
|
|
6
|
+
[![GitHub Actions][GitHub Actions badge]][GitHub Actions]
|
|
7
|
+
[![Codecov][Codecov badge]][Codecov]
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
> LogTape is still in the early stage of development. The API is not stable
|
|
11
|
+
> yet. Please be careful when using it in production.
|
|
12
|
+
|
|
13
|
+
LogTape is a simple logging library for Deno/Node.js/Bun/browsers. It is
|
|
14
|
+
designed to be used for both applications and libraries.
|
|
15
|
+
|
|
16
|
+
Currently, LogTape provides only few sinks, but you can easily add your own
|
|
17
|
+
sinks.
|
|
18
|
+
|
|
19
|
+
[GitHub Actions]: https://github.com/dahlia/logtape/actions/workflows/main.yaml
|
|
20
|
+
[GitHub Actions badge]: https://github.com/dahlia/logtape/actions/workflows/main.yaml/badge.svg
|
|
21
|
+
[Codecov]: https://codecov.io/gh/dahlia/logtape
|
|
22
|
+
[Codecov badge]: https://codecov.io/gh/dahlia/logtape/graph/badge.svg?token=yOejfcuX7r
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Installation
|
|
26
|
+
------------
|
|
27
|
+
|
|
28
|
+
### Deno
|
|
29
|
+
|
|
30
|
+
~~~~ sh
|
|
31
|
+
deno add @logtape/logtape
|
|
32
|
+
~~~~
|
|
33
|
+
|
|
34
|
+
### Node.js
|
|
35
|
+
|
|
36
|
+
~~~~ sh
|
|
37
|
+
npm add @logtape/logtape
|
|
38
|
+
~~~~
|
|
39
|
+
|
|
40
|
+
### Bun
|
|
41
|
+
|
|
42
|
+
~~~~ sh
|
|
43
|
+
bun add @logtape/logtape
|
|
44
|
+
~~~~
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Quick start
|
|
48
|
+
-----------
|
|
49
|
+
|
|
50
|
+
Set up LogTape in the entry point of your application (if you are composing
|
|
51
|
+
a library, you should not set up LogTape in the library itself; it is up to
|
|
52
|
+
the application to set up LogTape):
|
|
53
|
+
|
|
54
|
+
~~~~ typescript
|
|
55
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
56
|
+
|
|
57
|
+
configure({
|
|
58
|
+
sinks: { console: getConsoleSink() },
|
|
59
|
+
filters: {},
|
|
60
|
+
loggers: [
|
|
61
|
+
{ category: "my-app", level: "debug", sinks: ["console"] }
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
~~~~
|
|
65
|
+
|
|
66
|
+
And then you can use LogTape in your application or library:
|
|
67
|
+
|
|
68
|
+
~~~~ typescript
|
|
69
|
+
import { getLogger } from "@logtape/logtape";
|
|
70
|
+
|
|
71
|
+
const logger = getLogger(["my-app", "my-module"]);
|
|
72
|
+
|
|
73
|
+
export function myFunc(value: number): void {
|
|
74
|
+
logger.debug `Hello, ${value}!`;
|
|
75
|
+
}
|
|
76
|
+
~~~~
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
How to log
|
|
80
|
+
----------
|
|
81
|
+
|
|
82
|
+
There are total 5 log levels: `debug`, `info`, `warning`, `error`, `fatal` (in
|
|
83
|
+
the order of verbosity). You can log messages with the following syntax:
|
|
84
|
+
|
|
85
|
+
~~~~ typescript
|
|
86
|
+
logger.debug `This is a debug message with ${value}.`;
|
|
87
|
+
logger.info `This is an info message with ${value}.`;
|
|
88
|
+
logger.warn `This is a warning message with ${value}.`;
|
|
89
|
+
logger.error `This is an error message with ${value}.`;
|
|
90
|
+
logger.fatal `This is a fatal message with ${value}.`;
|
|
91
|
+
~~~~
|
|
92
|
+
|
|
93
|
+
You can also log messages with a function call:
|
|
94
|
+
|
|
95
|
+
~~~~ typescript
|
|
96
|
+
logger.debug("This is a debug message with {value}.", { value });
|
|
97
|
+
logger.info("This is an info message with {value}.", { value });
|
|
98
|
+
logger.warn("This is a warning message with {value}.", { value });
|
|
99
|
+
logger.error("This is an error message with {value}.", { value });
|
|
100
|
+
logger.fatal("This is a fatal message with {value}.", { value });
|
|
101
|
+
~~~~
|
|
102
|
+
|
|
103
|
+
Sometimes, values to be logged are expensive to compute. In such cases, you
|
|
104
|
+
can use a function to defer the computation so that it is only computed when
|
|
105
|
+
the log message is actually logged:
|
|
106
|
+
|
|
107
|
+
~~~~ typescript
|
|
108
|
+
logger.debug(l => l`This is a debug message with ${computeValue()}.`);
|
|
109
|
+
logger.debug("Or you can use a function call: {value}.", () => {
|
|
110
|
+
return { value: computeValue() };
|
|
111
|
+
});
|
|
112
|
+
~~~~
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
Categories
|
|
116
|
+
----------
|
|
117
|
+
|
|
118
|
+
LogTape uses a hierarchical category system to manage loggers. A category is
|
|
119
|
+
a list of strings. For example, `["my-app", "my-module"]` is a category.
|
|
120
|
+
|
|
121
|
+
When you log a message, it is dispatched to all loggers whose categories are
|
|
122
|
+
prefixes of the category of the logger. For example, if you log a message
|
|
123
|
+
with the category `["my-app", "my-module", "my-submodule"]`, it is dispatched
|
|
124
|
+
to loggers whose categories are `["my-app", "my-module"]` and `["my-app"]`.
|
|
125
|
+
|
|
126
|
+
This behavior allows you to control the verbosity of log messages by setting
|
|
127
|
+
the log level of loggers at different levels of the category hierarchy.
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
Sinks
|
|
131
|
+
-----
|
|
132
|
+
|
|
133
|
+
A sink is a destination of log messages. LogTape currently provides a few
|
|
134
|
+
sinks: console and stream. However, you can easily add your own sinks.
|
|
135
|
+
The signature of a sink is:
|
|
136
|
+
|
|
137
|
+
~~~~ typescript
|
|
138
|
+
export type Sink = (record: LogRecord) => void;
|
|
139
|
+
~~~~
|
|
140
|
+
|
|
141
|
+
Here's a simple example of a sink that writes log messages to console:
|
|
142
|
+
|
|
143
|
+
~~~~ typescript
|
|
144
|
+
import { configure } from "@logtape/logtape";
|
|
145
|
+
|
|
146
|
+
configure({
|
|
147
|
+
sinks: {
|
|
148
|
+
console(record) {
|
|
149
|
+
console.log(record.message);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
// Omitted for brevity
|
|
153
|
+
});
|
|
154
|
+
~~~~
|
|
155
|
+
|
|
156
|
+
Of course, you don't have to implement your own console sink because LogTape
|
|
157
|
+
provides a console sink:
|
|
158
|
+
|
|
159
|
+
~~~~ typescript
|
|
160
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
161
|
+
|
|
162
|
+
configure({
|
|
163
|
+
sinks: {
|
|
164
|
+
console: getConsoleSink(),
|
|
165
|
+
},
|
|
166
|
+
// Omitted for brevity
|
|
167
|
+
});
|
|
168
|
+
~~~~
|
|
169
|
+
|
|
170
|
+
Another built-in sink is a stream sink. It writes log messages to
|
|
171
|
+
a [`WritableStream`]. Here's an example of a stream sink that writes log
|
|
172
|
+
messages to the standard error:
|
|
173
|
+
|
|
174
|
+
~~~~ typescript
|
|
175
|
+
// Deno:
|
|
176
|
+
const stderrSink = getStreamSink(Deno.stderr.writable);
|
|
177
|
+
~~~~
|
|
178
|
+
|
|
179
|
+
~~~~ typescript
|
|
180
|
+
// Node.js:
|
|
181
|
+
import stream from "node:stream";
|
|
182
|
+
const stderrSink = getStreamSink(stream.Writable.toWeb(process.stderr));
|
|
183
|
+
~~~~
|
|
184
|
+
|
|
185
|
+
> [!NOTE]
|
|
186
|
+
> Here we use `WritableStream` from the Web Streams API. If you are using
|
|
187
|
+
> Node.js, you cannot directly pass `process.stderr` to `getStreamSink` because
|
|
188
|
+
> `process.stderr` is not a `WritableStream` but a [`Writable`], which is a
|
|
189
|
+
> Node.js stream. You can use [`Writable.toWeb()`] method to convert a Node.js
|
|
190
|
+
> stream to a `WritableStream`.
|
|
191
|
+
|
|
192
|
+
[`WritableStream`]: https://developer.mozilla.org/en-US/docs/Web/API/WritableStream
|
|
193
|
+
[`Writable`]: https://nodejs.org/api/stream.html#class-streamwritable
|
|
194
|
+
[`Writable.toWeb()`]: https://nodejs.org/api/stream.html#streamwritabletowebstreamwritable
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { WritableStream } from "node:stream/web";
|
|
2
|
+
export { WritableStream } from "node:stream/web";
|
|
3
|
+
const dntGlobals = {
|
|
4
|
+
WritableStream,
|
|
5
|
+
};
|
|
6
|
+
export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
7
|
+
function createMergeProxy(baseObj, extObj) {
|
|
8
|
+
return new Proxy(baseObj, {
|
|
9
|
+
get(_target, prop, _receiver) {
|
|
10
|
+
if (prop in extObj) {
|
|
11
|
+
return extObj[prop];
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return baseObj[prop];
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
set(_target, prop, value) {
|
|
18
|
+
if (prop in extObj) {
|
|
19
|
+
delete extObj[prop];
|
|
20
|
+
}
|
|
21
|
+
baseObj[prop] = value;
|
|
22
|
+
return true;
|
|
23
|
+
},
|
|
24
|
+
deleteProperty(_target, prop) {
|
|
25
|
+
let success = false;
|
|
26
|
+
if (prop in extObj) {
|
|
27
|
+
delete extObj[prop];
|
|
28
|
+
success = true;
|
|
29
|
+
}
|
|
30
|
+
if (prop in baseObj) {
|
|
31
|
+
delete baseObj[prop];
|
|
32
|
+
success = true;
|
|
33
|
+
}
|
|
34
|
+
return success;
|
|
35
|
+
},
|
|
36
|
+
ownKeys(_target) {
|
|
37
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
38
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
39
|
+
const extKeysSet = new Set(extKeys);
|
|
40
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
41
|
+
},
|
|
42
|
+
defineProperty(_target, prop, desc) {
|
|
43
|
+
if (prop in extObj) {
|
|
44
|
+
delete extObj[prop];
|
|
45
|
+
}
|
|
46
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
47
|
+
return true;
|
|
48
|
+
},
|
|
49
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
50
|
+
if (prop in extObj) {
|
|
51
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
has(_target, prop) {
|
|
58
|
+
return prop in extObj || prop in baseObj;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
package/esm/config.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { toFilter } from "./filter.js";
|
|
2
|
+
import { LoggerImpl } from "./logger.js";
|
|
3
|
+
import { getConsoleSink } from "./sink.js";
|
|
4
|
+
let configured = false;
|
|
5
|
+
/**
|
|
6
|
+
* Configure the loggers with the specified configuration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* configure({
|
|
11
|
+
* sinks: {
|
|
12
|
+
* console: getConsoleSink(),
|
|
13
|
+
* },
|
|
14
|
+
* filters: {
|
|
15
|
+
* slow: (log) =>
|
|
16
|
+
* "duration" in log.properties &&
|
|
17
|
+
* log.properties.duration as number > 1000,
|
|
18
|
+
* },
|
|
19
|
+
* loggers: [
|
|
20
|
+
* {
|
|
21
|
+
* category: "my-app",
|
|
22
|
+
* sinks: ["console"],
|
|
23
|
+
* level: "info",
|
|
24
|
+
* },
|
|
25
|
+
* {
|
|
26
|
+
* category: ["my-app", "sql"],
|
|
27
|
+
* filters: ["slow"],
|
|
28
|
+
* level: "debug",
|
|
29
|
+
* },
|
|
30
|
+
* {
|
|
31
|
+
* category: "logtape",
|
|
32
|
+
* sinks: ["console"],
|
|
33
|
+
* level: "error",
|
|
34
|
+
* },
|
|
35
|
+
* ],
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param config The configuration.
|
|
40
|
+
*/
|
|
41
|
+
export function configure(config) {
|
|
42
|
+
if (configured && !config.reset) {
|
|
43
|
+
throw new ConfigError("Already configured; if you want to reset, turn on the reset flag.");
|
|
44
|
+
}
|
|
45
|
+
configured = true;
|
|
46
|
+
LoggerImpl.getLogger([]).resetDescendants();
|
|
47
|
+
let metaConfigured = false;
|
|
48
|
+
for (const cfg of config.loggers) {
|
|
49
|
+
if (cfg.category.length === 0 ||
|
|
50
|
+
(cfg.category.length === 1 && cfg.category[0] === "logtape") ||
|
|
51
|
+
(cfg.category.length === 2 &&
|
|
52
|
+
cfg.category[0] === "logtape" &&
|
|
53
|
+
cfg.category[1] === "meta")) {
|
|
54
|
+
metaConfigured = true;
|
|
55
|
+
}
|
|
56
|
+
const logger = LoggerImpl.getLogger(cfg.category);
|
|
57
|
+
for (const sinkId of cfg.sinks ?? []) {
|
|
58
|
+
const sink = config.sinks[sinkId];
|
|
59
|
+
if (!sink) {
|
|
60
|
+
reset();
|
|
61
|
+
throw new ConfigError(`Sink not found: ${sinkId}.`);
|
|
62
|
+
}
|
|
63
|
+
logger.sinks.push(sink);
|
|
64
|
+
}
|
|
65
|
+
if (cfg.level !== undefined)
|
|
66
|
+
logger.filters.push(toFilter(cfg.level));
|
|
67
|
+
for (const filterId of cfg.filters ?? []) {
|
|
68
|
+
const filter = config.filters[filterId];
|
|
69
|
+
if (filter === undefined) {
|
|
70
|
+
reset();
|
|
71
|
+
throw new ConfigError(`Filter not found: ${filterId}.`);
|
|
72
|
+
}
|
|
73
|
+
logger.filters.push(toFilter(filter));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const meta = LoggerImpl.getLogger(["logtape", "meta"]);
|
|
77
|
+
if (!metaConfigured) {
|
|
78
|
+
meta.sinks.push(getConsoleSink());
|
|
79
|
+
}
|
|
80
|
+
meta.info("LogTape loggers are configured. Note that LogTape itself uses the meta " +
|
|
81
|
+
"logger, which has category {metaLoggerCategory}. The meta logger " +
|
|
82
|
+
"purposes to log internal errors such as sink exceptions. If you " +
|
|
83
|
+
"are seeing this message, the meta logger is somehow configured. " +
|
|
84
|
+
"It's recommended to configure the meta logger with a separate sink " +
|
|
85
|
+
"so that you can easily notice if logging itself fails or is " +
|
|
86
|
+
"misconfigured. To turn off this message, configure the meta logger " +
|
|
87
|
+
"with higher log levels than {dismissLevel}.", { metaLoggerCategory: ["logtape", "meta"], dismissLevel: "info" });
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Reset the configuration. Mostly for testing purposes.
|
|
91
|
+
*/
|
|
92
|
+
export function reset() {
|
|
93
|
+
LoggerImpl.getLogger([]).resetDescendants();
|
|
94
|
+
configured = false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A configuration error.
|
|
98
|
+
*/
|
|
99
|
+
export class ConfigError extends Error {
|
|
100
|
+
/**
|
|
101
|
+
* Constructs a new configuration error.
|
|
102
|
+
* @param message The error message.
|
|
103
|
+
*/
|
|
104
|
+
constructor(message) {
|
|
105
|
+
super(message);
|
|
106
|
+
this.name = "ConfigureError";
|
|
107
|
+
}
|
|
108
|
+
}
|
package/esm/filter.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a {@link FilterLike} value to an actual {@link Filter}.
|
|
3
|
+
*
|
|
4
|
+
* @param filter The filter-like value to convert.
|
|
5
|
+
* @returns The actual filter.
|
|
6
|
+
*/
|
|
7
|
+
export function toFilter(filter) {
|
|
8
|
+
if (typeof filter === "function")
|
|
9
|
+
return filter;
|
|
10
|
+
return getLevelFilter(filter);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Returns a filter that accepts log records with the specified level.
|
|
14
|
+
*
|
|
15
|
+
* @param level The level to filter by. If `null`, the filter will reject all
|
|
16
|
+
* records.
|
|
17
|
+
* @returns The filter.
|
|
18
|
+
*/
|
|
19
|
+
export function getLevelFilter(level) {
|
|
20
|
+
if (level == null)
|
|
21
|
+
return () => false;
|
|
22
|
+
if (level === "fatal") {
|
|
23
|
+
return (record) => record.level === "fatal";
|
|
24
|
+
}
|
|
25
|
+
else if (level === "error") {
|
|
26
|
+
return (record) => record.level === "fatal" || record.level === "error";
|
|
27
|
+
}
|
|
28
|
+
else if (level === "warning") {
|
|
29
|
+
return (record) => record.level === "fatal" ||
|
|
30
|
+
record.level === "error" ||
|
|
31
|
+
record.level === "warning";
|
|
32
|
+
}
|
|
33
|
+
else if (level === "info") {
|
|
34
|
+
return (record) => record.level === "fatal" ||
|
|
35
|
+
record.level === "error" ||
|
|
36
|
+
record.level === "warning" ||
|
|
37
|
+
record.level === "info";
|
|
38
|
+
}
|
|
39
|
+
else if (level === "debug")
|
|
40
|
+
return () => true;
|
|
41
|
+
throw new TypeError(`Invalid log level: ${level}.`);
|
|
42
|
+
}
|
package/esm/formatter.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The severity level abbreviations.
|
|
3
|
+
*/
|
|
4
|
+
const levelAbbreviations = {
|
|
5
|
+
"debug": "DBG",
|
|
6
|
+
"info": "INF",
|
|
7
|
+
"warning": "WRN",
|
|
8
|
+
"error": "ERR",
|
|
9
|
+
"fatal": "FTL",
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A platform-specific inspect function. In Deno, this is {@link Deno.inspect},
|
|
13
|
+
* and in Node.js/Bun it is {@link util.inspect}. If neither is available, it
|
|
14
|
+
* falls back to {@link JSON.stringify}.
|
|
15
|
+
*
|
|
16
|
+
* @param value The value to inspect.
|
|
17
|
+
* @returns The string representation of the value.
|
|
18
|
+
*/
|
|
19
|
+
const inspect = eval(`(
|
|
20
|
+
"Deno" in globalThis && "inspect" in globalThis.Deno &&
|
|
21
|
+
typeof globalThis.Deno.inspect === "function"
|
|
22
|
+
? globalThis.Deno.inspect
|
|
23
|
+
: "util" in globalThis && "inspect" in globalThis.util &&
|
|
24
|
+
globalThis.util.inspect === "function"
|
|
25
|
+
? globalThis.util.inspect
|
|
26
|
+
: JSON.stringify
|
|
27
|
+
)`);
|
|
28
|
+
/**
|
|
29
|
+
* The default text formatter. This formatter formats log records as follows:
|
|
30
|
+
*
|
|
31
|
+
* ```
|
|
32
|
+
* 2023-11-14 22:13:20.000 +00:00 [INF] category·subcategory: Hello, world!
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @param record The log record to format.
|
|
36
|
+
* @returns The formatted log record.
|
|
37
|
+
*/
|
|
38
|
+
export function defaultTextFormatter(record) {
|
|
39
|
+
const ts = new Date(record.timestamp);
|
|
40
|
+
let msg = "";
|
|
41
|
+
for (let i = 0; i < record.message.length; i++) {
|
|
42
|
+
if (i % 2 === 0)
|
|
43
|
+
msg += record.message[i];
|
|
44
|
+
else
|
|
45
|
+
msg += inspect(record.message[i]);
|
|
46
|
+
}
|
|
47
|
+
const category = record.category.join("\xb7");
|
|
48
|
+
return `${ts.toISOString().replace("T", " ").replace("Z", " +00:00")} [${levelAbbreviations[record.level]}] ${category}: ${msg}\n`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The styles for the log level in the console.
|
|
52
|
+
*/
|
|
53
|
+
const logLevelStyles = {
|
|
54
|
+
"debug": "background-color: gray; color: white;",
|
|
55
|
+
"info": "background-color: white; color: black;",
|
|
56
|
+
"warning": "background-color: orange;",
|
|
57
|
+
"error": "background-color: red;",
|
|
58
|
+
"fatal": "background-color: maroon;",
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The default console formatter.
|
|
62
|
+
*
|
|
63
|
+
* @param record The log record to format.
|
|
64
|
+
* @returns The formatted log record, as an array of arguments for
|
|
65
|
+
* {@link console.log}.
|
|
66
|
+
*/
|
|
67
|
+
export function defaultConsoleFormatter(record) {
|
|
68
|
+
let msg = "";
|
|
69
|
+
const values = [];
|
|
70
|
+
for (let i = 0; i < record.message.length; i++) {
|
|
71
|
+
if (i % 2 === 0)
|
|
72
|
+
msg += record.message[i];
|
|
73
|
+
else {
|
|
74
|
+
msg += "%o";
|
|
75
|
+
values.push(record.message[i]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [
|
|
79
|
+
`%c${record.level.toUpperCase()}%c %c${record.category.join("\xb7")} %c${msg}`,
|
|
80
|
+
logLevelStyles[record.level],
|
|
81
|
+
"background-color: default;",
|
|
82
|
+
"color: gray;",
|
|
83
|
+
"color: default;",
|
|
84
|
+
...values,
|
|
85
|
+
];
|
|
86
|
+
}
|