Can Deno's std/log assign different log levels to console and file handlers?
0 reputation · 10 Apr 2026, 13:27 UTC
0 reputation · 10 Apr 2026, 13:27 UTC
I am building a Deno application that uses the standard logging module (std/log) to output diagnostic information. I want the console to show only warnings and errors so that informational messages do not create noise during development, while still retaining a complete log file that records every level, including debug and info, for later analysis. The application creates a logger instance, attaches both a console handler and a file handler, and then logs messages at various levels throughout the code.
Given that the std/log API allows configuring handlers and log levels globally, I am unsure whether it is possible to assign a distinct minimum level to each handler (e.g., warn for console, debug for file) or if I must create separate logger instances with different configurations. How can I achieve per‑handler level filtering without introducing additional complexity or sacrificing the unified logging interface?
Yes, Deno's std/log module supports assigning different minimum log levels to individual handlers. You can configure a console handler to show only warnings and errors while a file handler records everything from debug upward — all within a single logger instance.
In std/log, filtering happens at two layers:
The effective level for a given output is the more restrictive of the two. So if your logger is set to DEBUG and your console handler is set to WARNING, the console shows only warnings and errors, while a file handler set to DEBUG receives everything.
Using the setup() function, you define handlers with their own levels and then attach them to a logger:
import * as log from "jsr:@std/log";
await log.setup({
handlers: {
console: new log.handlers.ConsoleHandler("WARNING"),
file: new log.handlers.FileHandler("./app.log", {
level: "DEBUG",
}),
},
loggers: {
default: {
level: "DEBUG",
handlers: ["console", "file"],
},
},
});
log.debug("debug message"); // file only
log.info("info message"); // file only
log.warning("warning message"); // console + file
log.error("error message"); // console + fileLevel names are case-insensitive strings ("DEBUG", "INFO", "WARNING", "ERROR") or numeric values. The logger level must be at least as permissive as your most verbose handler — here DEBUG — or the handler will never see lower-severity messages.
Run the script with deno run --allow-write --allow-read your_script.ts. Check that the console output contains only warnings and errors, then inspect app.log to confirm debug and info lines are present. You can also call log.getHandlers() to inspect the configured levels programmatically.
This behavior is stable in current std/log releases (e.g., jsr:@std/log 0.224+). If you import from deno.land/std/log, verify the version you pin matches the API described above.
No separate logger instances are needed — one logger with per-handler levels gives you the unified interface you want.
Use comments to ask for clarification. Post a solution as an answer.
26,525 reputation · 10 Apr 2026, 14:16 UTC
Besides setting a minimum level on each handler, you can further refine what gets written by attaching a Filter to a handler. A filter receives the LogRecord and returns true to let it through or false to discard it, independent of the level check. For example, you could suppress all messages from a particular module while still honoring the handler’s level:
import * as log from "jsr:@std/log";
const fileHandler = new log.handlers.FileHandler("./app.log", { level: "DEBUG" });
fileHandler.setFilter((record) => !record.loggerName.includes("noisy_module"));
await log.setup({
handlers: { file: fileHandler },
loggers: { default: { level: "DEBUG", handlers: ["file"] } },
});
This lets you combine level‑based filtering with custom logic without creating additional logger instances.