6 min read
I logged 100,000 lines with the same message and object through each library on this box, stdout redirected to a file: pino 10.4.0 wrote a median of about 371,000 lines per second, consola 3.4.2 about 155,000, so pino was roughly 2.4× faster. consola installs as 1 package at 448 KB on disk; pino installs as 13 packages (itself plus 12 dependencies) at 2,656 KB.
Short answer: pick pino for servers whose logs go to a file or a log shipper, and consola for CLIs, build tools and dev scripts where a human reads the output. No affiliate links in this post.
| 100k lines to a file (median of 7) | Lines/s | Range | Import ms |
|---|---|---|---|
| pino 10.4.0 default | 371,087 | 349,445–396,237 | 36.5 |
| pino, async destination | 363,663 | 319,118–395,330 | 36.5 |
| console.log + JSON.stringify | 184,516 | 167,736–223,928 | 0 |
| consola 3.4.2, fancy: false | 155,130 | 125,799–166,573 | 30.9 |
| consola, fancy: true forced (20k lines) | 19,709 | 16,121–20,576 | 29.8 |
pino beat even a hand-rolled console.log JSON line by about 2×, and consola's plain reporter sat a little below that baseline. Forcing the fancy reporter cost almost 8× against consola's own plain output, which is fine for a terminal and wasteful in CI logs. With stdout going to a file, the async destination made no measurable difference here. Writes to a pipe or a terminal behave differently, and the same buffering effect is measured in Python in stdout line vs full buffering. The same lines-per-second question for Python's logging module is measured in logging vs print.
Filtered calls matter too: one million debug() calls below the active level took 8.3 ms in pino and 21 ms in consola, so leaving debug lines in hot code is cheap in both.
| Build (esbuild minify, gzip -9) | Minified | gzip | Input files |
|---|---|---|---|
| consola, full entry (node) | 76,420 B | 22,762 B | 6 |
| consola/basic entry (node) | 6,772 B | 2,600 B | 4 |
| pino (node) | 63,288 B | 21,140 B | 30 |
| consola (browser) | 6,248 B | 2,428 B | 3 |
| pino (browser build) | 14,941 B | 5,425 B | 4 |
The surprise: bundled for Node, consola's full entry is slightly bigger than pino, most likely the fancy reporter and prompt code; I did not break the bundle down further. The consola/basic entry drops that to 2.6 KB gzip. On disk the picture flips: consola is a single dependency-free package, pino brings 12 dependencies, including sonic-boom and thread-stream. Color output in consola-style CLIs is its own size choice, measured in picocolors vs chalk, and size-limit vs bundlesize shows how to keep either number from creeping.
| Check | consola 3.4.2 | pino 10.4.0 |
|---|---|---|
| Output format | readable text, [info] request 0 plus the object | one JSON object per line |
| Bytes per line in this test | about 67 | about 148 (adds time, pid, hostname) |
| 10 identical messages within 1 s | 6 printed, then a "(repeated 4 times)" line | all 10 written |
| Default level | 3 (info) | info |
| Child loggers / tags | withTag() | child() |
| Redaction | no built-in option | redact paths, printed [Redacted] |
| Interactive prompts | prompt() built in | none |
The throttling row is the one that surprises people: consola collapses repeated identical messages by default (throttle 1,000 ms, throttleMin 5), which is friendly in a terminal and confusing when you count log lines in production. pino keeps every line and leaves pretty printing to a transport running in a worker thread, the same worker model compared in tinypool vs piscina. If your logs end up in a metrics and tracing stack, the collector side is walked through in OpenTelemetry Collector, Prometheus and Grafana.
| Situation | My pick |
|---|---|
| HTTP API or worker writing to files or a log shipper | pino |
| Need redaction of secrets in logs | pino |
| CLI, build tool or dev script read by humans | consola |
| Need prompts plus pretty output in one package | consola |
| Tiny bundled CLI | consola/basic |
Bottom line: on this box pino 10.4.0 wrote about 371,000 JSON lines per second versus about 155,000 for consola 3.4.2, with 13 installed packages versus 1 and JSON lines versus readable text. Who should not switch to pino: CLIs and scripts where people read the output and JSON lines just get in the way. Who should not stay on consola: services that log at volume, need every line kept, or need machine-parsable logs.
How this was made: I installed both packages on the ShopperCove box, ran each logger in fresh Node processes with stdout redirected to a file, took the median of 7 order-alternated rounds, bundled each entry with esbuild, and checked throttling, levels and redaction with in-memory streams. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.
Lab evidence
Hands-on on ShopperCove box 6 Oct 2026 ~03:05 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 20.19.2). consola 3.4.2 vs pino 10.4.0, esbuild 0.28.2 minify + gzip -9. Fresh process per run, stdout redirected to a local file, 2,000 warm-up + 100,000 timed lines (message + counter/user/path object), median of 7, order alternated (lines/s): pino 371,087 (349,445-396,237); pino async sonic-boom 363,663; console.log+JSON.stringify 184,516; consola fancy:false 155,130 (125,799-166,573); consola fancy:true forced 19,709 (20k lines). Import ~36.5 vs ~30.9 ms. 1e6 filtered debug(): pino 8.3 ms, consola 21 ms. Bytes/line ~148 vs ~67. Bundle node gzip: consola full 22,762 B, consola/basic 2,600 B, pino 21,140 B; browser 2,428 vs 5,425 B. Install 1 pkg 448 KB vs 13 pkgs 2,656 KB. consola throttles 10 identical messages to 6 + repeated line; pino writes all 10; pino redact built in. Not tested: transports/pino-pretty, real TTY, shippers, Bun/Deno, Node 22+. No affiliate.