도입
로그를 많이 남긴다고 운영이 쉬워지는 것은 아니다. 중요한 것은 상황에 맞는 로그 레벨을 일관되게 남기는 것이다.
로그 레벨이 섞이면 장애 상황에서 원인을 찾기 어렵다. INFO, WARN, ERROR의 기준을 먼저 정해야 한다.
핵심 개념 설명
로그 레벨은 시스템 상태의 심각도를 구분하는 기준이다. 실무에서는 보통 INFO, WARN, ERROR를 중심으로 설계한다.
기준은 단순해야 한다. 정상 흐름이면 INFO, 이상 징후면 WARN, 실패나 장애면 ERROR다.
- INFO: 정상적인 처리 흐름 기록
- WARN: 당장은 동작하지만 확인이 필요한 상태
- ERROR: 요청 실패, 데이터 문제, 장애 가능성이 있는 상태
동작 원리 또는 구조
로그 레벨은 장애 대응 흐름과 연결된다. 레벨이 높을수록 즉시 확인해야 하는 신호가 된다.
flowchart TD
A[이벤트 발생] --> B{처리 결과}
B -->|정상 처리| C[INFO]
B -->|처리는 됐지만 이상 징후| D[WARN]
B -->|처리 실패 또는 장애| E[ERROR]
C --> F[운영 흐름 추적]
D --> G[점검 대상]
E --> H[즉시 대응 대상]
로그 레벨은 개발자 편의가 아니라 운영 기준이다. 운영자가 어떤 로그를 먼저 봐야 하는지 결정하는 장치다.
실무 기준 / 적용 방법
1. INFO 기준
INFO는 정상적인 비즈니스 흐름을 추적할 때 사용한다. 서비스가 의도한 대로 동작했다는 기록이다.
단, 너무 많은 INFO는 노이즈가 된다. 요청 단위, 배치 시작과 종료, 주요 상태 변경 정도만 남긴다.
<?php
logger()->info('payment_request_received', [
'order_id' => $orderId,
'user_id' => $userId,
'amount' => $amount,
]);
logger()->info('payment_completed', [
'order_id' => $orderId,
'transaction_id' => $transactionId,
]);
2. WARN 기준
WARN은 처리는 계속되지만 확인이 필요한 상태에 사용한다. 즉시 장애는 아니지만 반복되면 문제가 되는 신호다.
예를 들어 외부 API 응답 지연, 재시도 성공, 예상보다 오래 걸린 쿼리 등이 WARN에 해당한다.
<?php
if ($responseTime > 3000) {
logger()->warning('external_api_slow_response', [
'api' => 'payment_gateway',
'response_time_ms' => $responseTime,
'order_id' => $orderId,
]);
}
if ($retryCount > 0 && $isSuccess) {
logger()->warning('payment_retry_succeeded', [
'order_id' => $orderId,
'retry_count' => $retryCount,
]);
}
3. ERROR 기준
ERROR는 실제 실패가 발생했을 때 사용한다. 요청 실패, 데이터 저장 실패, 외부 API 최종 실패가 기준이다.
ERROR는 알림과 연결될 수 있다. 그래서 단순 검증 실패까지 ERROR로 남기면 안 된다.
<?php
try {
$paymentService->approve($orderId);
} catch (Throwable $e) {
logger()->error('payment_approval_failed', [
'order_id' => $orderId,
'message' => $e->getMessage(),
'trace_id' => $traceId,
]);
throw $e;
}
실무 로그 설계 기준
1. 로그 메시지는 이벤트명으로 작성
로그 메시지는 문장보다 이벤트명으로 남기는 것이 좋다. 검색과 집계가 쉬워지기 때문이다.
<?php
// 좋지 않은 예
logger()->error('결제 승인 중 에러가 발생했습니다.');
// 좋은 예
logger()->error('payment_approval_failed', [
'order_id' => $orderId,
'error_code' => $errorCode,
]);
2. 로그에는 추적 가능한 값을 넣기
로그는 나중에 찾아보기 위해 남긴다. 그래서 주문번호, 사용자 ID, 요청 ID 같은 값이 필요하다.
특히 여러 서버를 거치는 구조라면 trace_id를 반드시 넣는 것이 좋다.
<?php
logger()->info('order_status_changed', [
'trace_id' => $traceId,
'order_id' => $orderId,
'before_status' => 'READY',
'after_status' => 'PAID',
]);
3. 민감 정보는 남기지 않기
로그에는 비밀번호, 주민번호, 카드번호를 남기면 안 된다. 로그는 생각보다 여러 사람이 접근할 수 있다.
필요한 경우에는 마스킹해서 저장한다. 운영 편의보다 보안 기준이 우선이다.
<?php
logger()->info('user_login_failed', [
'email' => maskEmail($email),
'ip' => $request->ip(),
]);
자주 발생하는 문제
1. 모든 예외를 ERROR로 남김
사용자 입력 오류나 권한 없음까지 ERROR로 남기면 실제 장애 로그를 찾기 어려워진다.
검증 실패는 보통 INFO 또는 WARN이다. 서버가 의도한 대로 거절한 요청이기 때문이다.
2. WARN 기준이 없음
WARN 기준이 없으면 거의 사용하지 않거나 반대로 너무 많이 남기게 된다.
실무에서는 “처리는 됐지만 반복되면 위험한 상태”를 WARN 기준으로 잡는 것이 좋다.
3. 로그에 원인 정보가 없음
“에러 발생”만 남기면 운영에서 쓸 수 없다. 무엇이, 어디서, 어떤 값으로 실패했는지가 필요하다.
<?php
logger()->error('db_insert_failed', [
'table' => 'payments',
'order_id' => $orderId,
'reason' => $e->getMessage(),
]);
4. 성공 로그를 너무 많이 남김
모든 함수마다 INFO를 남기면 로그 비용과 검색 비용이 증가한다. 핵심 비즈니스 이벤트만 남기는 것이 좋다.
한 줄 요약
INFO는 정상 흐름, WARN은 확인이 필요한 이상 징후, ERROR는 실제 실패와 장애 상황에만 사용해야 한다.
댓글 0