實際應用中除了基於 Metrics 告警, 往往還有基於紀錄檔的告警需求, 可以作為基於 Metrics 告警之外的一個補充. 典型如基於 NGINX 紀錄檔的錯誤率告警.本文將介紹如何基於 Loki 實現基於紀錄檔的告警.
本文我們基於以下 2 類實際場景進行實戰演練:
基於紀錄檔告警的廣泛應用於如下場景:
對於不是我們開發的元件, 如雲廠商/第三方的負載均衡器和無數其他元件(包括開源元件和封閉第三方元件)支援我們的應用程式,但不會公開我們想要的指標。有些根本不公開任何指標。 Loki 的警報和記錄規則可以生成有關係統狀態的指標和警報,並通過使用紀錄檔將元件帶入我們的可觀察性堆疊中。這是一種將高階可觀察性引入遺留架構的極其強大的方法。
有時,您想知道某件事情是否已經發生。根據紀錄檔發出警報可以很好地解決這個問題,例如查詢身份驗證憑據洩露的範例:
- name: credentials_leak
rules:
- alert: http-credentials-leaked
annotations:
message: "{{ $labels.job }} is leaking http basic auth credentials."
expr: 'sum by (cluster, job, pod) (count_over_time({namespace="prod"} |~ "http(s?)://(\\w+):(\\w+)@" [5m]) > 0)'
for: 10m
labels:
severity: critical
關於 Nomad 的就屬於這類場景.
Grafana Loki 包含一個名為 ruler 的元件。Ruler 負責持續評估一組可設定查詢並根據結果執行操作。其支援兩種規則:alerting 規則和 recording 規則。
Loki 的告警規則格式幾乎與 Prometheus 一樣. 這裡舉一個完整的例子:
groups:
- name: should_fire
rules:
- alert: HighPercentageError
expr: |
sum(rate({app="foo", env="production"} |= "error" [5m])) by (job)
/
sum(rate({app="foo", env="production"}[5m])) by (job)
> 0.05
for: 10m
labels:
severity: page
annotations:
summary: High request latency
- name: credentials_leak
rules:
- alert: http-credentials-leaked
annotations:
message: "{{ $labels.job }} is leaking http basic auth credentials."
expr: 'sum by (cluster, job, pod) (count_over_time({namespace="prod"} |~ "http(s?)://(\\w+):(\\w+)@" [5m]) > 0)'
for: 10m
labels:
severity: critical
Loki 紀錄檔查詢語言 (LogQL) 是一種查詢語言,用於從 Loki 中檢索紀錄檔。LogQL 與 Prometheus 非常相似,但有一些重要的區別。
所有 LogQL 查詢都包含紀錄檔流選擇器(log stream selector)。如下圖:
可選擇在紀錄檔流選擇器後新增紀錄檔管道(log pipeline)。紀錄檔管道是一組階段表示式,它們串聯在一起並應用於選定的紀錄檔流。每個表示式都可以過濾、解析或更改紀錄檔行及其各自的標籤。
以下範例顯示了正在執行的完整紀錄檔查詢:
{container="query-frontend",namespace="loki-dev"}
|= "metrics.go"
| logfmt
| duration > 10s
and throughput_mb < 500
該查詢由以下部分組成:
{container="query-frontend",namespace="loki-dev"}
,其目標是 loki-dev
名稱空間中的 query-frontend
容器。|= "metrics.go" | logfmt | duration > 10s and throughput_mb < 500
它將過濾掉包含單詞 metrics.go
的紀錄檔,然後解析每個紀錄檔行以提取更多標籤並使用它們進行過濾。為了進行告警, 我們往往需要在告警之前對非結構化紀錄檔進行解析, 解析後會獲得更精確的欄位資訊(稱為label
), 這就是為什麼我們需要使用解析器表示式.
解析器表示式可從紀錄檔內容中解析和提取標籤(label)。這些提取的標籤可用於使用標籤過濾表示式進行過濾,或用於 metrics 彙總。
如果原始紀錄檔流中已經存在提取的標籤 key名稱(典型如: level
),提取的標籤 key 將以 _extracted
關鍵字為字尾,以區分兩個標籤。你也可以使用標籤格式表示式強行覆蓋原始標籤。不過,如果提取的鍵出現兩次,則只保留第一個標籤值。
Loki 支援 JSON、logfmt、pattern、regexp 和 unpack 解析器。
今天我們重點介紹下 logfmt, pattern 和 regexp 解析器。
logfmt 解析器可以以兩種模式執行:
可以使用 | logfmt
新增 logfmt 解析器,並將從 logfmt 格式的紀錄檔行中提取所有鍵和值。
例如以下紀錄檔行:
at=info method=GET path=/ host=grafana.net fwd="124.133.124.161" service=8ms status=200
將提取到以下標籤:
"at" => "info"
"method" => "GET"
"path" => "/"
"host" => "grafana.net"
"fwd" => "124.133.124.161"
"service" => "8ms"
"status" => "200"
與 JSON 解析器類似,在管道中使用 | logfmt label="expression", another="expression"
將導致只提取標籤指定的欄位。
例如, | logfmt host, fwd_ip="fwd"
將從以下紀錄檔行中提取標籤 host
和 fwd
:
at=info method=GET path=/ host=grafana.net fwd="124.133.124.161" service=8ms status=200
並將 fwd
重新命名為 fwd_ip
:
"host" => "grafana.net"
"fwd_ip" => "124.133.124.161"
Pattern 解析器允許通過定義模式表示式(| pattern "<pattern-expression>"
)從紀錄檔行中明確提取欄位。該表示式與紀錄檔行的結構相匹配。
典型如 NGINX 紀錄檔:
0.191.12.2 - - [10/Jun/2021:09:14:29 +0000] "GET /api/plugins/versioncheck HTTP/1.1" 200 2 "-" "Go-http-client/2.0" "13.76.247.102, 34.120.177.193" "TLSv1.2" "US" ""
該紀錄檔行可以用表示式解析:
<ip> - - <_> "<method> <uri> <_>" <status> <size> <_> "<agent>" <_>
提取出這些欄位:
"ip" => "0.191.12.2"
"method" => "GET"
"uri" => "/api/plugins/versioncheck"
"status" => "200"
"size" => "2"
"agent" => "Go-http-client/2.0"
Pattern 表示式由捕獲(captures )和文字組成。
捕獲是以 <
和 >
字元分隔的欄位名。<example>
定義欄位名 example
。未命名的捕獲顯示為 <_>
。未命名的捕獲會跳過匹配的內容。
logfmt 和 json 會隱式提取所有值且不需要引數,而 regexp 解析器則不同,它只需要一個引數 | regexp "<re>"
,即使用 Golang RE2 語法的正規表示式。
正規表示式必須包含至少一個命名子匹配(例如 (?P<name>re)
),每個子匹配將提取不同的標籤。
例如,解析器 | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)"
將從以下行中提取:
POST /api/prom/api/v1/query_range (200) 1.5s
到這些標籤:
"method" => "POST"
"path" => "/api/prom/api/v1/query_range"
"status" => "200"
"duration" => "1.5s"