실패했는데 종료 코드는 0 이었다 · 로그 5천 줄을 엑셀 없이 읽는다 · 실습
로그가 말하는 것을 숫자로
목표
표준 라이브러리만으로 접근 로그와 배포 CSV 에서 운영 지표(건수·상태 분포·상위 경로·분위수·최악의 1분·서비스별 실패율)를 뽑는 도구를 만든다.
왜 중요한가
운영 서버에는 pandas 도 인터넷도 없지만 csv·Counter·statistics·datetime 은 있다. 한 줄씩 읽는 파서, 분위수, 시간대가 붙은 시각 비교 — 이 셋을 손에 익히면 로그가 5천 줄이든 50만 줄이든 같은 도구로 답을 낸다. 재료는 /opt/fixtures/pyops/access.log(nginx combined + 마지막 열 처리 시간(초), 2026-09-10 02:00 부터 05:00 KST 까지, 03:12 부터 03:17 까지가 장애 구간)와 /opt/fixtures/pyops/deploys.csv(service,version,deployed_at,duration_s,status)다.
단계
1. /root/pyops/data/logstat.py 를 만든다. logstat.py count <로그> 는 전체 줄 수와 정규식으로 파싱에 성공한 줄 수를 lines=<n> parsed=<m> 한 줄로 낸다. 파싱은 시각·경로·상태 코드·처리 시간을 뽑는다.
2. logstat.py status <로그> 는 상태 코드 클래스별 건수를 2xx=<n> 3xx=<n> 4xx=<n> 5xx=<n> 한 줄로 낸다(Counter).
3. logstat.py top <로그> --n 5 는 요청 수가 많은 경로 상위 n 개를 <건수> <경로> 형식으로 한 줄에 하나씩, 많은 순으로 낸다(most_common).
4. logstat.py latency <로그> 는 처리 시간의 p50·p95·p99 를 p50=<x> p95=<x> p99=<x> 로 낸다. statistics.quantiles(times, n=100)(기본 method) 의 [49]·[94]·[98] 을 소수 셋째 자리로 반올림한다.
5. logstat.py worst <로그> 는 5xx 가 가장 많았던 1분 구간을 worst_minute=<YYYY-MM-DDTHH:MM> count=<n> 으로 낸다. 시각은 strptime 으로 aware datetime 을 만들고 초를 0 으로 맞춰 묶는다. 로그의 시간대(+0900) 그대로 적는다.
6. /root/pyops/data/deploys.py 를 만든다. deploys.py summary <csv> 는 서비스별로 service=<이름> total=<n> failed=<m> fail_rate=<0.000> median_s=<x> 를 서비스 이름 순으로 한 줄씩 낸다(DictReader·statistics.median, fail_rate 는 소수 셋째 자리).
7. logstat.py status <로그> --since <ISO> --until <ISO> 로 시각 필터를 붙인다. 두 값은 2026-09-10T03:00:00+09:00 처럼 시간대가 붙은 ISO 8601 이고, since ≤ ts < until 인 줄만 센다.
8. logstat.py report <로그> --json 은 위 지표를 전부 담은 JSON 객체 하나를 표준 출력에 낸다. 키는 lines·parsed·status(클래스별 객체)·top([[경로, 건수], ...] 5개)·latency(p50·p95·p99)·worst_minute({"minute": ..., "count": ...})다. 이것을 /root/pyops/data/report.json 에 저장한다.
참고
- 정규식 예:
\[(?P<ts>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) [^"]*" (?P<status>\d{3}) \d+ "[^"]*" "[^"]*" (?P<rt>[\d.]+)$ - 시각:
datetime.strptime(ts, "%d/%b/%Y:%H:%M:%S %z"), 필터 입력:datetime.fromisoformat(s) - 흔한 실수: 평균으로 지연을 보고하는 것, naive 와 aware 시각을 비교하는 것, 파싱 실패 줄을 세지 않는 것.
단계 8개
- 한 줄씩 읽고 파싱 성공을 센다
- 상태 코드 클래스별 건수
- 요청이 많은 경로 상위 n
- 평균이 아니라 분위수
- 5xx 가 가장 많았던 1분
- 배포 CSV 의 서비스별 실패율
- 시간대가 붙은 시각으로 거른다
- 전부를 JSON 보고서로