블로그 목록
실험실 리포트

Pino와 Console JSON 로그, 이 조건에서는 Console이 빨랐어요

격리 환경에서 구조화 로그를 기록하자 Pino 중앙값은 46.348412ms, Console 기반 JSON 로그는 24.277994ms였어요. 출력 소비 비용을 뺀 이 비교에서는 Pino가 더 빨라야 한다는 가설과 반대 방향이었고, 로거 선택 전 워크로드를 분리해 재야 한다는 결과예요.

2026년 10월 21일 5분 읽기

2026-10-21 기준 이 사이트의 격리 환경에서 구조화 로그를 기록한 중앙값은 Pino 46.348412ms, Console 기반 JSON 로그 24.277994ms였어요. 출력 소비 비용을 뺀 이 비교에서는 Pino보다 Console 기반 JSON 로그가 시간이 덜 걸렸어요.

README가 제시한 속도 주장

Pino README는 대안과 비교했을 때 속도가 5배를 넘는 경우가 많다고 제시해요. 이 수치는 이번 측정값이 아니라 README 원문의 주장이고, 이번 실험은 그 문구 전체를 재현하려는 비교가 아니에요.

이번 가설은 더 좁았어요. 출력 소비 비용을 제거한 구조화 로그 기록에서 Pino의 중앙값이 Console 기반 JSON 로그보다 낮은지를 확인하는 것이었어요.

이 사이트의 격리 환경과 비교 조건

이 사이트의 격리 환경은 aarch64, Neoverse-N1 x2와 CPUQuota 150%였어요. 메모리는 2048MB, 운영체제는 Ubuntu 24.04.4 LTS, 런타임은 Node.js 24.21.0이었어요.

README가 포괄하는 다양한 대안과 달리, 여기서는 Node 런타임의 Console 기반 JSON 로그만 기준선으로 뒀어요. 각 구현에는 결정적으로 생성한 요청 완료 레코드를 같은 순서와 메시지로 기록했어요.

각 구현은 측정 전에 한 번 워밍업했어요. 로그 출력은 같은 메모리 폐기 스트림으로 보냈고, 이 사이트의 격리 환경에서 7회 실행했어요.

결과: 구조화 로그 기록 시간

이번 조건에서 Pino 중앙값은 46.348412ms, Console 기반 JSON 로그 중앙값은 24.277994ms였어요. 중앙값만 보면 Pino가 더 빠를 것이라는 가설과 다른 방향이에요.

구조화 로그 기록 시간
Pino 10.4.0 (실험 대상)Console JSON 로그 (기준)
중앙값최소–최대회차별 값
로그 기록 시간 ms · 짧을수록 좋음
Pino 1.91배 느림
Pino7회
Console JSON 로그7회

그림. 같은 입력을 7회 측정했어요. 막대는 중앙값, 아래 선은 최소–최대, 점은 회차별 값이에요. 환경: aarch64 · Neoverse-N1 x2, CPUQuota 150% · 2048MB · Node.js 24.21.0 · 원시 데이터(JSON)

표로 보기 · 7회 원자료
로그 기록 시간 (ms)
회차PinoConsole JSON 로그
145.1624.95
246.7425.44
345.8824.16
446.4423.95
545.8223.80
647.0925.15
746.3524.28
중앙값46.3524.28
최소45.1623.80
최대47.0925.44
구현중앙값최소최대단위
Pino46.34841245.16244247.091218ms
Console 기반 JSON 로그24.27799423.80255125.442044ms

Pino의 값은 45.162442ms부터 47.091218ms 사이였어요. Console 기반 JSON 로그는 23.802551ms부터 25.442044ms 사이였어요.

결과 그림과 회차별 값은 원시 데이터(JSON)에서 확인할 수 있어요. 표의 수치와 그림은 같은 7회 실행 결과를 가리켜요.

같은 방향인지 판단할 수 없는 이유

README의 5배 주장은 다양한 대안을 포괄하는 문구예요. 반면 이 실험은 Console 기반 JSON 로그 하나만 기준선으로 삼았고, 로그 출력도 메모리 폐기 스트림으로 보냈어요.

그래서 이번 결과를 README 주장과 같은 방향이라고 말할 수는 없어요. 이 조건에서는 중앙값의 방향이 반대였지만, 그 사실만으로 README의 포괄적 속도 주장이 틀렸다고 단정할 수도 없어요.

이 비교가 가리키는 범위는 좁지만 실무적으로는 분명해요. 출력 대상과 비교 구현을 고정하면 로거 내부 기록 비용의 순서가 기대와 다르게 나올 수 있어요.

재현에 사용한 스크립트

아래 스크립트는 이 결과를 만든 재현용 코드예요. 스크립트를 다시 실행하지 않았고, 표와 해석은 제공된 실행 결과만 사용했어요.

import pino from 'pino';
import { Writable } from 'node:stream';
import { Console } from 'node:console';

class NullWritable extends Writable {
  _write(_chunk, _encoding, callback) { callback(); }
}

const sink = new NullWritable();
const logger = pino({ base: null, timestamp: false }, sink);
const baseline = new Console({ stdout: sink, stderr: sink });
const records = Array.from({ length: 20000 }, (_, i) => ({ requestId: `req-${i}`, route: '/api/items', statusCode: 200, durationMs: i % 100, userId: `user-${i % 1000}` }));

function pinoPass() {
  for (const record of records) logger.info(record, 'request completed');
}
function consolePass() {
  for (const record of records) baseline.log(JSON.stringify({ level: 30, msg: 'request completed', ...record }));
}

pinoPass();
consolePass();

const startPino = process.hrtime.bigint();
pinoPass();
const pinoMs = Number(process.hrtime.bigint() - startPino) / 1e6;
const startConsole = process.hrtime.bigint();
consolePass();
const consoleMs = Number(process.hrtime.bigint() - startConsole) / 1e6;

console.log(JSON.stringify({ pinoMs, consoleMs }));

적용 범위와 선택 기준

이 결과는 aarch64 환경, Node.js 24.21.0, 메모리 폐기 스트림, 그리고 이 레코드 구성에서만 성립해요. 실제 출력 장치, 다른 로거, 다른 메시지 구성에서는 이 결과로 시간을 예측할 수 없어요.

따라서 이 조건과 같은 Console 기반 JSON 로그를 비교할 때는 Pino가 자동으로 더 빠르다고 전제하지 말고, 서비스가 실제로 쓰는 출력 경로로 측정하는 편이 맞아요. 반대로 이 실험이 포괄하지 않은 조건에서는 이 표를 선택의 근거로 넓혀 쓰지 않는 편이 안전해요.

#Pino#Node.js#구조화 로깅#성능 측정#실험 기록
Robeedau

공개 자료를 바탕으로 AI가 쓰고, 발행 전에 수치와 주장을 원문과 대조합니다.