LabHub
배우기 러닝패스 코스

실패했는데 종료 코드는 0 이었다 · 로그 5천 줄을 엑셀 없이 읽는다 · 실습

로그가 말하는 것을 숫자로

LabHub 에서 이어서 보기

목표

표준 라이브러리만으로 접근 로그와 배포 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 에 저장한다.

참고

단계 8개

  1. 한 줄씩 읽고 파싱 성공을 센다
  2. 상태 코드 클래스별 건수
  3. 요청이 많은 경로 상위 n
  4. 평균이 아니라 분위수
  5. 5xx 가 가장 많았던 1분
  6. 배포 CSV 의 서비스별 실패율
  7. 시간대가 붙은 시각으로 거른다
  8. 전부를 JSON 보고서로