Skip to content

Commit

Permalink
Sink filter
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Apr 21, 2024
1 parent fa2367e commit 5a800eb
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ To be released.
- Added `parseLogLevel()` function.
- Added `isLogLevel()` function.
- Added `getConfig()` function.
- Added `withFilter()` function.


Version 0.2.2
Expand Down
85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,91 @@ export default {
[`ctx.waitUntil()`]: https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil


Filters
-------

A filter is a function that filters log messages. A filter takes a log record
and returns a boolean value. If the filter returns `true`, the log record is
passed to the sinks; otherwise, the log record is discarded. Its signature is:

~~~~ typescript
export type Filter = (record: LogRecord) => boolean;
~~~~

For example, the following filter discards log messages whose property `elapsed`
is less than 100 milliseconds:

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

await configure({
// Omitted for brevity
filters: {
tooSlow(record: LogRecord) {
return "elapsed" in record.properties && record.properties.elapsed >= 100;
},
},
loggers: [
{
category: ["my-app", "database"],
level: "debug",
sinks: ["console"],
filters: ["tooSlow"],
}
]
});
~~~~

### Level filter

LogTape provides a built-in level filter. You can use the level filter to
filter log messages by their log levels. The level filter factory takes
a [`LogLevel`] string and returns a level filter. For example, the following
level filter discards log messages whose log level is less than `info`:

~~~~ typescript
import { getLevelFilter } from "@logtape/logtape";

await configure({
filters: {
infoOrHigher: getLevelFilter("info");
},
// Omitted for brevity
});
~~~~

[`LogLevel`]: https://jsr.io/@logtape/logtape/doc/~/LogLevel

### Sink filter

A sink filter is a filter that is applied to a specific sink. You can add a
sink filter to a sink by decorating the sink with [`withFilter()`]:

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

await configure({
sinks: {
filteredConsole: withFilter(
getConsoleSink(),
log => "elapsed" in log.properties && log.properties.elapsed >= 100,
),
},
// Omitted for brevity
});
~~~~

The `filteredConsoleSink` only logs messages whose property `elapsed` is greater
than or equal to 100 milliseconds to the console.

> [!TIP]
> The `withFilter()` function can take a [`LogLevel`] string as the second
> argument. In this case, the log messages whose log level is less than
> the specified log level are discarded.
[`withFilter()`]: https://jsr.io/@logtape/logtape/doc/~/withFilter


Testing
-------

Expand Down
1 change: 1 addition & 0 deletions logtape/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export {
type RotatingFileSinkOptions,
type Sink,
type StreamSinkOptions,
withFilter,
} from "./sink.ts";

// cSpell: ignore filesink
13 changes: 13 additions & 0 deletions logtape/sink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,27 @@ import fs from "node:fs";
import { isDeno } from "which_runtime";
import { debug, error, fatal, info, warning } from "./fixtures.ts";
import type { LogLevel } from "./level.ts";
import type { LogRecord } from "./record.ts";
import {
type FileSinkDriver,
getConsoleSink,
getFileSink,
getStreamSink,
type Sink,
withFilter,
} from "./sink.ts";

Deno.test("withFilter()", () => {
const buffer: LogRecord[] = [];
const sink = withFilter(buffer.push.bind(buffer), "warning");
sink(debug);
sink(info);
sink(warning);
sink(error);
sink(fatal);
assertEquals(buffer, [warning, error, fatal]);
});

interface ConsoleMock extends Console {
history(): unknown[];
}
Expand Down
22 changes: 22 additions & 0 deletions logtape/sink.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type FilterLike, toFilter } from "./filter.ts";
import {
type ConsoleFormatter,
defaultConsoleFormatter,
Expand All @@ -17,6 +18,27 @@ import type { LogRecord } from "./record.ts";
*/
export type Sink = (record: LogRecord) => void;

/**
* Turns a sink into a filtered sink. The returned sink only logs records that
* pass the filter.
*
* @example Filter a console sink to only log records with the info level
* ```typescript
* const sink = withFilter(getConsoleSink(), "info");
* ```
*
* @param sink A sink to be filtered.
* @param filter A filter to apply to the sink. It can be either a filter
* function or a {@link LogLevel} string.
* @returns A sink that only logs records that pass the filter.
*/
export function withFilter(sink: Sink, filter: FilterLike): Sink {
const filterFunc = toFilter(filter);
return (record: LogRecord) => {
if (filterFunc(record)) sink(record);
};
}

/**
* Options for the {@link getStreamSink} function.
*/
Expand Down

0 comments on commit 5a800eb

Please sign in to comment.