@logtape/file 0.9.0-dev.1
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 +43 -0
- package/esm/_dnt.shims.js +57 -0
- package/esm/file/filesink.base.js +78 -0
- package/esm/file/filesink.node.js +47 -0
- package/esm/file/mod.js +1 -0
- package/esm/logtape/config.js +287 -0
- package/esm/logtape/context.js +23 -0
- package/esm/logtape/filter.js +42 -0
- package/esm/logtape/formatter.js +261 -0
- package/esm/logtape/level.js +59 -0
- package/esm/logtape/logger.js +480 -0
- package/esm/logtape/mod.js +8 -0
- package/esm/logtape/nodeUtil.js +2 -0
- package/esm/logtape/record.js +1 -0
- package/esm/logtape/sink.js +96 -0
- package/esm/package.json +3 -0
- package/package.json +55 -0
- package/script/_dnt.shims.js +60 -0
- package/script/file/filesink.base.js +82 -0
- package/script/file/filesink.node.js +55 -0
- package/script/file/mod.js +6 -0
- package/script/logtape/config.js +321 -0
- package/script/logtape/context.js +26 -0
- package/script/logtape/filter.js +46 -0
- package/script/logtape/formatter.js +270 -0
- package/script/logtape/level.js +64 -0
- package/script/logtape/logger.js +511 -0
- package/script/logtape/mod.js +34 -0
- package/script/logtape/nodeUtil.js +7 -0
- package/script/logtape/record.js +2 -0
- package/script/logtape/sink.js +101 -0
- package/script/package.json +3 -0
- package/types/_dnt.shims.d.ts +2 -0
- package/types/_dnt.shims.d.ts.map +1 -0
- package/types/_dnt.test_shims.d.ts.map +1 -0
- package/types/deps/jsr.io/@david/which-runtime/0.2.1/mod.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_equals.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/fmt/0.222.1/colors.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/_common/assert_path.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/_common/constants.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/_common/normalize.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/_common/normalize_string.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/_os.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/join.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/posix/_util.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/posix/join.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/posix/normalize.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/windows/_util.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/windows/join.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.8/windows/normalize.d.ts.map +1 -0
- package/types/file/filesink.base.d.ts +89 -0
- package/types/file/filesink.base.d.ts.map +1 -0
- package/types/file/filesink.node.d.ts +34 -0
- package/types/file/filesink.node.d.ts.map +1 -0
- package/types/file/filesink.test.d.ts.map +1 -0
- package/types/file/mod.d.ts +3 -0
- package/types/file/mod.d.ts.map +1 -0
- package/types/logtape/config.d.ts +183 -0
- package/types/logtape/config.d.ts.map +1 -0
- package/types/logtape/context.d.ts +35 -0
- package/types/logtape/context.d.ts.map +1 -0
- package/types/logtape/filter.d.ts +31 -0
- package/types/logtape/filter.d.ts.map +1 -0
- package/types/logtape/fixtures.d.ts.map +1 -0
- package/types/logtape/formatter.d.ts +260 -0
- package/types/logtape/formatter.d.ts.map +1 -0
- package/types/logtape/level.d.ts +32 -0
- package/types/logtape/level.d.ts.map +1 -0
- package/types/logtape/logger.d.ts +423 -0
- package/types/logtape/logger.d.ts.map +1 -0
- package/types/logtape/mod.d.ts +9 -0
- package/types/logtape/mod.d.ts.map +1 -0
- package/types/logtape/nodeUtil.d.ts +3 -0
- package/types/logtape/nodeUtil.d.ts.map +1 -0
- package/types/logtape/record.d.ts +44 -0
- package/types/logtape/record.d.ts.map +1 -0
- package/types/logtape/sink.d.ts +108 -0
- package/types/logtape/sink.d.ts.map +1 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import * as dntShim from "../_dnt.shims.js";
|
|
2
|
+
import { compareLogLevel } from "./level.js";
|
|
3
|
+
/**
|
|
4
|
+
* Get a logger with the given category.
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const logger = getLogger(["my-app"]);
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* @param category The category of the logger. It can be a string or an array
|
|
11
|
+
* of strings. If it is a string, it is equivalent to an array
|
|
12
|
+
* with a single element.
|
|
13
|
+
* @returns The logger.
|
|
14
|
+
*/
|
|
15
|
+
export function getLogger(category = []) {
|
|
16
|
+
return LoggerImpl.getLogger(category);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The symbol for the global root logger.
|
|
20
|
+
*/
|
|
21
|
+
const globalRootLoggerSymbol = Symbol.for("logtape.rootLogger");
|
|
22
|
+
/**
|
|
23
|
+
* A logger implementation. Do not use this directly; use {@link getLogger}
|
|
24
|
+
* instead. This class is exported for testing purposes.
|
|
25
|
+
*/
|
|
26
|
+
export class LoggerImpl {
|
|
27
|
+
static getLogger(category = []) {
|
|
28
|
+
let rootLogger = globalRootLoggerSymbol in dntShim.dntGlobalThis
|
|
29
|
+
? (dntShim.dntGlobalThis[globalRootLoggerSymbol] ??
|
|
30
|
+
null)
|
|
31
|
+
: null;
|
|
32
|
+
if (rootLogger == null) {
|
|
33
|
+
rootLogger = new LoggerImpl(null, []);
|
|
34
|
+
dntShim.dntGlobalThis[globalRootLoggerSymbol] =
|
|
35
|
+
rootLogger;
|
|
36
|
+
}
|
|
37
|
+
if (typeof category === "string")
|
|
38
|
+
return rootLogger.getChild(category);
|
|
39
|
+
if (category.length === 0)
|
|
40
|
+
return rootLogger;
|
|
41
|
+
return rootLogger.getChild(category);
|
|
42
|
+
}
|
|
43
|
+
constructor(parent, category) {
|
|
44
|
+
Object.defineProperty(this, "parent", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: void 0
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "children", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: void 0
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "category", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "sinks", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "parentSinks", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: "inherit"
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(this, "filters", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
value: void 0
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(this, "lowestLevel", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
value: "debug"
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(this, "contextLocalStorage", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
value: void 0
|
|
91
|
+
});
|
|
92
|
+
this.parent = parent;
|
|
93
|
+
this.children = {};
|
|
94
|
+
this.category = category;
|
|
95
|
+
this.sinks = [];
|
|
96
|
+
this.filters = [];
|
|
97
|
+
}
|
|
98
|
+
getChild(subcategory) {
|
|
99
|
+
const name = typeof subcategory === "string" ? subcategory : subcategory[0];
|
|
100
|
+
const childRef = this.children[name];
|
|
101
|
+
let child = childRef instanceof LoggerImpl
|
|
102
|
+
? childRef
|
|
103
|
+
: childRef?.deref();
|
|
104
|
+
if (child == null) {
|
|
105
|
+
child = new LoggerImpl(this, [...this.category, name]);
|
|
106
|
+
this.children[name] = "WeakRef" in dntShim.dntGlobalThis
|
|
107
|
+
? new WeakRef(child)
|
|
108
|
+
: child;
|
|
109
|
+
}
|
|
110
|
+
if (typeof subcategory === "string" || subcategory.length === 1) {
|
|
111
|
+
return child;
|
|
112
|
+
}
|
|
113
|
+
return child.getChild(subcategory.slice(1));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Reset the logger. This removes all sinks and filters from the logger.
|
|
117
|
+
*/
|
|
118
|
+
reset() {
|
|
119
|
+
while (this.sinks.length > 0)
|
|
120
|
+
this.sinks.shift();
|
|
121
|
+
this.parentSinks = "inherit";
|
|
122
|
+
while (this.filters.length > 0)
|
|
123
|
+
this.filters.shift();
|
|
124
|
+
this.lowestLevel = "debug";
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Reset the logger and all its descendants. This removes all sinks and
|
|
128
|
+
* filters from the logger and all its descendants.
|
|
129
|
+
*/
|
|
130
|
+
resetDescendants() {
|
|
131
|
+
for (const child of Object.values(this.children)) {
|
|
132
|
+
const logger = child instanceof LoggerImpl ? child : child.deref();
|
|
133
|
+
if (logger != null)
|
|
134
|
+
logger.resetDescendants();
|
|
135
|
+
}
|
|
136
|
+
this.reset();
|
|
137
|
+
}
|
|
138
|
+
with(properties) {
|
|
139
|
+
return new LoggerCtx(this, { ...properties });
|
|
140
|
+
}
|
|
141
|
+
filter(record) {
|
|
142
|
+
for (const filter of this.filters) {
|
|
143
|
+
if (!filter(record))
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (this.filters.length < 1)
|
|
147
|
+
return this.parent?.filter(record) ?? true;
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
*getSinks(level) {
|
|
151
|
+
if (this.lowestLevel === null || compareLogLevel(level, this.lowestLevel) < 0) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (this.parent != null && this.parentSinks === "inherit") {
|
|
155
|
+
for (const sink of this.parent.getSinks(level))
|
|
156
|
+
yield sink;
|
|
157
|
+
}
|
|
158
|
+
for (const sink of this.sinks)
|
|
159
|
+
yield sink;
|
|
160
|
+
}
|
|
161
|
+
emit(record, bypassSinks) {
|
|
162
|
+
if (this.lowestLevel === null ||
|
|
163
|
+
compareLogLevel(record.level, this.lowestLevel) < 0 ||
|
|
164
|
+
!this.filter(record)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
for (const sink of this.getSinks(record.level)) {
|
|
168
|
+
if (bypassSinks?.has(sink))
|
|
169
|
+
continue;
|
|
170
|
+
try {
|
|
171
|
+
sink(record);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
const bypassSinks2 = new Set(bypassSinks);
|
|
175
|
+
bypassSinks2.add(sink);
|
|
176
|
+
metaLogger.log("fatal", "Failed to emit a log record to sink {sink}: {error}", { sink, error, record }, bypassSinks2);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
log(level, rawMessage, properties, bypassSinks) {
|
|
181
|
+
const implicitContext = LoggerImpl.getLogger().contextLocalStorage?.getStore() ?? {};
|
|
182
|
+
let cachedProps = undefined;
|
|
183
|
+
const record = typeof properties === "function"
|
|
184
|
+
? {
|
|
185
|
+
category: this.category,
|
|
186
|
+
level,
|
|
187
|
+
timestamp: Date.now(),
|
|
188
|
+
get message() {
|
|
189
|
+
return parseMessageTemplate(rawMessage, this.properties);
|
|
190
|
+
},
|
|
191
|
+
rawMessage,
|
|
192
|
+
get properties() {
|
|
193
|
+
if (cachedProps == null) {
|
|
194
|
+
cachedProps = {
|
|
195
|
+
...implicitContext,
|
|
196
|
+
...properties(),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return cachedProps;
|
|
200
|
+
},
|
|
201
|
+
}
|
|
202
|
+
: {
|
|
203
|
+
category: this.category,
|
|
204
|
+
level,
|
|
205
|
+
timestamp: Date.now(),
|
|
206
|
+
message: parseMessageTemplate(rawMessage, {
|
|
207
|
+
...implicitContext,
|
|
208
|
+
...properties,
|
|
209
|
+
}),
|
|
210
|
+
rawMessage,
|
|
211
|
+
properties: { ...implicitContext, ...properties },
|
|
212
|
+
};
|
|
213
|
+
this.emit(record, bypassSinks);
|
|
214
|
+
}
|
|
215
|
+
logLazily(level, callback, properties = {}) {
|
|
216
|
+
const implicitContext = LoggerImpl.getLogger().contextLocalStorage?.getStore() ?? {};
|
|
217
|
+
let rawMessage = undefined;
|
|
218
|
+
let msg = undefined;
|
|
219
|
+
function realizeMessage() {
|
|
220
|
+
if (msg == null || rawMessage == null) {
|
|
221
|
+
msg = callback((tpl, ...values) => {
|
|
222
|
+
rawMessage = tpl;
|
|
223
|
+
return renderMessage(tpl, values);
|
|
224
|
+
});
|
|
225
|
+
if (rawMessage == null)
|
|
226
|
+
throw new TypeError("No log record was made.");
|
|
227
|
+
}
|
|
228
|
+
return [msg, rawMessage];
|
|
229
|
+
}
|
|
230
|
+
this.emit({
|
|
231
|
+
category: this.category,
|
|
232
|
+
level,
|
|
233
|
+
get message() {
|
|
234
|
+
return realizeMessage()[0];
|
|
235
|
+
},
|
|
236
|
+
get rawMessage() {
|
|
237
|
+
return realizeMessage()[1];
|
|
238
|
+
},
|
|
239
|
+
timestamp: Date.now(),
|
|
240
|
+
properties: { ...implicitContext, ...properties },
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
logTemplate(level, messageTemplate, values, properties = {}) {
|
|
244
|
+
const implicitContext = LoggerImpl.getLogger().contextLocalStorage?.getStore() ?? {};
|
|
245
|
+
this.emit({
|
|
246
|
+
category: this.category,
|
|
247
|
+
level,
|
|
248
|
+
message: renderMessage(messageTemplate, values),
|
|
249
|
+
rawMessage: messageTemplate,
|
|
250
|
+
timestamp: Date.now(),
|
|
251
|
+
properties: { ...implicitContext, ...properties },
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
debug(message, ...values) {
|
|
255
|
+
if (typeof message === "string") {
|
|
256
|
+
this.log("debug", message, (values[0] ?? {}));
|
|
257
|
+
}
|
|
258
|
+
else if (typeof message === "function") {
|
|
259
|
+
this.logLazily("debug", message);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.logTemplate("debug", message, values);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
info(message, ...values) {
|
|
266
|
+
if (typeof message === "string") {
|
|
267
|
+
this.log("info", message, (values[0] ?? {}));
|
|
268
|
+
}
|
|
269
|
+
else if (typeof message === "function") {
|
|
270
|
+
this.logLazily("info", message);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.logTemplate("info", message, values);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
warn(message, ...values) {
|
|
277
|
+
if (typeof message === "string") {
|
|
278
|
+
this.log("warning", message, (values[0] ?? {}));
|
|
279
|
+
}
|
|
280
|
+
else if (typeof message === "function") {
|
|
281
|
+
this.logLazily("warning", message);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
this.logTemplate("warning", message, values);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
error(message, ...values) {
|
|
288
|
+
if (typeof message === "string") {
|
|
289
|
+
this.log("error", message, (values[0] ?? {}));
|
|
290
|
+
}
|
|
291
|
+
else if (typeof message === "function") {
|
|
292
|
+
this.logLazily("error", message);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
this.logTemplate("error", message, values);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
fatal(message, ...values) {
|
|
299
|
+
if (typeof message === "string") {
|
|
300
|
+
this.log("fatal", message, (values[0] ?? {}));
|
|
301
|
+
}
|
|
302
|
+
else if (typeof message === "function") {
|
|
303
|
+
this.logLazily("fatal", message);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.logTemplate("fatal", message, values);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* A logger implementation with contextual properties. Do not use this
|
|
312
|
+
* directly; use {@link Logger.with} instead. This class is exported
|
|
313
|
+
* for testing purposes.
|
|
314
|
+
*/
|
|
315
|
+
export class LoggerCtx {
|
|
316
|
+
constructor(logger, properties) {
|
|
317
|
+
Object.defineProperty(this, "logger", {
|
|
318
|
+
enumerable: true,
|
|
319
|
+
configurable: true,
|
|
320
|
+
writable: true,
|
|
321
|
+
value: void 0
|
|
322
|
+
});
|
|
323
|
+
Object.defineProperty(this, "properties", {
|
|
324
|
+
enumerable: true,
|
|
325
|
+
configurable: true,
|
|
326
|
+
writable: true,
|
|
327
|
+
value: void 0
|
|
328
|
+
});
|
|
329
|
+
this.logger = logger;
|
|
330
|
+
this.properties = properties;
|
|
331
|
+
}
|
|
332
|
+
get category() {
|
|
333
|
+
return this.logger.category;
|
|
334
|
+
}
|
|
335
|
+
get parent() {
|
|
336
|
+
return this.logger.parent;
|
|
337
|
+
}
|
|
338
|
+
getChild(subcategory) {
|
|
339
|
+
return this.logger.getChild(subcategory).with(this.properties);
|
|
340
|
+
}
|
|
341
|
+
with(properties) {
|
|
342
|
+
return new LoggerCtx(this.logger, { ...this.properties, ...properties });
|
|
343
|
+
}
|
|
344
|
+
log(level, message, properties, bypassSinks) {
|
|
345
|
+
this.logger.log(level, message, typeof properties === "function"
|
|
346
|
+
? () => ({
|
|
347
|
+
...this.properties,
|
|
348
|
+
...properties(),
|
|
349
|
+
})
|
|
350
|
+
: { ...this.properties, ...properties }, bypassSinks);
|
|
351
|
+
}
|
|
352
|
+
logLazily(level, callback) {
|
|
353
|
+
this.logger.logLazily(level, callback, this.properties);
|
|
354
|
+
}
|
|
355
|
+
logTemplate(level, messageTemplate, values) {
|
|
356
|
+
this.logger.logTemplate(level, messageTemplate, values, this.properties);
|
|
357
|
+
}
|
|
358
|
+
debug(message, ...values) {
|
|
359
|
+
if (typeof message === "string") {
|
|
360
|
+
this.log("debug", message, (values[0] ?? {}));
|
|
361
|
+
}
|
|
362
|
+
else if (typeof message === "function") {
|
|
363
|
+
this.logLazily("debug", message);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
this.logTemplate("debug", message, values);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
info(message, ...values) {
|
|
370
|
+
if (typeof message === "string") {
|
|
371
|
+
this.log("info", message, (values[0] ?? {}));
|
|
372
|
+
}
|
|
373
|
+
else if (typeof message === "function") {
|
|
374
|
+
this.logLazily("info", message);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
this.logTemplate("info", message, values);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
warn(message, ...values) {
|
|
381
|
+
if (typeof message === "string") {
|
|
382
|
+
this.log("warning", message, (values[0] ?? {}));
|
|
383
|
+
}
|
|
384
|
+
else if (typeof message === "function") {
|
|
385
|
+
this.logLazily("warning", message);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
this.logTemplate("warning", message, values);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
error(message, ...values) {
|
|
392
|
+
if (typeof message === "string") {
|
|
393
|
+
this.log("error", message, (values[0] ?? {}));
|
|
394
|
+
}
|
|
395
|
+
else if (typeof message === "function") {
|
|
396
|
+
this.logLazily("error", message);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
this.logTemplate("error", message, values);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
fatal(message, ...values) {
|
|
403
|
+
if (typeof message === "string") {
|
|
404
|
+
this.log("fatal", message, (values[0] ?? {}));
|
|
405
|
+
}
|
|
406
|
+
else if (typeof message === "function") {
|
|
407
|
+
this.logLazily("fatal", message);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
this.logTemplate("fatal", message, values);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* The meta logger. It is a logger with the category `["logtape", "meta"]`.
|
|
416
|
+
*/
|
|
417
|
+
const metaLogger = LoggerImpl.getLogger(["logtape", "meta"]);
|
|
418
|
+
/**
|
|
419
|
+
* Parse a message template into a message template array and a values array.
|
|
420
|
+
* @param template The message template.
|
|
421
|
+
* @param properties The values to replace placeholders with.
|
|
422
|
+
* @returns The message template array and the values array.
|
|
423
|
+
*/
|
|
424
|
+
export function parseMessageTemplate(template, properties) {
|
|
425
|
+
const message = [];
|
|
426
|
+
let part = "";
|
|
427
|
+
for (let i = 0; i < template.length; i++) {
|
|
428
|
+
const char = template.charAt(i);
|
|
429
|
+
const nextChar = template.charAt(i + 1);
|
|
430
|
+
if (char === "{" && nextChar === "{") {
|
|
431
|
+
// Escaped { character
|
|
432
|
+
part = part + char;
|
|
433
|
+
i++;
|
|
434
|
+
}
|
|
435
|
+
else if (char === "}" && nextChar === "}") {
|
|
436
|
+
// Escaped } character
|
|
437
|
+
part = part + char;
|
|
438
|
+
i++;
|
|
439
|
+
}
|
|
440
|
+
else if (char === "{") {
|
|
441
|
+
// Start of a placeholder
|
|
442
|
+
message.push(part);
|
|
443
|
+
part = "";
|
|
444
|
+
}
|
|
445
|
+
else if (char === "}") {
|
|
446
|
+
// End of a placeholder
|
|
447
|
+
let prop;
|
|
448
|
+
if (part.match(/^\s|\s$/)) {
|
|
449
|
+
prop = part in properties ? properties[part] : properties[part.trim()];
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
prop = properties[part];
|
|
453
|
+
}
|
|
454
|
+
message.push(prop);
|
|
455
|
+
part = "";
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
// Default case
|
|
459
|
+
part = part + char;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
message.push(part);
|
|
463
|
+
return message;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Render a message template with values.
|
|
467
|
+
* @param template The message template.
|
|
468
|
+
* @param values The message template values.
|
|
469
|
+
* @returns The message template values interleaved between the substitution
|
|
470
|
+
* values.
|
|
471
|
+
*/
|
|
472
|
+
export function renderMessage(template, values) {
|
|
473
|
+
const args = [];
|
|
474
|
+
for (let i = 0; i < template.length; i++) {
|
|
475
|
+
args.push(template[i]);
|
|
476
|
+
if (i < values.length)
|
|
477
|
+
args.push(values[i]);
|
|
478
|
+
}
|
|
479
|
+
return args;
|
|
480
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { ConfigError, configure, configureSync, dispose, disposeSync, getConfig, reset, resetSync, } from "./config.js";
|
|
2
|
+
export { withContext } from "./context.js";
|
|
3
|
+
export { getLevelFilter, toFilter, } from "./filter.js";
|
|
4
|
+
export { ansiColorFormatter, defaultConsoleFormatter, defaultTextFormatter, getAnsiColorFormatter, getTextFormatter, } from "./formatter.js";
|
|
5
|
+
export { compareLogLevel, isLogLevel, parseLogLevel, } from "./level.js";
|
|
6
|
+
export { getLogger } from "./logger.js";
|
|
7
|
+
export { getConsoleSink, getStreamSink, withFilter, } from "./sink.js";
|
|
8
|
+
// cSpell: ignore filesink
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { toFilter } from "./filter.js";
|
|
2
|
+
import { defaultConsoleFormatter, defaultTextFormatter, } from "./formatter.js";
|
|
3
|
+
/**
|
|
4
|
+
* Turns a sink into a filtered sink. The returned sink only logs records that
|
|
5
|
+
* pass the filter.
|
|
6
|
+
*
|
|
7
|
+
* @example Filter a console sink to only log records with the info level
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const sink = withFilter(getConsoleSink(), "info");
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* @param sink A sink to be filtered.
|
|
13
|
+
* @param filter A filter to apply to the sink. It can be either a filter
|
|
14
|
+
* function or a {@link LogLevel} string.
|
|
15
|
+
* @returns A sink that only logs records that pass the filter.
|
|
16
|
+
*/
|
|
17
|
+
export function withFilter(sink, filter) {
|
|
18
|
+
const filterFunc = toFilter(filter);
|
|
19
|
+
return (record) => {
|
|
20
|
+
if (filterFunc(record))
|
|
21
|
+
sink(record);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A factory that returns a sink that writes to a {@link WritableStream}.
|
|
26
|
+
*
|
|
27
|
+
* Note that the `stream` is of Web Streams API, which is different from
|
|
28
|
+
* Node.js streams. You can convert a Node.js stream to a Web Streams API
|
|
29
|
+
* stream using [`stream.Writable.toWeb()`] method.
|
|
30
|
+
*
|
|
31
|
+
* [`stream.Writable.toWeb()`]: https://nodejs.org/api/stream.html#streamwritabletowebstreamwritable
|
|
32
|
+
*
|
|
33
|
+
* @example Sink to the standard error in Deno
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const stderrSink = getStreamSink(Deno.stderr.writable);
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Sink to the standard error in Node.js
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import stream from "node:stream";
|
|
41
|
+
* const stderrSink = getStreamSink(stream.Writable.toWeb(process.stderr));
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param stream The stream to write to.
|
|
45
|
+
* @param options The options for the sink.
|
|
46
|
+
* @returns A sink that writes to the stream.
|
|
47
|
+
*/
|
|
48
|
+
export function getStreamSink(stream, options = {}) {
|
|
49
|
+
const formatter = options.formatter ?? defaultTextFormatter;
|
|
50
|
+
const encoder = options.encoder ?? new TextEncoder();
|
|
51
|
+
const writer = stream.getWriter();
|
|
52
|
+
let lastPromise = Promise.resolve();
|
|
53
|
+
const sink = (record) => {
|
|
54
|
+
const bytes = encoder.encode(formatter(record));
|
|
55
|
+
lastPromise = lastPromise
|
|
56
|
+
.then(() => writer.ready)
|
|
57
|
+
.then(() => writer.write(bytes));
|
|
58
|
+
};
|
|
59
|
+
sink[Symbol.asyncDispose] = async () => {
|
|
60
|
+
await lastPromise;
|
|
61
|
+
await writer.close();
|
|
62
|
+
};
|
|
63
|
+
return sink;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A console sink factory that returns a sink that logs to the console.
|
|
67
|
+
*
|
|
68
|
+
* @param options The options for the sink.
|
|
69
|
+
* @returns A sink that logs to the console.
|
|
70
|
+
*/
|
|
71
|
+
export function getConsoleSink(options = {}) {
|
|
72
|
+
const formatter = options.formatter ?? defaultConsoleFormatter;
|
|
73
|
+
const levelMap = {
|
|
74
|
+
debug: "debug",
|
|
75
|
+
info: "info",
|
|
76
|
+
warning: "warn",
|
|
77
|
+
error: "error",
|
|
78
|
+
fatal: "error",
|
|
79
|
+
...(options.levelMap ?? {}),
|
|
80
|
+
};
|
|
81
|
+
const console = options.console ?? globalThis.console;
|
|
82
|
+
return (record) => {
|
|
83
|
+
const args = formatter(record);
|
|
84
|
+
const method = levelMap[record.level];
|
|
85
|
+
if (method === undefined) {
|
|
86
|
+
throw new TypeError(`Invalid log level: ${record.level}.`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof args === "string") {
|
|
89
|
+
const msg = args.replace(/\r?\n$/, "");
|
|
90
|
+
console[method](msg);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console[method](...args);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
package/esm/package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@logtape/file",
|
|
3
|
+
"version": "0.9.0-dev.1",
|
|
4
|
+
"description": "File sink and rotating file sink for LogTape",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"logging",
|
|
7
|
+
"log",
|
|
8
|
+
"logger",
|
|
9
|
+
"file",
|
|
10
|
+
"sink",
|
|
11
|
+
"rotating"
|
|
12
|
+
],
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Hong Minhee",
|
|
15
|
+
"email": "hong@minhee.org",
|
|
16
|
+
"url": "https://hongminhee.org/"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://logtape.org/",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/dahlia/logtape.git",
|
|
22
|
+
"directory": "file/"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/dahlia/logtape/issues"
|
|
27
|
+
},
|
|
28
|
+
"main": "./script/file/mod.js",
|
|
29
|
+
"module": "./esm/file/mod.js",
|
|
30
|
+
"types": "./types/file/mod.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./types/file/mod.d.ts",
|
|
35
|
+
"default": "./esm/file/mod.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./types/file/mod.d.ts",
|
|
39
|
+
"default": "./script/file/mod.js"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"test": "node test_runner.js"
|
|
45
|
+
},
|
|
46
|
+
"funding": [
|
|
47
|
+
"https://github.com/sponsors/dahlia"
|
|
48
|
+
],
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.9.0",
|
|
51
|
+
"picocolors": "^1.0.0",
|
|
52
|
+
"@deno/shim-deno": "~0.18.0"
|
|
53
|
+
},
|
|
54
|
+
"_generatedBy": "dnt@dev"
|
|
55
|
+
}
|