웹 개발
운영 환경 로그에서 debug 레벨을 걸러내야 하는 이유
개발 중 디버깅에 편했던 verbose 로그를 운영에서 그대로 켜두면 성능과 비용, 보안 세 가지에 모두 영향을 미친다.
개발 중에는 요청마다 파라미터, 쿼리 결과, 중간 상태를 찍는 것이 당연하다. 그런데 이 로그를 운영에서도 그대로 켜두면 문제가 생기기 시작한다.
운영 환경 debug 로그가 만드는 문제
성능: 로그를 쓰는 것 자체가 I/O 작업이다. 요청마다 수십 줄의 로그가 찍히면 고트래픽 상황에서 로그 쓰기가 응답 지연에 영향을 준다. 특히 동기식 로그 출력이나 파일 쓰기가 느린 환경에서 두드러진다.
비용: CloudWatch, Datadog, Loki 같은 로그 수집 서비스는 보통 수집된 로그 양으로 과금한다. debug 로그가 전체 로그 볼륨의 90%를 차지하는 일이 흔하다.
보안: 디버그용 로그에는 요청 본문, 토큰, 사용자 ID가 평문으로 찍히기 쉽다. 로그 집계 시스템에 이런 데이터가 남으면 유출 경로가 된다.
로그 레벨 기반으로 필터링한다
pino를 예시로 들면, 로그 레벨을 환경 변수로 제어하는 구조가 자연스럽다.
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL ?? (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
})
export default logger
운영에서는 LOG_LEVEL=info(또는 설정 없이 기본값)로 두면 debug, trace 레벨은 자동으로 무시된다.
// 이 로그는 운영에서 출력되지 않는다
logger.debug({ query, params }, 'DB 쿼리 실행')
// 이 로그는 운영에서도 출력된다
logger.info({ userId }, '로그인 성공')
logger.error({ err }, '결제 처리 실패')
winston을 쓴다면
import winston from 'winston'
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
transports: [new winston.transports.Console()],
})
운영에서 남겨야 하는 로그
로그를 줄인다고 중요한 것까지 지우면 안 된다. 운영에서 info 이상으로 남겨야 하는 것들:
- 요청/응답 요약 (경로, 상태 코드, 소요 시간)
- 인증/인가 이벤트 (로그인, 권한 오류)
- 외부 API 호출 결과 (성공/실패, 소요 시간)
- 에러와 스택 트레이스 (
error레벨)
debug에만 남겨도 되는 것들:
- SQL 쿼리 전체 내용
- 함수 진입/종료 추적
- 중간 계산 결과
- 캐시 히트/미스 상세 내역
로컬에서 개발할 때 LOG_LEVEL=debug를 .env.local에 두고, 운영 환경 변수에서는 설정하지 않거나 info로 명시해두면 실수로 debug 로그가 운영에 켜지는 일을 방지할 수 있다.