LabHub
学习 学习路径 课程

Loki — 不索引日志的日志库

我报告说没有错误,那一小时其实有三十条

在 LabHub 中继续学习

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

목표

파서가 꺼낸 이름이 스트림 라벨과 부딪혀 밀려나는 것을 직접 재현하고, label_format·line_format·keep·drop 으로 결과를 원하는 모양으로 정리합니다.

왜 중요한가

Loki 에서 라벨은 두 곳에서 온다 — 수집할 때 붙여 색인된 스트림 라벨과, 질의할 때 파서가 본문에서 꺼낸 라벨이다. 이름이 겹치면 나중에 꺼낸 쪽에 _extracted 가 붙어 밀려나는데 경고도 오류도 없다. 그래서 level="error" 가 0건을 돌려주고, 사람은 그것을 '오류가 없다' 로 읽는다. 질의 시점의 이름 정리는 결과에만 작용하고 색인은 건드리지 않으므로 마음껏 써도 비용이 늘지 않는다 — 카디널리티가 걱정되는 값을 본문에 두고 질의할 때 꺼내 쓰는 설계가 성립하는 이유다.

단계

  1. /root/lk-format 에서 Loki 를 띄우고 date +%s/root/lk-format/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 {app="payment"} 의 한 시간 줄 수와, 그 스트림에 붙어 있는 스트림 라벨 이름들을 /root/lk-format/01-boot.txt 에 두 줄로 적으세요 — lines=<정수>labels=<이름들을 쉼표로, 사전순>.
  2. {app="payment"} | logfmt | level="error" 를 한 시간 구간으로 던져 결과가 몇 줄인지 보고, 이름이 밀려난 라벨로 다시 물어 몇 줄인지 세세요. 답은 /root/lk-format/02-collision.txt 에 세 줄로 적습니다 — naive=<정수>, extracted_name=<밀려난 라벨 이름>, correct=<정수>.
  3. label_format 을 써서 본문의 수준 값이 level 이라는 이름으로 오게 만든 뒤 level="error" 로 거르세요. 질의는 /root/lk-format/03-relabel.logql, 답은 /root/lk-format/03-relabel.txtlines=<정수> 로 적습니다. 결과 줄 수는 2단계의 correct 와 같아야 합니다.
  4. {app="payment"} 의 모든 줄을 <svc>/<본문의 수준>/<dur_ms> 형태로 다시 쓰는 질의를 /root/lk-format/04-line.logql 에 쓰세요. 슬래시 두 개로만 나누고 다른 글자는 넣지 않습니다. 그리고 결과 중 가장 이른 줄의 본문을 /root/lk-format/04-line.txt 에 한 줄로 적으세요.
  5. 본문의 수준별로 줄 수를 세는 지표 질의를 /root/lk-format/05-bylevel.logql 에 쓰세요. 결과는 정확히 두 계열이어야 하고 각 계열의 라벨은 lvl 하나뿐이어야 합니다. 값은 /root/lk-format/05-bylevel.txtinfo=<정수>error=<정수> 두 줄로 적습니다.
  6. 5단계 질의에서 keep 대신 drop 을 써서 같은 결과를 내는 질의를 /root/lk-format/06-drop.logql 에 쓰세요. 그리고 /root/lk-format/06-drop.txt 에 두 줄을 적습니다 — series=<계열 수>dropped=<버린 라벨 이름들을 쉼표로, 사전순>.
  7. 3단계에서 label_format 으로 라벨을 만들었고 5단계에서는 새 이름으로 계열을 묶었습니다. 그 뒤 색인의 스트림 수가 달라졌는지 확인해 /root/lk-format/07-index.txt 에 두 줄로 적으세요 — streams=<정수>changed=<yes|no>. 스트림 수는 series API 로 셉니다.
  8. /root/lk-format/08-panel.logql 에 질의 하나를 쓰세요. 조건은 셋입니다 — (1) 본문의 수준이 error 인 줄만 돌려줄 것, (2) 돌려주는 본문이 <dur_ms>ms <svc> <region> 형태일 것(공백 두 개로만 나누고 다른 글자는 없음), (3) 줄 필터를 본문 다시 쓰기 에 둘 것. 그리고 결과의 가장 이른 줄을 /root/lk-format/08-panel.txt 에 한 줄로 적으세요.

참고

라벨과 본문이 어긋난 자료를 넣는다

/root/lk-format 에서 Loki 를 띄우고 date +%s/root/lk-format/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 {app="payment"} 의 한 시간 줄 수와, 그 스트림에 붙어 있는 스트림 라벨 이름들을 /root/lk-format/01-boot.txt 에 두 줄로 적으세요 — lines=<정수>labels=<이름들을 쉼표로, 사전순>.

스트림 라벨은 결과의 stream 객체의 키입니다. Loki 3.x 는 service_name 을 자동으로 붙이니 그것도 목록에 들어갑니다. 사전순으로 정렬해 쉼표로 이으세요(공백 없이).

level="error" 가 0건을 돌려준다

{app="payment"} | logfmt | level="error" 를 한 시간 구간으로 던져 결과가 몇 줄인지 보고, 이름이 밀려난 라벨로 다시 물어 몇 줄인지 세세요. 답은 /root/lk-format/02-collision.txt 에 세 줄로 적습니다 — naive=<정수>, extracted_name=<밀려난 라벨 이름>, correct=<정수>.

결과 하나를 jq '.data.result[0].stream' 으로 펼쳐 키 목록을 보세요. 파서가 꺼낸 이름이 이미 있는 스트림 라벨과 부딪히면 Loki 가 접미를 붙여 밀어냅니다. 그 이름으로 다시 물으면 숫자가 나옵니다.

label_format 으로 이름을 되돌린다

label_format 을 써서 본문의 수준 값이 level 이라는 이름으로 오게 만든 뒤 level="error" 로 거르세요. 질의는 /root/lk-format/03-relabel.logql, 답은 /root/lk-format/03-relabel.txtlines=<정수> 로 적습니다. 결과 줄 수는 2단계의 correct 와 같아야 합니다.

label_format새이름=기존라벨 형태로 쓰거나 템플릿을 줄 수 있습니다. 이미 있는 이름에 덮어쓰는 것도 됩니다. 순서가 중요합니다 — 이름을 바꾼 뒤에 걸러야 합니다.

line_format 으로 줄을 다시 쓴다

{app="payment"} 의 모든 줄을 <svc>/<본문의 수준>/<dur_ms> 형태로 다시 쓰는 질의를 /root/lk-format/04-line.logql 에 쓰세요. 슬래시 두 개로만 나누고 다른 글자는 넣지 않습니다. 그리고 결과 중 가장 이른 줄의 본문을 /root/lk-format/04-line.txt 에 한 줄로 적으세요.

line_format 은 Go 템플릿을 받습니다. 필드 이름 앞에 점을 찍습니다. 수준은 2단계에서 찾은 밀려난 이름을 써야 합니다. 가장 이른 줄은 direction=forward 로 받으면 맨 앞에 옵니다.

keep 으로 계열을 묶는다

본문의 수준별로 줄 수를 세는 지표 질의를 /root/lk-format/05-bylevel.logql 에 쓰세요. 결과는 정확히 두 계열이어야 하고 각 계열의 라벨은 lvl 하나뿐이어야 합니다. 값은 /root/lk-format/05-bylevel.txtinfo=<정수>error=<정수> 두 줄로 적습니다.

label_format 으로 쓸 이름을 만들고, keep 으로 그 이름만 남긴 뒤, 바깥을 sum by (lvl) (count_over_time(... [1h])) 로 감쌉니다. keep 을 빼면 무슨 일이 생기는지도 한 번 보세요 — 계열 수로 바로 드러납니다.

drop 은 무엇을 버리고 무엇을 남기나

5단계 질의에서 keep 대신 drop 을 써서 같은 결과를 내는 질의를 /root/lk-format/06-drop.logql 에 쓰세요. 그리고 /root/lk-format/06-drop.txt 에 두 줄을 적습니다 — series=<계열 수>dropped=<버린 라벨 이름들을 쉼표로, 사전순>.

drop 은 이름을 하나씩 적어 버립니다. 1단계에서 적어 둔 스트림 라벨 목록과 2단계에서 본 파서 라벨 목록을 합치면 무엇을 버려야 하는지 나옵니다. 남길 것이 적을 때는 keep, 버릴 것이 적을 때는 drop 이 짧습니다.

응용 ① — 라벨을 바꿔도 색인은 그대로다

3단계에서 label_format 으로 라벨을 만들었고 5단계에서는 새 이름으로 계열을 묶었습니다. 그 뒤 색인의 스트림 수가 달라졌는지 확인해 /root/lk-format/07-index.txt 에 두 줄로 적으세요 — streams=<정수>changed=<yes|no>. 스트림 수는 series API 로 셉니다.

/loki/api/v1/seriesmatch[]={app=~".+"} 를 주면 지금 색인에 있는 스트림 목록이 나옵니다. 질의 시점에 만든 라벨이 거기 있는지 보세요. 없다면 그것이 바로 "질의 시점의 라벨은 저장되지 않는다" 의 증거입니다.

응용 ② — 사람이 읽을 수 있는 오류 패널 질의

/root/lk-format/08-panel.logql 에 질의 하나를 쓰세요. 조건은 셋입니다 — (1) 본문의 수준이 error 인 줄만 돌려줄 것, (2) 돌려주는 본문이 <dur_ms>ms <svc> <region> 형태일 것(공백 두 개로만 나누고 다른 글자는 없음), (3) 줄 필터를 본문 다시 쓰기 에 둘 것. 그리고 결과의 가장 이른 줄을 /root/lk-format/08-panel.txt 에 한 줄로 적으세요.

순서가 의미입니다 — line_format 뒤의 필터는 새 본문을 봅니다. 그래서 거르기를 먼저, 다시 쓰기를 나중에 해야 합니다. 수준으로 거를 때는 2단계에서 찾은 이름이나 3단계에서 만든 이름을 쓰면 됩니다.