Skip to content

Your first log

Once a channel is attached, you log through the log singleton. Every entry follows the same shape: module, action, message, and an optional context object.

Every log call takes three positional arguments — module, action, message — plus an optional fourth context object for structured data.

log.info("users", "register", "New user created", { userId: 42 });
// ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
// module action message context (optional)

The first three let you filter and search logs by feature area instead of wading through flat text; the fourth carries the structured metadata. Real-world examples across domains:

// Auth
log.info("auth", "login", "User signed in");
log.warn("auth", "tokenExpired", "JWT expired for session");
// Payments — message in the 3rd arg, structured data in the 4th (context)
log.info("payments", "chargeInitiated", "Charge initiated", { amount: 4999, currency: "USD" });
log.error("payments", "chargeFailed", new Error("Stripe declined card"));
// Email
log.success("email", "welcomeSent", "Welcome email delivered");
log.warn("email", "bounced", "Delivery failed — invalid address");
// Orders
log.debug("orders", "priceCalculation", "Calculated cart total", { subtotal: 120, discount: 10 });
log.success("orders", "fulfillment", "Order shipped via FedEx");
LevelMethodWhen to useConsole style
debuglog.debug()Dev diagnostics, variable dumps, flow tracingmagenta
infolog.info()Normal operational events (user registered, job queued)blue
warnlog.warn()Unexpected but recoverable conditions (retry, fallback)yellow
errorlog.error()Handled failures that need attention; app continuesred
successlog.success()Explicit happy-path confirmation (payment completed)green
fatallog.fatal()Unrecoverable failures; the app is going down (boot fail, uncaughtException)bright red on red background
log.debug("cache", "miss", "Key not found, fetching from DB");
log.info("jobs", "queued", "SendInvoice job added to queue");
log.warn("api", "rateLimitApproaching", "80% of quota used");
log.error("db", "connectionFailed", new Error("ECONNREFUSED 5432"));
log.success("payments", "captured", "Payment of $49.99 captured");
log.fatal("config", "missingSecret", new Error("STRIPE_KEY is required to boot"));

To attach arbitrary metadata, pass a fourth context argument, or pass a full LoggingData object to log.log(...).

// Fourth argument — context
log.info("orders", "checkout", "Order placed", {
orderId: "ord_9f2a",
userId: 101,
amount: 4999,
});
// Object form — log.log(...)
import { log, type LoggingData } from "@warlock.js/logger";
const entry: LoggingData = {
type: "error",
module: "orders",
action: "checkout",
message: "Card declined",
context: {
orderId: "ord_9f2a",
userId: 101,
amount: 4999,
retryCount: 2,
},
};
log.log(entry);

File and JSON channels persist context alongside the entry. The console channel renders it on a second line only when its showContext option is enabled — see Console Channel.

Pass an Error instance directly as the message argument. File and JSON channels capture the stack trace automatically.

try {
await processPayment(orderId);
} catch (err) {
log.error("payments", "processPayment", err);
}

Two shortcuts every Logger exposes.

timer(module, action) — measure a duration

Section titled “timer(module, action) — measure a duration”

Returns an end-function that emits an info entry with a durationMs field in context when called.

const end = log.timer("db", "users.findById");
const user = await usersRepo.findById(id);
end({ id, found: !!user });
// → info "db" "users.findById" "completed in 42ms" { durationMs: 42, id, found: true }

Pass an object to the end-function to merge extra fields into the context.

assert(condition, module, action, message, context?) — log on a failed invariant

Section titled “assert(condition, module, action, message, context?) — log on a failed invariant”

Logs an error entry only when condition is falsy. A no-op otherwise — the entry is never built and channels aren’t invoked, so it’s free on the happy path. Like console.assert, but it routes through the logger pipeline so persistent channels capture the failure.

log.assert(user !== null, "auth", "session", "user vanished mid-flight", { sessionId });

You’ve covered the call-site API. Next, learn how channels decide what to emit and where — start with the Channels Overview.

Or jump straight to a task-shaped guide: