Skip to content

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.

ts
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 override

The 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:

SurfaceNamespace filterLevel floor
Browser?mt-debug=auth:* URL param → localStorage["mt-debug"]?mt-debug-level= → localStorage["mt-debug-level"]
SSR/NodeMT_DEBUG=auth:* envMT_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:

ts
$debug.setPrefixFilter("MyApp"); // show only "MyApp"-prefixed logs
$debug.setPrefixFilter("-NoisyLib"); // hide everything from "NoisyLib"
$debug.setPrefixFilter(null); // clear → all prefixes pass

Same debug-style syntax as the namespace filter (*, comma/space-separated, leading - excludes). Sources, highest priority first:

SurfacePrefix filter
Browser?mt-debug-prefix=MyApp URL param → localStorage["mt-debug-prefix"]
SSR/NodeMT_DEBUG_PREFIX=MyApp env
Build-time defaultmtDebug({ 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.