LabHub
배우기 러닝패스 코스

Finding the Cause in Logs

The Collector Received Fewer Lines Than Were Sent

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

수집기가 받은 줄과 보낸 쪽의 카운터를 견주어 유실을 세고, 중복을 접고, 도착 지연 분포를 구하고, 마감 시각 집계의 과소집계와 재시작이 만든 착시를 숫자로 밝힙니다.

왜 중요한가

로그가 비어 있을 때 "안 왔다" 와 "일이 없었다" 는 결론이 정반대인데, 파일만 봐서는 구별되지 않습니다. 둘을 가르는 것은 발신 쪽이 붙인 단조 증가 번호뿐입니다. 그런데 그 번호도 프로세스가 재시작하면 1부터 다시 시작하므로, 부팅 식별자와 함께 묶지 않으면 재시작 전후가 중복으로 보이고 재시작 뒤의 유실은 가려집니다. 받은 순서가 일어난 순서가 아니라는 것도 같은 무게로 중요합니다 — 마감 시각에 집계하면 아직 안 온 줄이 빠지고, 며칠 뒤 같은 질의가 다른 숫자를 냅니다.

단계

  1. /root/gap/gen_gap.py 를 만들어 실행해 /root/gap/raw/ 아래 collector.ndjson 과 sender_state.json 을 만드세요.
  2. /root/gap/tally.json 에 받은 줄과 보낸 줄을 견주어 센 결과를 적으세요.
  3. /root/gap/missing.json 에 빠진 번호를 구간으로 묶어 적으세요.
  4. /root/gap/dedup.ndjson 에 중복을 접은 결과를 적으세요.
  5. /root/gap/delay.json 에 도착 지연 분포를 적으세요.
  6. /root/gap/cutoff.json 에 마감 시각 집계의 과소집계를 적으세요.
  7. /root/gap/reboot.json 에 재시작이 집계에 미친 영향을 적으세요.
  8. /root/gap/gap_report.md 에 네 절로 보고서를 남기세요.

참고

받은 것과 보낸 것을 함께 손에 쥐기

/root/gap/gen_gap.py 를 만들어 실행해 /root/gap/raw/collector.ndjson(2311줄, 중복 61 포함)과 /root/gap/raw/sender_state.json(스트림 4개, 보낸 줄 합계 2400)을 만드세요.

수집기 파일은 도착 순서(observed_ts)대로 쌓입니다. 보낸 쪽 카운터가 없으면 무엇이 빠졌는지 영영 알 수 없으므로 둘을 함께 만듭니다. 한 호스트는 중간에 재시작해서 (host, boot_id) 조합이 호스트 수보다 하나 많아야 합니다.

받은 줄과 보낸 줄을 견주기

/root/gap/tally.json 에 lines_in_file · unique_records · duplicate_lines · sent_total · missing_total · loss_rate 를 적으세요.

파일의 줄 수는 사건의 수가 아닙니다. 같은 줄이 두 번 온 것을 먼저 접어야 '받은 것' 이 나오고, 그것을 보낸 쪽 카운터와 빼야 '안 온 것' 이 나옵니다. 같은 줄인지는 (host, boot_id, seq) 로 봅니다.

빠진 번호를 구간으로 묶기

/root/gap/missing.json 에 스트림마다 빠진 번호를 연속 구간으로 묶어 host · boot_id · first · last · count 로 적으세요. (host, boot_id, first) 오름차순입니다.

흩어진 한 건씩의 손실과 50건이 한 덩어리로 빠진 것은 원인이 다릅니다. 그래서 개수만 세지 말고 구간으로 묶습니다. 스트림마다 보낸 쪽이 알려 준 first_seq 부터 last_seq 까지를 훑으면서, 받지 못한 번호가 이어지는 동안 구간을 늘려 갑니다.

재전송이 만든 중복 접기

/root/gap/dedup.ndjson 에 중복을 접은 레코드를 host · boot_id · seq · event_ts · observed_ts · msg 로, (host, boot_id, seq) 오름차순으로 쓰세요. 같은 줄이 여러 번 왔으면 가장 먼저 도착한 것을 남깁니다.

at-least-once 수집기에서 같은 줄이 두 번 오는 것은 고장이 아니라 설계입니다. 필요한 것은 '무엇을 같은 줄로 볼 것인가' 의 정의입니다. 내용의 지문으로 접으면 진짜로 똑같은 두 사건까지 하나가 되므로, 번호가 있을 때는 번호를 씁니다.

도착 지연의 분포 구하기

/root/gap/delay.json 에 count · p50_sec · p95_sec · max_sec · over_60s · worst(host · boot_id · seq · delay_sec)를 적으세요. 지연은 observed_ts 빼기 event_ts 를 초 단위 정수로 버린 값입니다.

받은 순서는 일어난 순서가 아닙니다. 두 시각을 따로 남겨 두는 이유가 이것이고, 둘의 차이가 수집 경로의 건강 상태입니다. 평균은 쓸모가 적습니다 — 대부분이 몇 초이고 일부가 몇 분이면 평균은 둘 다 설명하지 못합니다.

마감 시각에 집계했다면 얼마를 놓쳤나

/root/gap/cutoff.json 에 cutoff_ts · event_before_cutoff · observed_before_cutoff · late_arrivals · undercount_rate 를 적으세요. 마감 시각은 2026-05-20T03:20:00Z 이고 비교는 미만입니다.

같은 질의를 자정 직후와 사흘 뒤에 돌리면 숫자가 다릅니다. 버그가 아니라 늦은 도착입니다. 사건 시각이 마감 전인데 도착이 마감 후인 레코드가 바로 그때 못 센 것들이고, 그 비율이 마감 여유를 얼마나 두어야 하는지를 말해 줍니다.

재시작이 유실과 중복을 뒤바꾼다

/root/gap/reboot.json 에 hosts_with_multiple_boots · boots_by_host · false_duplicates · missing_with_boot · missing_without_boot 를 적으세요.

프로세스가 다시 뜨면 번호는 1부터 다시 시작합니다. 부팅 식별자를 빼고 세면 재시작 전후의 같은 번호가 중복으로 보이고, 재시작 뒤에 빠진 번호는 재시작 전의 기록에 가려 유실이 아닌 것으로 보입니다. 두 숫자를 나란히 내어 그 차이를 보이세요.

무엇을 요청할지 글로 남기기

/root/gap/gap_report.md## 무엇이 얼마나 빠졌나 ## 중복과 늦은 도착 ## 왜 숫자가 달라지나 ## 무엇을 요청할 것인가 네 절로 적으세요. 유실 건수 · 중복 줄 수 · 지연 p95 · 마감 전에 못 받은 건수 · boot_id 를 무시했을 때의 유실 건수를 숫자로 포함해야 합니다.

이 보고서의 마지막 절이 실제로 가장 값어치가 큽니다. 다음 묶음부터 무엇을 붙여 달라고 할지가 다음 조사의 난이도를 정하기 때문입니다. 발신 번호와 부팅 식별자, 사건 시각과 관측 시각을 모두 요구하는 이유를 앞의 숫자로 뒷받침하세요.