| name | filter-log-entries |
| description | Drop log entries — per-channel levels whitelist, per-channel filter predicate, logger-wide setMinLevel(level) fast path. Triggers: `levels`, `filter`, `minLevel`, `log.setMinLevel`, `shouldBeLogged`, `LoggingData`, `LogLevel`; "silence a noisy module", "route errors to a dedicated file", "raise global severity floor", "drop debug logs in prod"; typical import `import { log } from "@warlock.js/logger"`. Skip: custom sinks — `@warlock.js/logger/write-custom-log-channel/SKILL.md`; channel picks — `@warlock.js/logger/pick-log-channel/SKILL.md`; competing libs `pino.levels`, `winston.format.filter`, `debug` env var. |
Filtering — levels + filter predicate + minLevel
Every channel can silently drop entries it doesn't care about. Three mechanisms stack: a logger-wide minLevel floor (cheapest), then per-channel levels whitelist, then per-channel filter predicate.
1. levels — the per-channel whitelist
new FileLog({ levels: ["error", "warn"] });
- Omitting
levels (or passing []) means allow all six.
- No regex / no range — it's a literal whitelist of
LogLevel strings.
2. filter — the per-channel custom predicate
new ConsoleLog({
filter: (data) => data.module !== "healthcheck",
});
data is the full LoggingData: { type, module, action, message, context? }.
- Predicate runs after
levels — an entry blocked by levels never reaches filter.
3. minLevel — the logger-wide severity floor
For the common "drop everything below X" case, skip the per-channel levels array and use the logger-wide fast path:
log.setMinLevel("info");
log.configure({ minLevel: "warn" });
Severity ordering: debug < info ≈ success < warn < error < fatal. success is treated as informational severity — setMinLevel("warn") drops it. fatal is strictly above error, so setMinLevel("fatal") admits only fatal entries (handy for "page me only on fatal" routing).
Pass undefined to clear:
log.setMinLevel(undefined);
This runs before the channel loop — cheaper than per-channel levels filters when you want a uniform floor. Per-channel levels and filter still run on top for channels that need a tighter or differently-shaped rule.
Combining — real patterns
Route errors to a dedicated file
log.setChannels([
new ConsoleLog(),
new FileLog({
name: "errors",
levels: ["error", "warn"],
chunk: "daily",
}),
]);
Silence a noisy module
new ConsoleLog({
filter: (data) => data.module !== "socket.io",
});
Keep the dev terminal focused
new ConsoleLog({
filter: (data) => data.module === "auth",
});
Errors always pass, info only for one module
new ConsoleLog({
filter: (data) => data.type === "error" || data.module === "payments",
});
Where filtering happens
LogChannel.shouldBeLogged(data) runs both checks in order:
if (this.config("levels")?.length && !this.config("levels").includes(data.type)) return false;
const filter = this.config("filter");
if (filter) return filter(data);
return true;
If you extend LogChannel to write a custom channel, call this.shouldBeLogged(data) first thing inside your log(data) method — you inherit both mechanisms for free. See @warlock.js/logger/write-custom-log-channel/SKILL.md.
Logger-wide custom filtering — not a thing
There is no logger.setGlobalFilter(). Each channel filters itself. If you want the same predicate everywhere, pass it to every channel constructor (or wrap your channels in a helper).
Performance note
Filters run on every entry per channel. A synchronous, cheap predicate is fine. Avoid await inside — the channel receives a fully-formed LoggingData and the filter is sync-only (type: (data: LoggingData) => boolean).
The minLevel check is the fastest of the three (single comparison before fan-out), so prefer it when "drop everything below X uniformly" matches your need.