Skip to content

Commit

Permalink
Sink option types
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Apr 17, 2024
1 parent 821c24f commit 11d80a5
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 17 deletions.
87 changes: 85 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,23 @@ to loggers whose categories are `["my-app", "my-module"]` and `["my-app"]`.
This behavior allows you to control the verbosity of log messages by setting
the log level of loggers at different levels of the category hierarchy.

Here's an example of setting log levels for different categories:

~~~~ typescript
import { configure, getConsoleSink } from "@logtape/logtape";

configure({
sinks: {
console: getConsoleSink(),
},
filters: {},
loggers: [
{ category: ["my-app"], level: "info", sinks: ["console"] },
{ category: ["my-app", "my-module"], level: "debug", sinks: ["console"] },
],
})
~~~~


Sinks
-----
Expand Down Expand Up @@ -156,6 +173,8 @@ configure({
});
~~~~

### Console sink

Of course, you don't have to implement your own console sink because LogTape
provides a console sink:

Expand All @@ -170,19 +189,38 @@ configure({
});
~~~~

See also [`getConsoleSink()`] function and [`ConsoleSinkOptions`] interface
in the API reference for more details.

[`getConsoleSink()`]: https://jsr.io/@logtape/logtape/doc/~/getConsoleSink
[`ConsoleSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/ConsoleSinkOptions

### Stream sink

Another built-in sink is a stream sink. It writes log messages to
a [`WritableStream`]. Here's an example of a stream sink that writes log
messages to the standard error:

~~~~ typescript
// Deno:
const stderrSink = getStreamSink(Deno.stderr.writable);
configure({
sinks: {
stream: getStreamSink(Deno.stderr.writable),
},
// Omitted for brevity
});
~~~~

~~~~ typescript
// Node.js:
import stream from "node:stream";
const stderrSink = getStreamSink(stream.Writable.toWeb(process.stderr));

configure({
sinks: {
stream: getStreamSink(stream.Writable.toWeb(process.stderr)),
},
// Omitted for brevity
});
~~~~

> [!NOTE]
Expand All @@ -192,6 +230,51 @@ const stderrSink = getStreamSink(stream.Writable.toWeb(process.stderr));
> Node.js stream. You can use [`Writable.toWeb()`] method to convert a Node.js
> stream to a `WritableStream`.
See also [`getStreamSink()`] function and [`StreamSinkOptions`] interface
in the API reference for more details.

[`WritableStream`]: https://developer.mozilla.org/en-US/docs/Web/API/WritableStream
[`Writable`]: https://nodejs.org/api/stream.html#class-streamwritable
[`Writable.toWeb()`]: https://nodejs.org/api/stream.html#streamwritabletowebstreamwritable
[`getStreamSink()`]: https://jsr.io/@logtape/logtape/doc/~/getStreamSink
[`StreamSinkOptions`]: https://jsr.io/@logtape/logtape/doc/~/StreamSinkOptions

### Buffer sink

For testing purposes, you may want to collect log messages in memory. Although
LogTape does not provide a built-in buffer sink, you can easily implement it:

~~~~ typescript
import { type LogRecord, configure } from "@logtape/logtape";

const buffer: LogRecord[] = [];

configure({
sinks: {
buffer: buffer.push.bind(buffer),
},
// Omitted for brevity
});
~~~~

### Text formatter

A stream sink writes log messages in a plain text format. You can customize
the format by providing a text formatter. The type of a text formatter is:

~~~~ typescript
export type TextFormatter = (record: LogRecord) => string;
~~~~

Here's an example of a text formatter that writes log messages in a JSON format:

~~~~ typescript
configure({
sinks: {
stream: getStreamSink(Deno.stderr.writable, {
formatter: JSON.stringify,
}),
},
// Omitted for brevity
})
~~~~
3 changes: 1 addition & 2 deletions logtape/sink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { assertThrows } from "@std/assert/assert-throws";
import { delay } from "@std/async/delay";
import makeConsoleMock from "consolemock";
import { debug, error, fatal, info, warning } from "./fixtures.ts";
import { defaultConsoleFormatter } from "./formatter.ts";
import type { LogLevel } from "./record.ts";
import { getConsoleSink, getStreamSink } from "./sink.ts";

Expand Down Expand Up @@ -43,7 +42,7 @@ Deno.test("getStreamSink()", async () => {
Deno.test("getConsoleSink()", () => {
// @ts-ignore: consolemock is not typed
const mock: ConsoleMock = makeConsoleMock();
const sink = getConsoleSink(defaultConsoleFormatter, mock);
const sink = getConsoleSink({ console: mock });
sink(debug);
sink(info);
sink(warning);
Expand Down
51 changes: 38 additions & 13 deletions logtape/sink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ import type { LogRecord } from "./record.ts";
*/
export type Sink = (record: LogRecord) => void;

/**
* Options for the {@link getStreamSink} function.
*/
export interface StreamSinkOptions {
/**
* The text formatter to use. Defaults to {@link defaultTextFormatter}.
*/
formatter?: TextFormatter;

/**
* The text encoder to use. Defaults to an instance of {@link TextEncoder}.
*/
encoder?: { encode(text: string): Uint8Array };
}

/**
* A factory that returns a sink that writes to a {@link WritableStream}.
*
Expand All @@ -38,36 +53,46 @@ export type Sink = (record: LogRecord) => void;
* ```
*
* @param stream The stream to write to.
* @param formatter The text formatter to use. Defaults to
* {@link defaultTextFormatter}.
* @param encoder The text encoder to use. Defaults to an instance of
* {@link TextEncoder}.
* @param options The options for the sink.
* @returns A sink that writes to the stream.
*/
export function getStreamSink(
stream: WritableStream,
formatter: TextFormatter = defaultTextFormatter,
encoder: { encode(text: string): Uint8Array } = new TextEncoder(),
options: StreamSinkOptions = {},
): Sink {
const formatter = options.formatter ?? defaultTextFormatter;
const encoder = options.encoder ?? new TextEncoder();
const writer = stream.getWriter();
return (record: LogRecord) => {
const bytes = encoder.encode(formatter(record));
writer.ready.then(() => writer.write(bytes));
};
}

/**
* Options for the {@link getConsoleSink} function.
*/
export interface ConsoleSinkOptions {
/**
* The console formatter to use. Defaults to {@link defaultConsoleFormatter}.
*/
formatter?: ConsoleFormatter;

/**
* The console to log to. Defaults to {@link console}.
*/
console?: Console;
}

/**
* A console sink factory that returns a sink that logs to the console.
*
* @param formatter A console formatter. Defaults to
* {@link defaultConsoleFormatter}.
* @param console The console to log to. Defaults to {@link console}.
* @param options The options for the sink.
* @returns A sink that logs to the console.
*/
export function getConsoleSink(
formatter: ConsoleFormatter = defaultConsoleFormatter,
console: Console = globalThis.console,
): Sink {
export function getConsoleSink(options: ConsoleSinkOptions = {}): Sink {
const formatter = options.formatter ?? defaultConsoleFormatter;
const console = options.console ?? globalThis.console;
return (record: LogRecord) => {
const args = formatter(record);
if (record.level === "debug") console.debug(...args);
Expand Down

0 comments on commit 11d80a5

Please sign in to comment.