Namespaces and filtering
Namespaces and runtime filtering
Beyond the build-time levels / minLevel allowlist, $debug carries runtime controls so you can change what shows without editing config or restarting, and scope logs by namespace.
const auth = $debug.ns("auth"); // scoped logger, tags every call [auth]
auth("info", "login ok"); // → MARS-TECH Info [auth] - login ok
auth("debug", payload, { tag: "token" }); // namespace becomes "auth:token"
$debug.enable("auth:*,-auth:token"); // show auth:* except auth:token
$debug.disable(); // mute everything
$debug.enable(null); // drop the runtime override → back to the plugin's `filter` default
$debug.setLevel("warning"); // raise the floor (within the build set)
$debug.setLevel(null); // clear the runtime level overrideThe namespace is the tag: $debug("info", "x", { tag: "auth" }) and $debug.ns("auth")("info", "x") filter identically. With no filter set, every call shows.
Filter syntax (à la the debug package): comma/space-separated terms, * wildcard, leading - to exclude (exclusions win). A call with no tag has an empty namespace: a filter that names namespaces (auth:*) hides it, while a filter made only of exclusions (-noisy) lets everything else through, untagged calls included.
Where the filter comes from, highest priority first:
| Surface | Namespace filter | Level floor |
|---|---|---|
| Browser | ?mt-debug=auth:* URL param → localStorage["mt-debug"] | ?mt-debug-level= → localStorage["mt-debug-level"] |
| SSR/Node | MT_DEBUG=auth:* env | MT_DEBUG_LEVEL=warning env |
Below these sits the build-time default set with the filter plugin option, which $debug.enable(null) returns to.
enable / disable / setLevel persist to localStorage in the browser (so the choice survives reloads); on the server they apply for the process lifetime. Runtime narrowing is bounded by the build-time level set: you can hide compiled levels, not resurrect stripped ones.
Filters match the real namespace, even when redaction masks it in the output: ?mt-debug=-user:* still mutes $debug.ns("user:" + email).
Prefix filtering
A second, orthogonal axis filters by the rendered prefix, the head of each line ([MARS-TECH]-style). With brandPrefix a prefix is a per-source identity, so this is how you mute a noisy library or isolate your app's own logs:
$debug.setPrefixFilter("MyApp"); // show only "MyApp"-prefixed logs
$debug.setPrefixFilter("-NoisyLib"); // hide everything from "NoisyLib"
$debug.setPrefixFilter(null); // clear → all prefixes passSame debug-style syntax as the namespace filter (*, comma/space-separated, leading - excludes). Sources, highest priority first:
| Surface | Prefix filter |
|---|---|
| Browser | ?mt-debug-prefix=MyApp URL param → localStorage["mt-debug-prefix"] |
| SSR/Node | MT_DEBUG_PREFIX=MyApp env |
| Build-time default | mtDebug({ prefixFilter: "-NoisyLib" }) plugin option |
In the browser, setPrefixFilter persists to localStorage like enable.
Matched against each call's effective prefix (options.prefix if set, else the configured prefix), before any redaction. Under test: 'log', the test console logger honors it too.
Caveat
Utility helpers (time/count/group) always carry the configured prefix, so a library's helper output filters under the app prefix, not the library's.