"관측 가능성은 대시보드가 많다는 뜻이 아니야. 예상하지 못한 장애가 생겼을 때, 남아 있는 신호만으로 무슨 일이 있었는지 물을 수 있다는 뜻이야."
로그, 메트릭, 트레이스가 답하는 질문
- 로그: 특정 시점에 어떤 사건이 일어났는지 기록한다. 오류 메시지와 요청 문맥처럼 개별 사건의 세부 정보를 담기 좋다.
- 메트릭: 시간에 따른 수치와 분포를 집계한다. 요청률, 오류율, p99 지연, 열린 연결 수처럼 서비스 상태의 변화를 알림과 대시보드에 쓰기 좋다.
- 트레이스: 요청 하나가 여러 작업과 서비스를 지나간 경로를 span으로 잇는다. 지연이 어느 구간에서 생겼는지 찾는 데 유용하다.
세 신호는 서로 대신하지 않아. 메트릭으로 이상을 발견하고, 트레이스로 느린 구간을 좁힌 뒤, 같은 요청 ID나 trace ID가 담긴 로그에서 세부 사건을 확인하는 식으로 이어져야 해.
구조화 로그에는 문맥과 경계를 함께 담아
운영 로그는 사람이 읽는 문장만 모아 두기보다 JSON처럼 필드가 분리된 형식이 다루기 쉬워. 요청 ID, 서비스 이름, 로그 수준, 작업 이름처럼 반복해서 검색할 문맥을 일정한 키로 남겨. 메시지는 사람이 이해할 설명을 맡고, 필드는 필터와 집계를 맡는 거야.
import pino from 'pino';
const log = pino({
level: process.env.LOG_LEVEL ?? 'info',
redact: ['req.headers.authorization'],
});
const requestLog = log.child({ requestId, route: '/users/:id' });
requestLog.info({ userId }, 'user loaded');
전체 요청이나 사용자 객체를 그대로 기록하면 토큰, 쿠키, 개인정보가 섞일 수 있어. 필요한 필드만 허용하고 로거의 redaction 기능을 방어선으로 더해. 로그 보존 기간과 접근 권한도 애플리케이션 보안의 일부야.
메트릭은 cardinality를 설계해야 해
- Counter: 요청 수나 오류 수처럼 누적되는 값.
- Gauge: 큐 길이나 열린 연결 수처럼 오르내리는 현재 값.
- Histogram: 요청 지연이나 응답 크기의 분포.
트레이스는 요청의 경로를 잇는다
OpenTelemetry는 계측 코드와 내보내기 형식을 특정 관측 백엔드에서 분리하는 표준이야. Node SDK와 지원되는 자동 계측을 애플리케이션 코드보다 먼저 초기화하면 HTTP 클라이언트와 서버, 일부 프레임워크와 데이터베이스 라이브러리에 span을 붙일 수 있어. 자동 계측 범위는 패키지와 버전에 따라 다르므로 실제로 생성된 span을 확인해야 해.
// instrumentation.mjs
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter(),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
트레이스도 무한히 모을 수는 없어. 샘플링 정책을 정하고, 쿼리 원문이나 사용자 데이터 같은 민감한 속성이 span에 들어가지 않도록 검토해. 중요한 작업이 자동 계측 범위 밖이라면 직접 span을 추가하되, 의미 있는 경계에만 두는 편이 좋아.
비동기 문맥과 상관관계
Node용 OpenTelemetry는 비동기 작업 사이에서 현재 trace 문맥을 이어 가기 위해 AsyncLocalStorage 계열의 문맥 관리 기능을 사용해. 그 덕분에 한 요청에서 생긴 하위 작업들이 같은 trace에 연결될 수 있어. 로그에도 현재 trace ID와 span ID를 넣으면 트레이스 화면에서 발견한 요청을 로그 검색으로 바로 이어 갈 수 있지.
문맥이 끊기는 사용자 정의 스케줄러나 지원되지 않는 라이브러리를 만나면 span이 서로 다른 trace로 갈라질 수 있어. 자동 계측을 설치했다는 사실보다 실제 요청 하나가 끝까지 연결되는지 검증하는 일이 중요해.