import{AsyncLocalStorage}from'node:async_hooks';constals=newAsyncLocalStorage<{requestId: string;userId?: string}>();app.use((req,res,next)=>{constrequestId=req.header('x-request-id')??crypto.randomUUID();als.run({requestId},()=>next());});exportfunctionlog() {constctx=als.getStore()??{};returnlogger.child(ctx);}// 어디서나
log().info({orderId},'created order');// → 자동으로 requestId 포함
Level 가이드
fatal: 프로세스 종료급.
error: 처리 가능하지만 비정상.
warn: 문제 신호 (재시도 발생, fallback 사용).
info: 정상 핵심 이벤트 (사용자 가입, 결제 완료).
debug: 흐름 추적. 운영에서는 끔.
trace: 매우 상세. 로컬만.
메시지 vs 필드
// ❌ 동적 값을 메시지에
logger.info(`User ${userId} purchased ${productId} for ${amount}`);// ✅ 필드로
logger.info({userId,productId,amount},'purchase completed');// 검색: amount > 100 같은 쿼리 가능
🤔 의사결정 기준
데이터
어디
식별자 (userId, orderId)
항상 필드
타이밍 (ms, duration)
필드
에러 객체
{ err } (pino 자동 stack 추출)
요청 / 응답 본문
필드 + redact 정책
PII
절대 X — 별도 audit log
❌ 안티패턴
stdout 에 plain text: 파싱 불가. JSON 한 줄.
stderr 와 stdout 혼용: error 가 다른 stream 에. 한 곳으로 + level 필드.