diff --git a/README.md b/README.md index 50d95e2..39904f2 100644 --- a/README.md +++ b/README.md @@ -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 ----- @@ -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: @@ -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] @@ -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 +}) +~~~~ diff --git a/logtape/sink.test.ts b/logtape/sink.test.ts index 0cf31fa..3acaa79 100644 --- a/logtape/sink.test.ts +++ b/logtape/sink.test.ts @@ -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"; @@ -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); diff --git a/logtape/sink.ts b/logtape/sink.ts index 933e467..20597d1 100644 --- a/logtape/sink.ts +++ b/logtape/sink.ts @@ -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}. * @@ -38,17 +53,15 @@ 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)); @@ -56,18 +69,30 @@ export function getStreamSink( }; } +/** + * 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);