Skip to content
Warlock.js v4

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.

await 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
await log.info("auth", "login", "User signed in");
await log.warn("auth", "tokenExpired", "JWT expired for session");
// Payments — message in the 3rd arg, structured data in the 4th (context)
await log.info("payments", "chargeInitiated", "Charge initiated", { amount: 4999, currency: "USD" });
await log.error("payments", "chargeFailed", new Error("Stripe declined card"));
// Email
await log.success("email", "welcomeSent", "Welcome email delivered");
await log.warn("email", "bounced", "Delivery failed — invalid address");
// Orders
await log.debug("orders", "priceCalculation", "Calculated cart total", { subtotal: 120, discount: 10 });
await 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()Failures that need attention; accepts Error objectsred
successlog.success()Explicit happy-path confirmation (payment completed)green
await log.debug("cache", "miss", "Key not found, fetching from DB");
await log.info("jobs", "queued", "SendInvoice job added to queue");
await log.warn("api", "rateLimitApproaching", "80% of quota used");
await log.error("db", "connectionFailed", new Error("ECONNREFUSED 5432"));
await log.success("payments", "captured", "Payment of $49.99 captured");

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

// Fourth argument — context
await 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,
},
};
await 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) {
await 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: