Ship errors to Slack (or any external channel)
You want error (and maybe warn) entries to land in a Slack channel so the team sees production failures without tailing a file. The logger doesn’t bundle network sinks, but a custom channel is ~15 lines: extend LogChannel, filter to the levels you care about, and POST.
The Slack channel
Section titled “The Slack channel”import { LogChannel, type BasicLogConfigurations, type LoggingData } from "@warlock.js/logger";
// Extend the base config so the inherited `levels`, `filter`, and `redact`// options are part of the type the channel accepts.type SlackConfig = BasicLogConfigurations & { webhookUrl: string;};
export class SlackLog extends LogChannel<SlackConfig> { public name = "slack";
public async log(data: LoggingData) { // Inherit the levels whitelist + filter predicate for free. if (!this.shouldBeLogged(data)) { return; }
const { type, module, action, message } = data;
await fetch(this.config("webhookUrl"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: `:rotating_light: *[${type.toUpperCase()}]* \`${module}\` › \`${action}\`\n${message}`, }), }); }}Register it for errors only
Section titled “Register it for errors only”import { log, ConsoleLog, FileLog } from "@warlock.js/logger";import { SlackLog } from "./channels/slack-log";
log.setChannels([ new ConsoleLog(), // everything to the terminal new FileLog({ chunk: "daily" }), // everything to disk new SlackLog({ webhookUrl: process.env.SLACK_WEBHOOK_URL!, levels: ["error", "warn"], // ← only these reach Slack }),]);The levels: ["error", "warn"] whitelist means debug / info / success never hit the network — shouldBeLogged drops them before your fetch runs. The console and file channels still see everything; filtering is per channel.
Don’t let a flaky webhook crash your app
Section titled “Don’t let a flaky webhook crash your app”A channel’s log() is fired without being awaited by the logger, so an unhandled rejection from fetch could take down the process. Swallow transport failures inside the channel:
public async log(data: LoggingData) { if (!this.shouldBeLogged(data)) { return; }
try { await fetch(this.config("webhookUrl"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: this.format(data) }), }); } catch { // A logging sink must never be the thing that crashes the app. // Drop the Slack delivery; the console + file channels still recorded it. }}Redact before it leaves your machine
Section titled “Redact before it leaves your machine”Slack is an external destination — scrub secrets harder for it than for your local file. Per-channel redact paths are additive on top of the logger floor (a channel can redact more, never less):
new SlackLog({ webhookUrl: process.env.SLACK_WEBHOOK_URL!, levels: ["error"], redact: { paths: ["context.user.email", "context.token"], },});See Redaction for how the floor and per-channel paths combine.
See also
Section titled “See also”- Custom Channel — the full
LogChannelcontract, theinit()hook, andflushSync - Filtering — the
levelswhitelist andfilterpredicate - Redaction — additive per-channel redaction