DEV Community

Cover image for Log inside a loop without touching the code
JSLab
JSLab

Posted on

Log inside a loop without touching the code

Loops are where console.log goes to multiply. One log per pass is fine for 8 passes and useless for 8,000. Here are the techniques I reach for, from "edit the code" to "don't touch it at all".

The loop we'll debug finds spikes in a list of readings:

const readings = [12, 15, 11, 40, 14, 13, 90, 12];
const spikes = [];

for (let i = 0; i < readings.length; i++) {
  const delta = readings[i] - (readings[i - 1] ?? readings[i]);
  if (delta > 20) spikes.push(i);
}

spikes; // [3, 6]
Enter fullscreen mode Exit fullscreen mode

1. Log only what matters

Logging every pass buries the answer. Log the passes you care about:

for (let i = 0; i < readings.length; i++) {
  const delta = readings[i] - (readings[i - 1] ?? readings[i]);
  if (delta > 20) console.log({ i, delta });
  if (delta > 20) spikes.push(i);
}
// { i: 3, delta: 29 }
// { i: 6, delta: 77 }
Enter fullscreen mode Exit fullscreen mode

Logging an object ({ i, delta }) instead of two loose values means every line says what it is.

2. Collect rows, print a table once

When you do need every pass, collect them and print once at the end:

const trace = [];
for (let i = 0; i < readings.length; i++) {
  const delta = readings[i] - (readings[i - 1] ?? readings[i]);
  trace.push({ i, reading: readings[i], delta });
  if (delta > 20) spikes.push(i);
}
console.table(trace);
Enter fullscreen mode Exit fullscreen mode

One table with eight rows reads far better than eight scattered logs, and you can see the pattern (a big positive delta, then a big negative one) at a glance.

3. console.count for "how often?"

Sometimes you don't need the values, just how many times a branch ran:

if (delta > 20) console.count("spike");
// spike: 1
// spike: 2
Enter fullscreen mode Exit fullscreen mode

4. Don't edit the code: logpoints

Every technique above changes the loop. That's a risk: you add a log, the bug moves, or the log ships. Debuggers solved this long ago with logpoints, a breakpoint that logs instead of pausing. Browser DevTools and most editors have them.

JSLab, a JavaScript & TypeScript scratchpad for macOS, has them too, without starting a debugger. Click the gutter beside a line, or press F9 with the caret on it, and that line is logged on every run. Its text doesn't change.

Put a logpoint on the const delta = … line and you get one row per pass, each tagged with that line, while the code stays exactly as you wrote it.

Here's a logpoint in a real JSLab tab: the dot beside line 5, and the 33.74 row tagged :5 that it produced. Line 5 itself is unchanged.

A JSLab tab showing a docs example. A blue logpoint dot sits in the gutter beside line 5, const rounded = Math.round(total * 100) / 100. The output shows 33.74 tagged :5 and Total: 33.74 tagged :8.

A few details from the docs worth knowing:

  • A logpoint follows its line as you edit above or below it.
  • Toggling one schedules a run, even if nothing else changed.
  • ⇧⌘F9 clears every logpoint in the tab.
  • Logpoints belong to one tab and aren't saved, so they can't leak into a file.

If you'd rather mark it in the code, a magic comment before the loop's opening brace logs each pass too:

for (const r of readings) /*?*/ {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Bonus: the loop that never ends

While you're editing a loop, you'll write while (i < 10) and forget i++. In JSLab, loop protection stops a loop after 2,000 iterations by default and the error names the line, so a slip doesn't hang the run. The limit is Loop Limit in Settings → Advanced.

Which one when

  • Need the answer once: a conditional log.
  • Need to see the pattern: collect rows, console.table.
  • Don't want to touch the code: a logpoint.

JSLab launches Nov 15. Logpoints and loop protection are documented here: https://js-lab.app/docs/editor

Top comments (0)