도입
문자열 로그는 사람이 읽기는 쉽지만 검색과 집계가 어렵다.
서비스가 커질수록 로그는 분석 대상이 된다. 이때 필요한 방식이 Structured Logging이다.
핵심은 “문장이 아니라 데이터로 남긴다”는 점이다. JSON 형태로 기록해 필드 단위로 조회한다.
핵심 개념 설명
Structured Logging은 로그를 key-value 구조로 남기는 방식이다. 로그 한 줄이 하나의 JSON 객체가 된다.
이 방식은 필드 기준 검색이 가능하다. order_id, user_id 같은 값으로 바로 필터링할 수 있다.
- 비구조 로그: 문자열 기반, 파싱 필요
- 구조 로그: JSON 기반, 즉시 검색 가능
- 분석 도구: ELK, Loki, Datadog 등
{
"level": "INFO",
"event": "payment_completed",
"order_id": "ORD-1001",
"user_id": 42,
"amount": 15000,
"timestamp": "2026-04-26T10:00:00Z"
}
동작 원리 또는 구조
로그는 애플리케이션에서 생성되고 수집 시스템을 통해 저장된다.
flowchart LR
A[Application] --> B[Logger]
B --> C[JSON Log Output]
C --> D[Log Collector]
D --> E[Storage (Elasticsearch / Loki)]
E --> F[Search & Dashboard]
구조 로그는 파싱 과정이 없다. 수집 단계에서 바로 필드로 인식된다.
그래서 검색 성능과 정확도가 크게 좋아진다. 운영 효율이 여기서 갈린다.
실무 기준 / 적용 방법
1. 로그 포맷을 먼저 정의
필드를 먼저 정하지 않으면 로그가 난잡해진다. 공통 스키마를 잡는 것이 시작이다.
최소 필드는 level, event, timestamp다. 여기에 trace_id, user_id 등을 추가한다.
{
"level": "INFO",
"event": "string",
"timestamp": "ISO8601",
"trace_id": "string",
"user_id": "number",
"context": {}
}
2. PHP (Monolog) 적용
PHP에서는 Monolog의 JsonFormatter를 사용한다. 출력을 JSON으로 강제하면 된다.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\JsonFormatter;
$logger = new Logger('app');
$handler = new StreamHandler('php://stdout');
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);
$logger->info('payment_completed', [
'order_id' => 'ORD-1001',
'user_id' => 42,
'amount' => 15000,
]);
stdout으로 내보내는 이유는 컨테이너 환경 때문이다. Docker/K8s에서는 stdout 수집이 기본이다.
3. trace_id 반드시 포함
요청 하나를 끝까지 추적하려면 trace_id가 필요하다. 특히 MSA 구조에서는 필수다.
모든 로그에 동일한 trace_id를 넣어야 한다. 그래야 하나의 요청 흐름을 묶을 수 있다.
<?php
$traceId = bin2hex(random_bytes(8));
logger()->info('request_start', [
'trace_id' => $traceId,
]);
logger()->info('payment_completed', [
'trace_id' => $traceId,
'order_id' => $orderId,
]);
4. 컨텍스트는 객체로 묶기
필드를 무작정 늘리면 관리가 어려워진다. 관련 데이터는 context로 묶는 것이 좋다.
{
"event": "payment_completed",
"context": {
"order_id": "ORD-1001",
"user_id": 42,
"amount": 15000
}
}
검색 기준이 되는 필드는 상단에 둔다. 나머지는 context로 정리하는 구조가 안정적이다.
자주 발생하는 문제
1. 문자열 로그와 혼용
JSON 로그와 문자열 로그를 섞으면 파싱이 깨지고 검색이 어려워진다.
한 번 정하면 반드시 통일해야 한다. 중간에 섞이는 순간 의미가 없어진다.
2. 필드 이름이 제각각
orderId, order_id, oid 같이 섞이면 검색이 불가능해진다.
snake_case 또는 camelCase 중 하나로 통일한다. 팀 규칙으로 강제해야 한다.
3. 로그 크기 과다
모든 데이터를 넣으면 로그 비용이 폭발한다. 특히 request/response 전체 저장은 위험하다.
필요한 값만 남겨야 한다. 디버깅용 로그는 별도로 분리한다.
4. 민감 정보 노출
JSON이라 더 쉽게 노출된다. 카드번호, 토큰, 비밀번호는 반드시 제거한다.
필요 시 마스킹 처리한다. 로그는 장기간 저장된다는 점을 고려해야 한다.
5. 타임존 불일치
로그 시간 기준이 서버마다 다르면 정렬이 꼬인다.
항상 UTC로 저장한다. 표시만 로컬 시간으로 변환한다.
한 줄 요약
Structured Logging은 로그를 JSON 데이터로 남겨 필드 단위 검색과 분석을 가능하게 하는 방식이며, 실무에서는 trace_id와 일관된 스키마 설계가 핵심이다.
댓글 0