LabHub
배우기 러닝패스 코스

관측성 · 로그 수준과 표본 추출 · 실습

디버그 로그를 켠 날 조사에 필요한 줄이 사라졌다

LabHub 에서 이어서 보기

목표

고정된 로그 파일 하나로 수준별 비용 표를 만들고, 요청 단위 표본·오류 보존·반복 접기·초당 상한을 직접 구현해 비용과 조사 가능성의 맞바꿈을 숫자로 만든 뒤 정책 파일로 못박습니다.

왜 중요한가

로그 비용을 줄이라는 요구는 항상 온다. 그때 가장 쉬운 답이 '수준을 올린다' 와 '표본을 뽑는다' 인데, 둘 다 잘못하면 비용만 줄고 조사 능력은 0 이 된다. 사람이 조사할 때 읽는 단위는 줄이 아니라 한 요청이 남긴 줄의 묶음이기 때문이다. 줄 단위로 10% 를 남기면 어느 요청도 끝까지 읽을 수 없지만, 요청 단위로 10% 를 남기면 그 10% 는 완전하다. 여기에 '오류로 끝난 요청은 빼지 않는다' 는 규칙 하나를 더하면 비용은 거의 그대로인 채 실패한 요청의 보존율이 0% 에서 100% 로 올라간다. 이 실습에서 만드는 표는 다음번 비용 회의에서 그대로 쓸 수 있는 근거다.

단계

1. 재료 로그는 /opt/lab/logsample/app.log 입니다. 줄 형식은 <시각> <수준> req=<id> handler=<경로> msg="<메시지>" 이고 수준은 DEBUG·INFO·WARN·ERROR 넷입니다. /root/obs-log-sampling/cost.tsv 에 머리글 없이 네 줄, 각 줄은 탭으로 나눈 세 칸 <수준> <줄 수> <바이트> 를 적으세요. 바이트는 그 수준의 줄들이 차지하는 바이트 수이고 줄바꿈 문자 한 개를 포함합니다.
2. DEBUG 줄을 전부 버렸을 때의 결과를 /root/obs-log-sampling/drop-debug.txt 에 여섯 줄로 적으세요. kept_lines=<남은 줄 수>, kept_bytes=<남은 바이트>, saved_pct=<바이트 기준 절감률, 소수 두 자리>, req=<오류로 끝난 요청 중 request_id 가 사전순으로 가장 앞선 것>, req_lines_before=<그 요청의 원래 줄 수>, req_lines_after=<DEBUG 를 뺀 뒤 그 요청에 남은 줄 수> 입니다. '오류로 끝난 요청' 은 수준이 ERROR 인 줄을 하나 이상 가진 요청입니다.
3. /root/obs-log-sampling/sample.py 를 만드세요. python3 sample.py --rate N < 입력 > 출력 으로 부르면 표준 입력의 줄을 읽어 남길 줄만 그대로 표준 출력에 씁니다(입력 순서 유지). 남기는 기준은 요청 단위입니다 — 줄의 req= 값을 r 이라 할 때 int(hashlib.md5(r.encode()).hexdigest()[:8], 16) % N == 0 이면 그 줄을 남깁니다. --rate 1 은 전부 남깁니다. 그다음 python3 sample.py --rate 10 < /opt/lab/logsample/app.log > /root/obs-log-sampling/sampled-10.log 로 결과 파일을 만드세요.
4. /root/obs-log-sampling/sample.py--keep-errors 옵션을 더하세요. 이 옵션이 있으면 수준이 ERROR 인 줄을 하나라도 가진 요청은 표본률과 무관하게 모든 줄을 남깁니다(입력 순서 유지). 옵션이 없을 때의 동작은 3단계와 같아야 합니다. 그다음 python3 sample.py --rate 10 --keep-errors < /opt/lab/logsample/app.log > /root/obs-log-sampling/sampled-10-err.log 로 결과를 만드세요.
5. 표본률 N 을 1·4·10·50 으로 바꿔 가며 /root/obs-log-sampling/tradeoff.tsv 를 만드세요. 머리글 없이 네 줄이고 각 줄은 탭으로 나눈 다섯 칸 <N> <표본만 썼을 때 남은 줄 수> <그때 완전히 남은 오류 요청 수> <오류 보존까지 켰을 때 남은 줄 수> <그때 완전히 남은 오류 요청 수> 입니다. '완전히 남은 오류 요청' 은 오류로 끝난 요청 중 결과 파일에 줄이 하나라도 남아 있는 요청을 셉니다.
6. /root/obs-log-sampling/squeeze.py 를 만드세요. python3 squeeze.py [--max-debug-per-sec K] < 입력 > 출력 (K 기본값 20)으로 부르면 두 규칙을 차례로 적용합니다. ① 바로 앞 줄과 (수준, handler, msg) 가 모두 같으면 한 덩어리로 보고 첫 줄만 남기되, 덩어리의 줄 수 k 가 2 이상이면 그 줄 끝에 repeated=<k> 를 붙입니다. ② 접은 뒤, DEBUG 줄만 같은 초(시각 문자열의 앞 19자) 안에서 앞에서부터 K 개까지 남기고 나머지는 버립니다. 그다음 python3 squeeze.py < /opt/lab/logsample/app.log > /root/obs-log-sampling/squeezed.log 를 만들고, /root/obs-log-sampling/squeeze.txt 에 네 줄 in_lines=, after_collapse=, after_cap=, bytes_saved_pct=<소수 두 자리> 를 적으세요.
7. sample.py --rate 10 --keep-errors 의 출력을 squeeze.py(기본 상한)에 그대로 흘려 /root/obs-log-sampling/final.log 를 만드세요. 그리고 /root/obs-log-sampling/final.txt 에 여덟 줄을 적으세요 — in_lines=, out_lines=, in_bytes=, out_bytes=, reduction_pct=<바이트 기준, 소수 두 자리>, error_requests_kept=<final.log 에 줄이 남아 있는 오류 요청 수>, trace_req=<2단계에서 고른 그 요청 id>, trace_lines=<final.log 에 남은 그 요청의 줄 수> 입니다.
8. /root/obs-log-sampling/policy.yml 을 YAML 로 쓰세요. levels 아래 DEBUG·INFO·WARN·ERROR 네 수준마다 retain_days(정수)와 sample_rate(정수) 를 둡니다. ERROR 의 sample_rate 는 1 이어야 하고 retain_days 는 DEBUG 보다 커야 합니다. DEBUG 의 sample_rate 는 2 이상이어야 합니다. rules 아래에는 keep_errors_whole_request: true, collapse_repeats: true, max_debug_per_sec(6단계에서 쓴 기본값)를 둡니다. estimate 아래에는 raw_bytes·kept_bytes·reduction_pct 를 7단계 결과와 같은 값으로 적고, 마지막에 note 로 이 정책의 맞바꿈을 40자 이상 한 문장으로 적으세요.

참고

단계 8개

  1. 수준별로 얼마를 쓰고 있는지부터 센다
  2. DEBUG 를 끄면 무엇이 남고 무엇이 사라지나
  3. 줄이 아니라 요청을 표본으로 뽑는다
  4. 오류로 끝난 요청은 표본에서 빼지 않는다
  5. 표본률과 조사 가능성의 맞바꿈을 표로 만든다
  6. 반복을 접고 초당 상한을 건다
  7. 응용 ① — 두 필터를 이어 붙이고 조사가 되는지 확인한다
  8. 응용 ② — 수준·표본률·보존기간·비용을 정책 파일로 못박는다