PHP API 장애를 Metrics·JSON Logs·Traces로 좁히기 — request_id 연결 예시
도입
Metrics, Logs, Traces의 뜻을 각각 알고 있어도 장애 조사 과정에서 세 데이터를 연결하지 못하면 원인을 좁히기 어렵다. PHP API의 응답 시간이 늘고 오류율이 상승했다면 영향 범위, 실패한 요청, 시간이 오래 걸린 구간을 같은 시각과 식별자로 이어서 살펴봐야 한다.
이 글의 대시보드, 로그, trace, 요청 수와 응답 시간은 연결 방식을 설명하기 위해 만든 예시다. 실제 운영 측정값이나 이번 편집 과정에서 다시 실행한 테스트 결과가 아니다. 운영 환경의 알림 기준은 해당 서비스의 정상 범위를 먼저 측정한 뒤 결정해야 한다.
1. 세 데이터가 답하는 질문
| 데이터 | 답하는 질문 | 대표 기준 |
|---|---|---|
| Metrics | 언제부터 얼마나 많은 요청이 비정상인가 | service, route, status |
| Logs | 어떤 요청에서 무슨 사건이 발생했는가 | request_id, event, error_code |
| Traces | 그 요청은 어느 구간에서 시간을 썼는가 | trace_id, span_id |
조사는 항상 Metric에서 시작할 필요는 없다. 사용자가 요청 ID를 알려 줬다면 로그에서 시작할 수 있다. 외부 호출이 없는 단일 애플리케이션이라면 구조화 로그와 프로파일러만으로 충분할 수도 있다.
2. PHP 요청에 검증된 request_id를 부여한다
<?php
declare(strict_types=1);
$incoming = $_SERVER['HTTP_X_REQUEST_ID'] ?? '';
$requestId = preg_match('/^[A-Za-z0-9_-]{8,64}$/', $incoming)
? $incoming
: bin2hex(random_bytes(16));
header('X-Request-ID: ' . $requestId);
function writeJsonLog(string $level, string $event, array $context = []): void
{
global $requestId;
$record = [
'timestamp' => gmdate('c'),
'level' => $level,
'event' => $event,
'request_id' => $requestId,
] + $context;
error_log(json_encode(
$record,
JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
));
}
외부에서 받은 식별자는 길이와 허용 문자를 제한한다. 같은 요청 ID를 HTTP 응답, 웹 서버 access log, 애플리케이션 로그와 trace 속성에 기록하면 한 요청의 흐름을 따라갈 수 있다.
이 예시는 요청 ID를 전달하는 최소 코드다. 실제 애플리케이션에서는 프레임워크의 요청 수명 주기와 동시 실행 방식을 고려해 전역 변수 대신 요청 범위의 객체나 context에 보관하는 편이 안전하다.
3. 로그 레벨과 필드를 일관되게 정한다
- INFO: 요청 시작·완료처럼 정상적인 핵심 흐름
- WARN: 처리는 됐지만 반복되면 문제가 될 수 있는 지연·재시도·대체 경로
- ERROR: 요청 실패, 저장 실패처럼 처리를 완료하지 못한 사건
의도한 입력 거절을 모두 ERROR로 기록하면 실제 장애 신호가 묻힐 수 있다. 반대로 “에러 발생”이라는 문장만 남기면 원인과 영향 범위를 검색하기 어렵다. 사건 이름과 검색할 필드를 정해 JSON으로 기록한다.
{
"timestamp": "2026-08-11T07:20:31Z",
"level": "ERROR",
"event": "post_read_failed",
"request_id": "7b2f8b80c6a94cb39f0a2fcb82c41862",
"route": "/posts/{id}",
"error_code": "db_timeout",
"duration_ms": 3012
}
위 JSON은 설명용 예시다. orderId, order_id, oid처럼 같은 의미의 필드가 섞이지 않도록 로그 스키마를 정한다. Authorization 헤더, 세션, 비밀번호, 카드 정보와 전체 요청 본문은 기록하지 않는다. 시간은 UTC ISO 8601처럼 하나의 형식으로 저장하고 화면에서 필요한 시간대로 변환한다.
4. Metrics로 영향 범위를 좁히는 예시
http_requests_total{
service="blog-api",
route="/posts/{id}",
status="500"
} 23
http_request_duration_seconds_bucket{
service="blog-api",
route="/posts/{id}",
le="0.5"
} 812
위 값은 설명용이다. 실제 조사에서는 요청 수, 오류율과 응답 시간 분포를 route 단위로 살펴보고 이상이 시작된 시각과 최근 배포 시각을 함께 기록한다. 사용자 ID나 request_id처럼 값의 종류가 계속 늘어나는 정보는 metric label로 사용하지 않는다.
5. Logs에서 실패 요청을 선택하는 예시
Metric에서 찾은 이상 시각의 ERROR 로그 중 하나를 선택한다. 같은 request_id로 access log와 PHP 로그를 검색해 HTTP 상태, 애플리케이션 사건과 처리 시간을 연결한다.
request_id: 7b2f8b80c6a94cb39f0a2fcb82c41862
route: /posts/{id}
status: 500
event: post_read_failed
error_code: db_timeout
duration_ms: 3012
6. Trace에서 오래 걸린 구간을 찾는 예시
GET /posts/{id} 3,018 ms
├─ auth.verify 4 ms
├─ mariadb SELECT posts 3,006 ms
└─ response.serialize 3 ms
이 예시에서는 MariaDB span이 전체 시간의 대부분을 차지한다. 이것만 보고 인덱스를 추가해서는 안 된다. 같은 시각의 연결 수, 잠금 대기와 실행 계획을 확인해 다음 조사 대상을 정해야 한다. Trace는 시간이 오래 걸린 위치를 보여주지만 원인을 자동으로 확정하지 않는다.
여러 서비스와 큐를 거치는 요청에는 표준 trace context를 전달한다. 임의의 요청 ID를 복사하는 것과 실제 분산 tracing의 trace·span·parent 관계는 구분해야 한다.
7. 로컬 환경에서 확인할 항목
다음 절차는 재현 환경을 갖췄을 때 수행할 검증 방법이다. 이 글의 이미지가 해당 절차를 실제로 실행한 결과라는 뜻은 아니다.
- 같은 API를 정상·의도적 지연·오류 모드로 호출한다.
- 모든 응답에
X-Request-ID가 있는지 확인한다. - 오류 로그에 같은
request_id가 기록되는지 확인한다. - 지연 요청의 trace에서 의도한 구간이 가장 긴 span인지 확인한다.
- 로그에 인증정보와 개인정보가 포함되지 않았는지 확인한다.
curl -i http://127.0.0.1:8080/posts/1
curl -i \
-H 'X-Request-ID: local-test-0001' \
http://127.0.0.1:8080/posts/1
위 명령은 해당 경로를 제공하는 로컬 API가 준비됐다는 전제의 예시다. 정상·지연·오류 모드를 구분하는 라우트나 입력은 애플리케이션의 테스트 구성에 맞게 별도로 준비해야 한다.
8. 서비스 규모에 맞는 수준부터 도입한다
트래픽이 적고 외부 호출이 없는 단일 PHP 애플리케이션은 JSON 로그와 기본 Metrics부터 시작할 수 있다. 여러 서비스, 큐와 외부 API를 거치는 요청은 trace context가 없으면 구간별 지연을 연결하기 어렵다. 모든 요청과 세부 작업을 무제한 수집하면 저장 비용과 처리 부담이 늘어나므로 보존 기간과 샘플링 정책도 함께 정한다.
운영 점검표
- Metric label에 사용자 ID나 request_id처럼 값의 종류가 계속 늘어나는 정보를 넣지 않는다.
- 로그의 필드명과 시간대를 통일한다.
- 민감정보 제거를 코드와 테스트로 확인한다.
- 응답·로그·trace에서 같은 요청을 찾을 수 있는지 검증한다.
- 느린 span만 보고 원인을 단정하지 않고 다음 증거를 확인한다.
- 서비스 규모에 맞는 보존 기간과 샘플링 기준을 정한다.
참고 자료
문서 확인일: 2026-09-08. 이 글의 코드와 화면은 관측 데이터의 연결 구조를 설명하기 위한 예시다.
댓글 0