# debug-js/debug: namespace-based logging for Node.js and the browser

> The debug package turns console output into opt-in, namespaced channels you enable with the DEBUG environment variable. It is a small tool with a narrow job, and the README is honest about where it stops.

**debug-js/debug** — A tiny JavaScript debugging utility modelled after Node.js core's debugging technique. Works in Node.js and web browsers

- Repository: https://github.com/debug-js/debug
- Stars: 11,456 · Forks: 994
- Language: JavaScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/debug-js-debug

## The problem debug-js/debug solves, and who it is for

Most code that logs has two bad options. Either every message goes to the console and the output is unreadable, or the messages are wrapped in conditionals that nobody removes. debug-js/debug takes a third path: the logging call sites stay in the source, and the decision about whether they print is made outside the program, at the moment you run it.

The README describes it as "a tiny JavaScript debugging utility modelled after Node.js core's debugging technique", working in Node.js and web browsers. The intended audience is library authors and application developers who want their own diagnostics to be available but off by default. The README's conventions section is explicit about the library case: if you use it in one or more of your libraries, you "should" use the name of your library as the namespace, so that other developers can toggle your output without guessing names. If a library has several debuggers, the README says to prefix them with the library name and separate features with a colon, giving "connect:bodyParser" as the example.

That naming rule is the whole social contract of the package. A namespace is a public identifier, and once you ship it, people will type it into an environment variable. Renaming it later breaks their command lines.

## How the namespace mechanism works

The package exports a function. You call it with a name, and it returns a decorated version of console.error that you pass debug statements to. That returned function is what carries the namespace, the color and the enabled/disabled state.

Enablement is driven by the DEBUG environment variable, which the README says is space or comma delimited. The matching rules are simple and worth knowing exactly. A literal name matches itself. The asterisk is a wildcard, so DEBUG=connect:* turns on connect:bodyParser, connect:compress and connect:session at once. A leading minus excludes, so DEBUG=*,-connect:* enables everything except namespaces beginning with connect:. There is also an escape hatch documented under conventions: if you append a * to the end of a name, it is always enabled regardless of the DEBUG setting, which the README suggests using for normal output as well as debug output.

Output formatting is printf-style. The README lists six supported formatters: %O for a multi-line object, %o for a single-line object, %s for strings, %d for numbers, %j for JSON (replaced by the string '[Circular]' when the argument has circular references), and %% for a literal percent sign, which does not consume an argument. When stderr is a TTY, debug shows the elapsed time since the previous call as "+NNNms"; when stdout is not a TTY, the README says Date#toISOString() is used instead, which makes the output more suitable for log files. That switch is automatic and based on whether the stream is a terminal, not on a flag you set.

## Installing debug-js/debug and enabling a first namespace

The install step in the README is a single npm command. The package ships src, LICENSE and README.md according to package.json, with main pointing at ./src/index.js and browser at ./src/browser.js, so bundlers pick the browser entry automatically.

```bash
npm install debug
```

After installing, require the module and bind it to a namespace. The README's app example uses 'http' as the namespace and calls the returned function with a format string.

```js
var debug = require('debug')('http')
  , http = require('http')
  , name = 'My App';

debug('booting %o', name);

http.createServer(function(req, res){
  debug(req.method + ' ' + req.url);
  res.end('hello\n');
}).listen(3000, function(){
  debug('listening');
});
```

Running that file without setting DEBUG produces no output from the debug calls. To see them, set the variable for the process. On a POSIX shell the README's pattern is a space or comma delimited list, and DEBUG=* turns on everything.

```bash
DEBUG=http node app.js
DEBUG=* node app.js
```

On Windows the syntax differs and the README covers both shells. In CMD the variable is set with the set command, and in PowerShell with $env:.

```cmd
set DEBUG=* & node app.js
```

```cmd
$env:DEBUG='app';node app.js
```

The README also gives an npm script form for PowerShell, "windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js". If you see nothing after setting the variable, the namespace string is the first thing to check: it must match exactly, or match a wildcard you enabled.

## Colors, supports-color and the DEBUG_ variables

Every debug instance gets a color derived from its namespace name, which is what makes interleaved output from several modules readable. In Node.js the README states that colors are enabled when stderr is a TTY, and that you should install the supports-color module alongside debug, otherwise debug will only use a small handful of basic colors. That is a real installation detail people miss: the package has exactly one runtime dependency, ms, and supports-color is listed under peerDependenciesMeta as optional. Nothing installs it for you.

In browsers, colors are enabled on web inspectors that understand the %c formatting option, which the README identifies as WebKit inspectors, Firefox since version 31, and the Firebug plugin for Firefox.

Beyond DEBUG itself, the README documents four environment variables that change output: DEBUG_HIDE_DATE hides the date in non-TTY output, DEBUG_COLORS controls whether colors are used, DEBUG_DEPTH sets object inspection depth, and DEBUG_SHOW_HIDDEN shows hidden properties on inspected objects. The README notes that variables beginning with DEBUG_ are converted into an Options object used with the %o and %O formatters, and points at the Node.js util.inspect() documentation for the complete list. That pointer matters because the README does not enumerate the options itself; if you need a specific inspect option, you are reading the Node.js docs, not this one.

## Where debug-js/debug is the wrong tool

This is a debugging utility, not a logging system, and the gap is wide. There are no log levels, so there is no way to say "errors always, warnings in staging, info never". There is no transport abstraction: output goes to console.error in Node.js and to the console in browsers, and if you want it in a file, a syslog socket or an aggregator, that is your problem. There is no rotation, no sampling and no rate limiting. A debug call inside a hot loop will print on every iteration once the namespace is enabled, because the only gate is the enabled flag.

The namespace model has its own failure mode. Because enablement is global to the process and driven by an environment variable, you cannot change it at runtime through the public API described in the README. A long-running server that starts without DEBUG set stays quiet until it restarts. For a short-lived CLI or a test run that is fine; for a service where you want to raise verbosity during an incident, the mechanism does not help you.

The README is also silent on several things a production user would ask about. There is no documented rollback procedure, no migration guide between major versions, and no documented policy on how long deprecated namespaces are kept. If you are pinning debug in a library's dependency tree, that silence is the risk you are accepting.

## How debug-js/debug differs from a structured logger

The closest alternative in the Node.js ecosystem is a structured logging library such as pino, which takes a different approach to the same problem. Where debug builds a human-readable line from a printf format string and writes it to console.error, a structured logger emits an object per event with a level, a timestamp and arbitrary fields, then serializes it to JSON for a transport to consume. That difference decides the use case. If you want to grep a terminal during development, debug's output is already in the shape you want. If you want to ship logs to an aggregator and query them by field, JSON objects are the shape you want, and debug gives you none of that.

The trade-offs run both ways. Structured loggers carry configuration for levels and transports, and they expect you to decide those things up front. debug has one runtime dependency and a namespace string, and the README's own framing is that it is modelled after Node.js core's debugging technique. It is also worth noting that debug and a structured logger are not mutually exclusive; a library can use debug for its internal diagnostics and let the application keep its own structured logging for business events. The mistake is treating debug as the application's log pipeline.

For browser work, the comparison is different again. Browser devtools already have console filtering and log levels, so debug's value there is mostly the namespace convention and the consistent %c coloring rather than the enable/disable switch.

## Maintenance, licence and what upgrading costs

The repository is not archived, and the last push was on 2026-04-01. The most recent release listed is 4.4.3 on 2025-09-13, preceded by 4.4.1 on 2025-05-13 and 4.4.0 on 2024-12-06. The release cadence is slow and the 4.x line has been stable for a long time, which for a dependency this small is closer to a feature than a warning sign. There is no changelog content in the repository beyond the version numbers, so the cost of a 4.x to 4.y upgrade cannot be assessed from what is documented here.

The licence is MIT, stated in package.json and present as a LICENSE file at the repository root. MIT is permissive: it allows use, modification and redistribution with the licence and copyright notice retained. That is a description of the licence text, not legal advice; if your organisation has rules about attribution in distributed binaries, read the LICENSE file yourself.

The runtime footprint is one dependency, ms, with a caret range of ^2.1.3. The engines field declares node >=6.0, so the package will install on very old Node.js versions, though that says nothing about whether the rest of your stack will. The test setup is split: mocha runs test.js and test.node.js for Node.js, karma runs the browser suite, and xo handles linting, all wired through npm scripts. If you vendor or fork the package, those scripts are the entry points you would need to keep working.

## Conclusion

Adopt debug-js/debug when you want per-module log channels that stay silent in production until someone sets DEBUG. Do not adopt it as an application logging framework: it has no levels, no transports, no rotation and no structured output. Before adding it to a library, check the namespace convention in the README and decide whether you also want to list supports-color, since colors in Node.js are limited without it. Verify the Node.js version your project targets against the engines field in package.json.

## FAQ

### How do I install debug-js/debug?

The README gives a single command, npm install debug. The package's main entry is ./src/index.js and its browser entry is ./src/browser.js, so bundlers select the browser build automatically.

### How do I enable debug output for a specific namespace?

Set the DEBUG environment variable to the namespace before starting the process, for example DEBUG=http node app.js. The README says the value is space or comma delimited, and DEBUG=* enables everything.

### Why does debug-js/debug show only a few colors in Node.js?

The README states that colors are enabled when stderr is a TTY, and that you should install the supports-color module alongside debug, otherwise debug will only use a small handful of basic colors. supports-color is listed as an optional peer dependency, so it is not installed automatically.

### How do I set the DEBUG variable on Windows?

The README gives separate syntax for each shell: in CMD use set DEBUG=* & node app.js, and in PowerShell use $env:DEBUG='app';node app.js. It also shows an npm script form, "windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js".

### Can I exclude some namespaces while enabling everything else?

Yes. The README documents a leading minus for exclusion, so DEBUG=*,-connect:* includes all debuggers except those starting with connect:.

## Sources

- [debug-js/debug on GitHub](https://github.com/debug-js/debug)
- [Issues](https://github.com/debug-js/debug/issues)
- [License: MIT](https://github.com/debug-js/debug/blob/master/LICENSE)
- [README](https://github.com/debug-js/debug/blob/master/README.md)
- [Releases](https://github.com/debug-js/debug/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/debug-js-debug
