@logtape/logtape 0.1.0-dev.15 → 0.1.0-dev.17

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 CHANGED
@@ -241,103 +241,113 @@ in the API reference for more details.
241
241
 
242
242
  ### File sink
243
243
 
244
- LogTape provides a platform-independent file sink. You can use it by providing
245
- a platform-specific file driver for Deno or Node.js. Here's an example of
246
- a file sink that writes log messages to a file:
244
+ > [!NOTE]
245
+ > File sink is unavailable in the browser environment.
247
246
 
248
- ~~~~ typescript
249
- // Deno
250
- import { type FileSinkDriver, getFileSink } from "@logtape/logtape";
247
+ LogTape provides a file sink as well. Here's an example of a file sink that
248
+ writes log messages to a file:
251
249
 
252
- const driver: FileSinkDriver<Deno.FsFile> = {
253
- openSync(path: string) {
254
- return Deno.openSync(path, { create: true, append: true });
255
- },
256
- writeSync(fd, chunk) {
257
- fd.writeSync(chunk);
258
- },
259
- flushSync(fd) {
260
- fd.syncSync();
261
- },
262
- closeSync(fd) {
263
- fd.close();
264
- },
265
- };
250
+ ~~~~ typescript
251
+ import { getFileSink } from "@logtape/logtape";
266
252
 
267
253
  configure({
268
254
  sinks: {
269
- file: getFileSink("my-app.log", driver),
255
+ file: getFileSink("my-app.log"),
270
256
  },
271
257
  // Omitted for brevity
272
258
  });
273
259
  ~~~~
274
260
 
261
+ See also [`getFileSink()`] function and [`FileSinkOptions`] interface
262
+ in the API reference for more details.
263
+
264
+ [`getFileSink()`]: https://jsr.io/@logtape/logtape/doc/~/getFileSink
265
+ [`FileSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/FileSinkOptions
266
+
267
+ ### Text formatter
268
+
269
+ A stream sink and a file sink write log messages in a plain text format.
270
+ You can customize the format by providing a text formatter. The type of a
271
+ text formatter is:
272
+
275
273
  ~~~~ typescript
276
- // Node.js or Bun
277
- import fs from "node:fs";
278
- import { type FileSinkDriver, getFileSink } from "@logtape/logtape";
274
+ export type TextFormatter = (record: LogRecord) => string;
275
+ ~~~~
279
276
 
280
- const driver: FileSinkDriver<number> = {
281
- openSync(path: string) {
282
- return fs.openSync(path, "a");
283
- },
284
- writeSync: fs.writeSync,
285
- flushSync: fs.fsyncSync,
286
- closeSync: fs.closeSync,
287
- };
277
+ Here's an example of a text formatter that writes log messages in a JSON format:
288
278
 
279
+ ~~~~ typescript
289
280
  configure({
290
281
  sinks: {
291
- file: getFileSink("my-app.log", driver),
282
+ stream: getStreamSink(Deno.stderr.writable, {
283
+ formatter: JSON.stringify,
284
+ }),
292
285
  },
293
286
  // Omitted for brevity
294
- });
287
+ })
295
288
  ~~~~
296
289
 
297
- See also [`getFileSink()`] function, [`FileSinkOptions`] interface, and
298
- [`FileSinkDriver`] interface in the API reference for more details.
290
+ ### Disposable sink
299
291
 
300
- [`getFileSink()`]: https://jsr.io/@logtape/logtape/doc/~/getFileSink
301
- [`FileSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/FileSinkOptions
302
- [`FileSinkDriver`]: https://jsr.io/@logtape/logtape/doc/~/FileSinkDriver
292
+ A disposable sink is a sink that can be disposed of. They are automatically
293
+ disposed of when the configuration is reset or the program exits. The type
294
+ of a disposable sink is: `Sink & Disposable`. You can create a disposable
295
+ sink by defining a `[Symbol.dispose]` method:
303
296
 
304
- ### Buffer sink
297
+ ~~~~ typescript
298
+ const disposableSink: Sink & Disposable = (record: LogRecord) => {
299
+ console.log(record.message);
300
+ };
301
+ disposableSink[Symbol.dispose] = () => {
302
+ console.log("Disposed!");
303
+ };
304
+ ~~~~
305
305
 
306
- For testing purposes, you may want to collect log messages in memory. Although
307
- LogTape does not provide a built-in buffer sink, you can easily implement it:
306
+
307
+ Testing
308
+ -------
309
+
310
+ Here are some tips for testing your application or library with LogTape.
311
+
312
+ ### Reset configuration
313
+
314
+ You can reset the configuration of LogTape to its initial state. This is
315
+ useful when you want to reset the configuration between tests. For example,
316
+ the following code shows how to reset the configuration after a test
317
+ (regardless of whether the test passes or fails) in Deno:
308
318
 
309
319
  ~~~~ typescript
310
- import { type LogRecord, configure } from "@logtape/logtape";
320
+ import { configure, reset } from "@logtape/logtape";
311
321
 
312
- const buffer: LogRecord[] = [];
322
+ Deno.test("my test", async (t) => {
323
+ await t.step("set up", () => {
324
+ configure({ /* ... */ });
325
+ });
313
326
 
314
- configure({
315
- sinks: {
316
- buffer: buffer.push.bind(buffer),
317
- },
318
- // Omitted for brevity
327
+ await t.step("run test", () => {
328
+ // Run the test
329
+ });
330
+
331
+ await t.step("tear down", () => {
332
+ reset();
333
+ });
319
334
  });
320
335
  ~~~~
321
336
 
322
- ### Text formatter
337
+ ### Buffer sink
323
338
 
324
- A stream sink and a file sink write log messages in a plain text format.
325
- You can customize the format by providing a text formatter. The type of a
326
- text formatter is:
339
+ For testing purposes, you may want to collect log messages in memory. Although
340
+ LogTape does not provide a built-in buffer sink, you can easily implement it:
327
341
 
328
342
  ~~~~ typescript
329
- export type TextFormatter = (record: LogRecord) => string;
330
- ~~~~
343
+ import { type LogRecord, configure } from "@logtape/logtape";
331
344
 
332
- Here's an example of a text formatter that writes log messages in a JSON format:
345
+ const buffer: LogRecord[] = [];
333
346
 
334
- ~~~~ typescript
335
347
  configure({
336
348
  sinks: {
337
- stream: getStreamSink(Deno.stderr.writable, {
338
- formatter: JSON.stringify,
339
- }),
349
+ buffer: buffer.push.bind(buffer),
340
350
  },
341
351
  // Omitted for brevity
342
- })
352
+ });
343
353
  ~~~~
package/esm/config.js CHANGED
@@ -1,10 +1,21 @@
1
+ import * as dntShim from "./_dnt.shims.js";
1
2
  import { toFilter } from "./filter.js";
2
3
  import { LoggerImpl } from "./logger.js";
3
4
  import { getConsoleSink } from "./sink.js";
5
+ /**
6
+ * Whether the loggers are configured.
7
+ */
4
8
  let configured = false;
9
+ /**
10
+ * Disposables to dispose when resetting the configuration.
11
+ */
12
+ const disposables = new Set();
5
13
  /**
6
14
  * Configure the loggers with the specified configuration.
7
15
  *
16
+ * Note that if the given sinks or filters are disposable, they will be
17
+ * disposed when the configuration is reset, or when the process exits.
18
+ *
8
19
  * @example
9
20
  * ```typescript
10
21
  * configure({
@@ -42,8 +53,8 @@ export function configure(config) {
42
53
  if (configured && !config.reset) {
43
54
  throw new ConfigError("Already configured; if you want to reset, turn on the reset flag.");
44
55
  }
56
+ reset();
45
57
  configured = true;
46
- LoggerImpl.getLogger([]).resetDescendants();
47
58
  let metaConfigured = false;
48
59
  for (const cfg of config.loggers) {
49
60
  if (cfg.category.length === 0 ||
@@ -73,6 +84,20 @@ export function configure(config) {
73
84
  logger.filters.push(toFilter(filter));
74
85
  }
75
86
  }
87
+ for (const sink of Object.values(config.sinks)) {
88
+ if (Symbol.dispose in sink)
89
+ disposables.add(sink);
90
+ }
91
+ for (const filter of Object.values(config.filters)) {
92
+ if (filter != null && typeof filter !== "string" && Symbol.dispose in filter)
93
+ disposables.add(filter);
94
+ }
95
+ if ("process" in dntShim.dntGlobalThis) { // @ts-ignore: It's fine to use process in Node
96
+ process.on("exit", dispose);
97
+ }
98
+ else { // @ts-ignore: It's fine to addEventListener() on the browser/Deno
99
+ addEventListener("unload", dispose);
100
+ }
76
101
  const meta = LoggerImpl.getLogger(["logtape", "meta"]);
77
102
  if (!metaConfigured) {
78
103
  meta.sinks.push(getConsoleSink());
@@ -90,9 +115,18 @@ export function configure(config) {
90
115
  * Reset the configuration. Mostly for testing purposes.
91
116
  */
92
117
  export function reset() {
118
+ dispose();
93
119
  LoggerImpl.getLogger([]).resetDescendants();
94
120
  configured = false;
95
121
  }
122
+ /**
123
+ * Dispose of the disposables.
124
+ */
125
+ export function dispose() {
126
+ for (const disposable of disposables)
127
+ disposable[Symbol.dispose]();
128
+ disposables.clear();
129
+ }
96
130
  /**
97
131
  * A configuration error.
98
132
  */
@@ -0,0 +1,32 @@
1
+ import * as dntShim from "./_dnt.shims.js";
2
+ import fs from "node:fs";
3
+ import { webDriver } from "./filesink.web.js";
4
+ import { getFileSink as getBaseFileSink, } from "./sink.js";
5
+ /**
6
+ * A Node.js-specific file sink driver.
7
+ */
8
+ export const nodeDriver = {
9
+ openSync(path) {
10
+ return fs.openSync(path, "a");
11
+ },
12
+ writeSync: fs.writeSync,
13
+ flushSync: fs.fsyncSync,
14
+ closeSync: fs.closeSync,
15
+ };
16
+ /**
17
+ * Get a file sink.
18
+ *
19
+ * Note that this function is unavailable in the browser.
20
+ *
21
+ * @param path A path to the file to write to.
22
+ * @param options The options for the sink.
23
+ * @returns A sink that writes to the file. The sink is also a disposable
24
+ * object that closes the file when disposed.
25
+ */
26
+ export function getFileSink(path, options = {}) {
27
+ if ("document" in dntShim.dntGlobalThis) {
28
+ return getBaseFileSink(path, { ...options, ...webDriver });
29
+ }
30
+ return getBaseFileSink(path, { ...options, ...nodeDriver });
31
+ }
32
+ // cSpell: ignore filesink
@@ -0,0 +1,12 @@
1
+ function notImplemented() {
2
+ throw new Error("File sink is not available in the browser.");
3
+ }
4
+ /**
5
+ * A browser-specific file sink driver. All methods throw an error.
6
+ */
7
+ export const webDriver = {
8
+ openSync: notImplemented,
9
+ writeSync: notImplemented,
10
+ flushSync: notImplemented,
11
+ closeSync: notImplemented,
12
+ };
package/esm/mod.js CHANGED
@@ -1,5 +1,7 @@
1
- export { ConfigError, configure, } from "./config.js";
1
+ export { ConfigError, configure, reset, } from "./config.js";
2
+ export { getFileSink } from "./filesink.node.js";
2
3
  export { getLevelFilter, toFilter, } from "./filter.js";
3
4
  export { defaultConsoleFormatter, defaultTextFormatter, } from "./formatter.js";
4
5
  export { getLogger } from "./logger.js";
5
- export { getConsoleSink, getFileSink, getStreamSink, } from "./sink.js";
6
+ export { getConsoleSink, getStreamSink, } from "./sink.js";
7
+ // cSpell: ignore filesink
package/esm/sink.js CHANGED
@@ -27,10 +27,12 @@ export function getStreamSink(stream, options = {}) {
27
27
  const formatter = options.formatter ?? defaultTextFormatter;
28
28
  const encoder = options.encoder ?? new TextEncoder();
29
29
  const writer = stream.getWriter();
30
- return (record) => {
30
+ const sink = (record) => {
31
31
  const bytes = encoder.encode(formatter(record));
32
32
  writer.ready.then(() => writer.write(bytes));
33
33
  };
34
+ sink[Symbol.dispose] = () => writer.close();
35
+ return sink;
34
36
  }
35
37
  /**
36
38
  * A console sink factory that returns a sink that logs to the console.
@@ -58,10 +60,12 @@ export function getConsoleSink(options = {}) {
58
60
  }
59
61
  /**
60
62
  * Get a platform-independent file sink.
63
+ *
61
64
  * @typeParam TFile The type of the file descriptor.
62
65
  * @param path A path to the file to write to.
63
66
  * @param options The options for the sink and the file driver.
64
- * @returns A sink that writes to the file.
67
+ * @returns A sink that writes to the file. The sink is also a disposable
68
+ * object that closes the file when disposed.
65
69
  */
66
70
  export function getFileSink(path, options) {
67
71
  const formatter = options.formatter ?? defaultTextFormatter;
@@ -71,6 +75,6 @@ export function getFileSink(path, options) {
71
75
  options.writeSync(fd, encoder.encode(formatter(record)));
72
76
  options.flushSync(fd);
73
77
  };
74
- sink.close = () => options.closeSync(fd);
78
+ sink[Symbol.dispose] = () => options.closeSync(fd);
75
79
  return sink;
76
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/logtape",
3
- "version": "0.1.0-dev.15+0a0e7e21",
3
+ "version": "0.1.0-dev.17+5e7affa9",
4
4
  "description": "Simple logging library for Deno/Node.js/Bun/browsers",
5
5
  "keywords": [
6
6
  "logging",
package/script/config.js CHANGED
@@ -1,13 +1,47 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfigError = exports.reset = exports.configure = void 0;
26
+ exports.ConfigError = exports.dispose = exports.reset = exports.configure = void 0;
27
+ const dntShim = __importStar(require("./_dnt.shims.js"));
4
28
  const filter_js_1 = require("./filter.js");
5
29
  const logger_js_1 = require("./logger.js");
6
30
  const sink_js_1 = require("./sink.js");
31
+ /**
32
+ * Whether the loggers are configured.
33
+ */
7
34
  let configured = false;
35
+ /**
36
+ * Disposables to dispose when resetting the configuration.
37
+ */
38
+ const disposables = new Set();
8
39
  /**
9
40
  * Configure the loggers with the specified configuration.
10
41
  *
42
+ * Note that if the given sinks or filters are disposable, they will be
43
+ * disposed when the configuration is reset, or when the process exits.
44
+ *
11
45
  * @example
12
46
  * ```typescript
13
47
  * configure({
@@ -45,8 +79,8 @@ function configure(config) {
45
79
  if (configured && !config.reset) {
46
80
  throw new ConfigError("Already configured; if you want to reset, turn on the reset flag.");
47
81
  }
82
+ reset();
48
83
  configured = true;
49
- logger_js_1.LoggerImpl.getLogger([]).resetDescendants();
50
84
  let metaConfigured = false;
51
85
  for (const cfg of config.loggers) {
52
86
  if (cfg.category.length === 0 ||
@@ -76,6 +110,20 @@ function configure(config) {
76
110
  logger.filters.push((0, filter_js_1.toFilter)(filter));
77
111
  }
78
112
  }
113
+ for (const sink of Object.values(config.sinks)) {
114
+ if (Symbol.dispose in sink)
115
+ disposables.add(sink);
116
+ }
117
+ for (const filter of Object.values(config.filters)) {
118
+ if (filter != null && typeof filter !== "string" && Symbol.dispose in filter)
119
+ disposables.add(filter);
120
+ }
121
+ if ("process" in dntShim.dntGlobalThis) { // @ts-ignore: It's fine to use process in Node
122
+ process.on("exit", dispose);
123
+ }
124
+ else { // @ts-ignore: It's fine to addEventListener() on the browser/Deno
125
+ addEventListener("unload", dispose);
126
+ }
79
127
  const meta = logger_js_1.LoggerImpl.getLogger(["logtape", "meta"]);
80
128
  if (!metaConfigured) {
81
129
  meta.sinks.push((0, sink_js_1.getConsoleSink)());
@@ -94,10 +142,20 @@ exports.configure = configure;
94
142
  * Reset the configuration. Mostly for testing purposes.
95
143
  */
96
144
  function reset() {
145
+ dispose();
97
146
  logger_js_1.LoggerImpl.getLogger([]).resetDescendants();
98
147
  configured = false;
99
148
  }
100
149
  exports.reset = reset;
150
+ /**
151
+ * Dispose of the disposables.
152
+ */
153
+ function dispose() {
154
+ for (const disposable of disposables)
155
+ disposable[Symbol.dispose]();
156
+ disposables.clear();
157
+ }
158
+ exports.dispose = dispose;
101
159
  /**
102
160
  * A configuration error.
103
161
  */
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.getFileSink = exports.nodeDriver = void 0;
30
+ const dntShim = __importStar(require("./_dnt.shims.js"));
31
+ const node_fs_1 = __importDefault(require("node:fs"));
32
+ const filesink_web_js_1 = require("./filesink.web.js");
33
+ const sink_js_1 = require("./sink.js");
34
+ /**
35
+ * A Node.js-specific file sink driver.
36
+ */
37
+ exports.nodeDriver = {
38
+ openSync(path) {
39
+ return node_fs_1.default.openSync(path, "a");
40
+ },
41
+ writeSync: node_fs_1.default.writeSync,
42
+ flushSync: node_fs_1.default.fsyncSync,
43
+ closeSync: node_fs_1.default.closeSync,
44
+ };
45
+ /**
46
+ * Get a file sink.
47
+ *
48
+ * Note that this function is unavailable in the browser.
49
+ *
50
+ * @param path A path to the file to write to.
51
+ * @param options The options for the sink.
52
+ * @returns A sink that writes to the file. The sink is also a disposable
53
+ * object that closes the file when disposed.
54
+ */
55
+ function getFileSink(path, options = {}) {
56
+ if ("document" in dntShim.dntGlobalThis) {
57
+ return (0, sink_js_1.getFileSink)(path, { ...options, ...filesink_web_js_1.webDriver });
58
+ }
59
+ return (0, sink_js_1.getFileSink)(path, { ...options, ...exports.nodeDriver });
60
+ }
61
+ exports.getFileSink = getFileSink;
62
+ // cSpell: ignore filesink
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.webDriver = void 0;
4
+ function notImplemented() {
5
+ throw new Error("File sink is not available in the browser.");
6
+ }
7
+ /**
8
+ * A browser-specific file sink driver. All methods throw an error.
9
+ */
10
+ exports.webDriver = {
11
+ openSync: notImplemented,
12
+ writeSync: notImplemented,
13
+ flushSync: notImplemented,
14
+ closeSync: notImplemented,
15
+ };
package/script/mod.js CHANGED
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getStreamSink = exports.getFileSink = exports.getConsoleSink = exports.getLogger = exports.defaultTextFormatter = exports.defaultConsoleFormatter = exports.toFilter = exports.getLevelFilter = exports.configure = exports.ConfigError = void 0;
3
+ exports.getStreamSink = exports.getConsoleSink = exports.getLogger = exports.defaultTextFormatter = exports.defaultConsoleFormatter = exports.toFilter = exports.getLevelFilter = exports.getFileSink = exports.reset = exports.configure = exports.ConfigError = void 0;
4
4
  var config_js_1 = require("./config.js");
5
5
  Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return config_js_1.ConfigError; } });
6
6
  Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return config_js_1.configure; } });
7
+ Object.defineProperty(exports, "reset", { enumerable: true, get: function () { return config_js_1.reset; } });
8
+ var filesink_node_js_1 = require("./filesink.node.js");
9
+ Object.defineProperty(exports, "getFileSink", { enumerable: true, get: function () { return filesink_node_js_1.getFileSink; } });
7
10
  var filter_js_1 = require("./filter.js");
8
11
  Object.defineProperty(exports, "getLevelFilter", { enumerable: true, get: function () { return filter_js_1.getLevelFilter; } });
9
12
  Object.defineProperty(exports, "toFilter", { enumerable: true, get: function () { return filter_js_1.toFilter; } });
@@ -14,5 +17,5 @@ var logger_js_1 = require("./logger.js");
14
17
  Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return logger_js_1.getLogger; } });
15
18
  var sink_js_1 = require("./sink.js");
16
19
  Object.defineProperty(exports, "getConsoleSink", { enumerable: true, get: function () { return sink_js_1.getConsoleSink; } });
17
- Object.defineProperty(exports, "getFileSink", { enumerable: true, get: function () { return sink_js_1.getFileSink; } });
18
20
  Object.defineProperty(exports, "getStreamSink", { enumerable: true, get: function () { return sink_js_1.getStreamSink; } });
21
+ // cSpell: ignore filesink
package/script/sink.js CHANGED
@@ -30,10 +30,12 @@ function getStreamSink(stream, options = {}) {
30
30
  const formatter = options.formatter ?? formatter_js_1.defaultTextFormatter;
31
31
  const encoder = options.encoder ?? new TextEncoder();
32
32
  const writer = stream.getWriter();
33
- return (record) => {
33
+ const sink = (record) => {
34
34
  const bytes = encoder.encode(formatter(record));
35
35
  writer.ready.then(() => writer.write(bytes));
36
36
  };
37
+ sink[Symbol.dispose] = () => writer.close();
38
+ return sink;
37
39
  }
38
40
  exports.getStreamSink = getStreamSink;
39
41
  /**
@@ -63,10 +65,12 @@ function getConsoleSink(options = {}) {
63
65
  exports.getConsoleSink = getConsoleSink;
64
66
  /**
65
67
  * Get a platform-independent file sink.
68
+ *
66
69
  * @typeParam TFile The type of the file descriptor.
67
70
  * @param path A path to the file to write to.
68
71
  * @param options The options for the sink and the file driver.
69
- * @returns A sink that writes to the file.
72
+ * @returns A sink that writes to the file. The sink is also a disposable
73
+ * object that closes the file when disposed.
70
74
  */
71
75
  function getFileSink(path, options) {
72
76
  const formatter = options.formatter ?? formatter_js_1.defaultTextFormatter;
@@ -76,7 +80,7 @@ function getFileSink(path, options) {
76
80
  options.writeSync(fd, encoder.encode(formatter(record)));
77
81
  options.flushSync(fd);
78
82
  };
79
- sink.close = () => options.closeSync(fd);
83
+ sink[Symbol.dispose] = () => options.closeSync(fd);
80
84
  return sink;
81
85
  }
82
86
  exports.getFileSink = getFileSink;
package/types/config.d.ts CHANGED
@@ -50,6 +50,9 @@ export interface LoggerConfig<TSinkId extends string, TFilterId extends string>
50
50
  /**
51
51
  * Configure the loggers with the specified configuration.
52
52
  *
53
+ * Note that if the given sinks or filters are disposable, they will be
54
+ * disposed when the configuration is reset, or when the process exits.
55
+ *
53
56
  * @example
54
57
  * ```typescript
55
58
  * configure({
@@ -88,6 +91,10 @@ export declare function configure<TSinkId extends string, TFilterId extends stri
88
91
  * Reset the configuration. Mostly for testing purposes.
89
92
  */
90
93
  export declare function reset(): void;
94
+ /**
95
+ * Dispose of the disposables.
96
+ */
97
+ export declare function dispose(): void;
91
98
  /**
92
99
  * A configuration error.
93
100
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM;IACtE;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAEvC;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAE5C;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAC3B,OAAO,SAAS,MAAM,EACtB,SAAS,SAAS,MAAM;IAExB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,EACxE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,QA0DnC;AAED;;GAEG;AACH,wBAAgB,KAAK,SAGpB;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;;OAGG;gBACS,OAAO,EAAE,MAAM;CAI5B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM;IACtE;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAEvC;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAE5C;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAC3B,OAAO,SAAS,MAAM,EACtB,SAAS,SAAS,MAAM;IAExB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,EACxE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,QA0EnC;AAED;;GAEG;AACH,wBAAgB,KAAK,SAIpB;AAED;;GAEG;AACH,wBAAgB,OAAO,SAGtB;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;;OAGG;gBACS,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,18 @@
1
+ /// <reference types="node" />
2
+ import { type FileSinkDriver, type FileSinkOptions, type Sink } from "./sink.js";
3
+ /**
4
+ * A Node.js-specific file sink driver.
5
+ */
6
+ export declare const nodeDriver: FileSinkDriver<number>;
7
+ /**
8
+ * Get a file sink.
9
+ *
10
+ * Note that this function is unavailable in the browser.
11
+ *
12
+ * @param path A path to the file to write to.
13
+ * @param options The options for the sink.
14
+ * @returns A sink that writes to the file. The sink is also a disposable
15
+ * object that closes the file when disposed.
16
+ */
17
+ export declare function getFileSink(path: string, options?: FileSinkOptions): Sink & Disposable;
18
+ //# sourceMappingURL=filesink.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesink.node.d.ts","sourceRoot":"","sources":["../src/filesink.node.ts"],"names":[],"mappings":";AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,IAAI,EACV,MAAM,WAAW,CAAC;AAEnB;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,cAAc,CAAC,MAAM,CAO7C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,eAAoB,GAC5B,IAAI,GAAG,UAAU,CAKnB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesink.test.d.ts","sourceRoot":"","sources":["../src/filesink.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import type { FileSinkDriver } from "./sink.js";
2
+ /**
3
+ * A browser-specific file sink driver. All methods throw an error.
4
+ */
5
+ export declare const webDriver: FileSinkDriver<void>;
6
+ //# sourceMappingURL=filesink.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesink.web.d.ts","sourceRoot":"","sources":["../src/filesink.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,cAAc,CAAC,IAAI,CAK1C,CAAC"}
package/types/mod.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- export { type Config, ConfigError, configure, type LoggerConfig, } from "./config.js";
1
+ export { type Config, ConfigError, configure, type LoggerConfig, reset, } from "./config.js";
2
+ export { getFileSink } from "./filesink.node.js";
2
3
  export { type Filter, type FilterLike, getLevelFilter, toFilter, } from "./filter.js";
3
4
  export { type ConsoleFormatter, defaultConsoleFormatter, defaultTextFormatter, type TextFormatter, } from "./formatter.js";
4
5
  export { getLogger, type Logger } from "./logger.js";
5
6
  export type { LogLevel, LogRecord } from "./record.js";
6
- export { type ConsoleSinkOptions, type FileSinkDriver, type FileSinkOptions, getConsoleSink, getFileSink, getStreamSink, type Sink, type StreamSinkOptions, } from "./sink.js";
7
+ export { type ConsoleSinkOptions, type FileSinkDriver, type FileSinkOptions, getConsoleSink, getStreamSink, type Sink, type StreamSinkOptions, } from "./sink.js";
7
8
  //# sourceMappingURL=mod.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,WAAW,EACX,SAAS,EACT,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,KAAK,IAAI,EACT,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,WAAW,EACX,SAAS,EACT,KAAK,YAAY,EACjB,KAAK,GACN,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EACf,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,cAAc,EACd,aAAa,EACb,KAAK,IAAI,EACT,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC"}
package/types/sink.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
+ /// <reference types="node" />
3
4
  import * as dntShim from "./_dnt.shims.js";
4
5
  import { type ConsoleFormatter, type TextFormatter } from "./formatter.js";
5
6
  import type { LogRecord } from "./record.js";
@@ -52,7 +53,7 @@ export interface StreamSinkOptions {
52
53
  * @param options The options for the sink.
53
54
  * @returns A sink that writes to the stream.
54
55
  */
55
- export declare function getStreamSink(stream: dntShim.WritableStream, options?: StreamSinkOptions): Sink;
56
+ export declare function getStreamSink(stream: dntShim.WritableStream, options?: StreamSinkOptions): Sink & Disposable;
56
57
  /**
57
58
  * Options for the {@link getConsoleSink} function.
58
59
  */
@@ -106,12 +107,12 @@ export interface FileSinkDriver<TFile> {
106
107
  }
107
108
  /**
108
109
  * Get a platform-independent file sink.
110
+ *
109
111
  * @typeParam TFile The type of the file descriptor.
110
112
  * @param path A path to the file to write to.
111
113
  * @param options The options for the sink and the file driver.
112
- * @returns A sink that writes to the file.
114
+ * @returns A sink that writes to the file. The sink is also a disposable
115
+ * object that closes the file when disposed.
113
116
  */
114
- export declare function getFileSink<TFile>(path: string, options: FileSinkOptions & FileSinkDriver<TFile>): Sink & {
115
- close: () => void;
116
- };
117
+ export declare function getFileSink<TFile>(path: string, options: FileSinkOptions & FileSinkDriver<TFile>): Sink & Disposable;
117
118
  //# sourceMappingURL=sink.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../src/sink.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EACL,KAAK,gBAAgB,EAGrB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,CAAC,cAAc,EAC9B,OAAO,GAAE,iBAAsB,GAC9B,IAAI,CAQN;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,kBAAuB,GAAG,IAAI,CAYrE;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IAE9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAE9C;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,GAC/C,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CAU9B"}
1
+ {"version":3,"file":"sink.d.ts","sourceRoot":"","sources":["../src/sink.ts"],"names":[],"mappings":";;;AAAA,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EACL,KAAK,gBAAgB,EAGrB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAE1B;;OAEG;IACH,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,CAAC,cAAc,EAC9B,OAAO,GAAE,iBAAsB,GAC9B,IAAI,GAAG,UAAU,CAUnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,kBAAuB,GAAG,IAAI,CAYrE;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IAE9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAE9C;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,GAC/C,IAAI,GAAG,UAAU,CAUnB"}