LabHub
배우기 러닝패스 코스

ログから原因を見つける

障害の3分を見ようとして一日分を丸ごと読んだ

LabHub 에서 이어서 보기

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

목표

회전까지 끝난 여섯 시간치 로그에서 사고 구간 3분만 꺼냅니다. 겹치지 않는 파일은 열지 않고, 남은 파일에서는 이진 탐색으로 바이트 구간을 찍어 그 사이만 읽고, 압축본은 앞에서부터 훑되 일찍 멈춥니다.

왜 중요한가

"3분만 보여 달라" 는 요청에 하루치를 통째로 읽는 것이 로그 작업에서 가장 흔한 사고입니다. 그런데 시각이 RFC 3339 표기로 굳어 있고 파일이 시간순이면 문자열 비교가 곧 시간 비교라, 파일을 읽지 않고 이진 탐색으로 구간의 시작 자리를 찍을 수 있습니다. 어려운 것은 탐색이 아니라 경계입니다. 구간을 반열림으로 잡아야 이어지는 구간이 겹치지 않고, 같은 시각의 줄이 여럿일 때 그 시각의 첫 줄을 찾아야 앞의 몇 줄이 조용히 빠지지 않습니다. 그리고 빠르다는 말만으로는 아무것도 증명되지 않으므로, 통째로 훑는 느린 방법과 한 번은 대조해야 합니다.

단계

  1. /root/slice/gen_slice.py 를 만들어 실행해 /root/slice/var/log/ 아래 app.log · app.log.1 · app.log.2.gz 를 만드세요.
  2. /root/slice/index.json — 회전본을 오래된 것부터 세우고 파일마다 담긴 구간을 적으세요.
  3. /root/slice/plan.json — 사고 구간과 겹치는 파일만 고르고 나머지는 이유와 함께 건너뛰세요.
  4. /root/slice/offsets.json — 이진 탐색으로 구간의 시작 바이트와 끝 바이트를 찍으세요.
  5. /root/slice/window_a.log — 찍어 둔 바이트 구간만 읽어 사고 구간을 뽑으세요.
  6. /root/slice/proof.json — 느린 방법과 대조해 결과가 같음을 증명하세요.
  7. /root/slice/window_b.log/root/slice/gz_scan.json — 압축본 안의 두 번째 구간을 일찍 멈추며 뽑으세요.
  8. /root/slice/slice_report.md — 방법과 근거를 보고서로 남기세요.

참고

회전까지 끝난 로그 묶음 재현하기

/root/slice/gen_slice.py 를 만들어 실행해 /root/slice/var/log/ 아래 app.log(57600줄) · app.log.1(57600줄) · app.log.2.gz(풀면 57600줄)를 만드세요.

세 파일은 같은 프로그램이 쓴 같은 형식이고, 회전으로 쪼개진 것뿐입니다. 초당 여덟 줄로 두 시간치씩 담고, 30초마다 같은 밀리초에 세 줄이 겹치게 하세요. 압축본은 파이썬 gzip.GzipFile 에 mtime=0 을 주어 쓰면 다시 만들어도 같은 파일이 나옵니다.

회전본을 시간순으로 세우고 담긴 구간 재기

/root/slice/index.jsonfiles(오래된 것부터 담은 배열, 항목마다 file · compressed · bytes · first_ts · last_ts)와 rule(그렇게 세운 규칙 한 문장, 20자 이상)을 적으세요. 시각은 줄의 앞 24자를 그대로 씁니다.

번호가 붙은 회전본은 숫자가 클수록 과거이고, 확장자가 없는 것이 지금 쓰는 파일입니다. logrotate 의 dateext 를 쓰면 이름이 날짜라 방향이 반대가 되니 규칙을 적을 때 함께 짚어 두세요. 비압축 파일의 마지막 줄은 파일 끝에서 몇 KB 만 읽어도 꺼낼 수 있습니다 — 통째로 읽지 마세요. 재료는 /root/slice/var/log/ 아래 세 파일입니다.

겹치지 않는 파일은 아예 열지 않기

/root/slice/plan.jsonwindow(start 는 2026-04-12T03:58:30.000Z, end 는 2026-04-12T04:01:30.000Z)와 open(구간과 겹치는 파일을 오래된 것부터 담은 배열), skip(나머지 파일을 file · reason 으로 담은 배열)을 적으세요.

2단계에서 잰 first_ts 와 last_ts 만 있으면 파일을 열지 않고도 겹치는지 알 수 있습니다. 구간이 반열림이므로 겹침 판정도 반열림입니다 — 파일의 시작이 구간의 끝보다 앞서고, 파일의 끝이 구간의 시작보다 앞서지 않으면 겹칩니다. 재료는 /root/slice/index.json 입니다. reason 은 10자 이상으로 적으세요.

이진 탐색으로 구간의 시작과 끝을 바이트로 찍기

/root/slice/offsets.jsonoffsets(3단계에서 고른 비압축 파일마다 file · start_offset · end_offset 을 담은 배열, 오래된 것부터)를 적으세요. start_offset 은 시각이 구간 시작 이상인 첫 줄의 시작 바이트이고, end_offset 은 구간 끝 이상인 첫 줄의 시작 바이트입니다(없으면 파일 크기).

파일 크기를 반으로 접어 그 바이트로 seek 하면 거의 늘 줄 한가운데입니다. 한 줄을 읽어 버려 줄 경계에 선 뒤 그 줄의 앞 24자로 판정하세요. 같은 시각의 줄이 셋 있으므로 '조건을 만족하는 아무 줄' 이 아니라 '조건을 처음 만족하는 줄' 을 찾아야 합니다 — 조건을 만족하면 범위의 오른쪽 끝을 그 줄의 시작으로 당기세요. 재료는 /root/slice/plan.json 입니다.

찍어 둔 바이트 구간만 읽어 사고 구간 뽑기

/root/slice/window_a.log 에 사고 구간 [2026-04-12T03:58:30.000Z, 2026-04-12T04:01:30.000Z) 의 줄을 시간순으로, 원문 그대로 쓰세요. 파일은 오래된 것부터 이어 붙입니다.

4단계에서 찍은 두 오프셋 사이를 그대로 읽어 내면 됩니다. end_offset 이 구간 밖 첫 줄의 시작이라 그 앞까지가 정확히 반열림 구간입니다. 줄을 다시 파싱하거나 다듬지 마세요 — 원문 바이트를 그대로 옮겨야 다음 단계의 해시 대조가 성립합니다. 재료는 /root/slice/offsets.json 입니다.

느린 방법과 대조해 결과를 증명하기

/root/slice/proof.json 에 window · fast_lines · slow_lines · sha256_fast · sha256_slow · match(불리언) · bytes_read_fast · bytes_read_slow 를 적으세요. bytes_read_fast 는 4단계의 두 오프셋 사이 바이트의 합이고, bytes_read_slow 는 압축본까지 풀어 세 파일을 통째로 읽을 때의 바이트입니다.

느린 방법은 세 파일을 전부(압축본은 풀어서) 훑으며 구간의 줄을 모으는 것입니다. 시간은 재지 마세요 — 기계마다 흔들립니다. 대신 줄 수와 sha256 해시를 견줍니다. 해시는 뽑힌 줄을 이어 붙인 바이트에 대해 계산하고, 두 방법의 값이 같아야 match 가 참입니다. 재료는 /root/slice/window_a.log 와 /root/slice/offsets.json 입니다.

압축본에서는 일찍 멈추는 것이 답이다

두 번째 질문은 [2026-04-12T00:20:00.000Z, 2026-04-12T00:23:00.000Z) 입니다. /root/slice/window_b.log 에 그 구간의 줄을 원문 그대로 쓰고, /root/slice/gz_scan.json 에 window · file · total_lines · lines_read · lines_kept 를 적으세요. lines_read 는 멈출 때까지 실제로 푼 줄 수입니다.

이 구간은 압축본 안에 있습니다. gzip 스트림은 앞의 내용에 기대어 풀리므로 임의 지점으로 건너뛸 수 없고, 이진 탐색을 걸면 같은 자리를 몇 번이고 다시 푸는 꼴이 됩니다. 앞에서부터 한 번만 훑되 구간의 끝을 지나는 줄을 만나면 그 자리에서 빠져나오세요. total_lines 는 얼마나 아꼈는지 적기 위한 값이라 한 번은 끝까지 세어야 합니다. 재료는 /root/slice/var/log/app.log.2.gz 입니다.

방법과 근거를 문서로 남기기

/root/slice/slice_report.md## 무엇을 물었나 ## 왜 통째로 읽지 않아도 되었나 ## 압축본은 무엇이 달랐나 ## 결과를 어떻게 증명했나 네 절로 적으세요. 사고 구간의 줄 수 · 빠른 방법이 읽은 바이트 · 압축본에서 푼 줄 수를 숫자로 포함해야 합니다.

이 문서를 읽을 사람은 다음에 같은 질문을 받는 사람입니다. 그 사람이 알아야 하는 것은 명령이 아니라 전제입니다 — 왜 이진 탐색이 성립했는지, 어떤 파일을 왜 열지 않았는지, 압축본에서 무엇이 달랐는지, 결과를 무엇과 대조했는지. 숫자는 지어내지 말고 /root/slice/proof.json 과 /root/slice/gz_scan.json 에서 꺼내 쓰세요.