由於 Grafana Loki 提供的 Query examples 只有提供語法範例,沒有實際的日誌內容,對於初次接觸 LogQL 的人在理解上比較困難,為了避免自己忘記所以產生這一篇。
官方的範例語法是使用 /var/log/secure 日誌來作演示,我們就用 Ubuntu 的 /var/log/auth.log 日誌來操作看看吧。
安裝 Promtail
從 Loki 存儲庫下載最新的發行版本
https://github.com/grafana/loki/releases
wget "https://github.com/grafana/loki/releases/download/v2.7.1/promtail-linux-amd64.zip"
unzip promtail-linux-amd64.zip
chmod a+x promtail-linux-amd64
wget "https://raw.githubusercontent.com/grafana/loki/main/clients/cmd/promtail/promtail-local-config.yaml"
修改 promtail-local-config.yaml
vi promtail-local-config.yaml
書籤路徑 positions 負責記錄最後處理的事件,當 Promtail 重新啟動時需要它,以允許它從中斷的地方繼續讀取日誌。
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://your_loki_ip:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*log
透過以下指令啟動 Promtail,把 /var/log 底下的日誌推送到 Loki。
./promtail-linux-amd64 -config.file=promtail-local-config.yaml
我們故意產生一些日誌作為範例,先查看一下內容。
cat /var/log/auth.log
Feb 3 13:17:01 grafana CRON[401708]: pam_unix(cron:session): session opened for user root by (uid=0)
Feb 3 13:17:01 grafana CRON[401708]: pam_unix(cron:session): session closed for user root
Feb 3 13:38:09 grafana sudo: administrator : TTY=pts/1 ; PWD=/home/administrator ; USER=root ; COMMAND=/usr/bin/cat /etc/grafana/grafana.ini
Feb 3 13:38:09 grafana sudo: pam_unix(sudo:session): session opened for user root by administrator(uid=0)
Feb 3 13:38:09 grafana sudo: pam_unix(sudo:session): session closed for user root
Feb 3 13:40:04 grafana sshd[401739]: Accepted password for administrator from 192.168.0.92 port 57302 ssh2
Feb 3 13:40:04 grafana sshd[401739]: pam_unix(sshd:session): session opened for user administrator by (uid=0)
Feb 3 13:40:04 grafana systemd-logind[713]: New session 4284 of user administrator.
Feb 3 13:41:25 grafana sshd[401739]: pam_unix(sshd:session): session closed for user administrator
Feb 3 13:41:25 grafana systemd-logind[713]: Session 4284 logged out. Waiting for processes to exit.
Feb 3 13:41:25 grafana systemd-logind[713]: Removed session 4284.
Feb 3 13:41:59 grafana sshd[401901]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.0.92 user=administrator
Feb 3 13:42:01 grafana sshd[401901]: Failed password for administrator from 192.168.0.92 port 57374 ssh2
Feb 3 13:42:04 grafana sshd[401901]: error: Received disconnect from 192.168.0.92 port 57374:0: [preauth]
Feb 3 13:42:04 grafana sshd[401901]: Disconnected from authenticating user administrator 192.168.0.92 port 57374 [preauth]
不出意外的話,馬上可以用 Grafana Explore 查詢到。
LogQL Analyzer
沒有環境的朋友也可以利用官方提供的 LogQL Analyzer
https://grafana.com/docs/loki/latest/logql/analyzer/
把上面的 auth.log 貼到 log lines,就可以透過 Run query 進行練習。
練習1:過濾 IP 地址
Match IP addresses with the syntax: ip("").
The can be:
{filename="/var/log/auth.log"} |= ip("192.168.0.0/24")
過濾的結果如下
Feb 3 13:40:04 grafana sshd[401739]: Accepted password for administrator from 192.168.0.92 port 57302 ssh2
Feb 3 13:41:59 grafana sshd[401901]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.0.92 user=administrator
Feb 3 13:42:01 grafana sshd[401901]: Failed password for administrator from 192.168.0.92 port 57374 ssh2
Feb 3 13:42:04 grafana sshd[401901]: error: Received disconnect from 192.168.0.92 port 57374:0: [preauth]
Feb 3 13:42:04 grafana sshd[401901]: Disconnected from authenticating user administrator 192.168.0.92 port 57374 [preauth]
練習2:正規表達式 Regex Parser
從 /var/log/auth.log 中,提取登錄失敗的用戶和 IP 地址。
{filename="/var/log/auth.log"}
|~ "Failed password"
| regexp "(^(?P<user>\\S+ {1,2}){9})"
| regexp "(^(?P<ip>\\S+ {1,2}){11})"
| line_format "USER = {{.user}}\tIP = {{.ip}}"
過濾的結果如下
USER = administrator IP = 192.168.0.92
沒辦法一次看懂沒關係,我們從分解動作開始。
{filename="/var/log/auth.log"}
|~ "Failed password"
已經過濾出想要處理的日誌,接下來就是如何使用正規表達式解析取出用戶和 IP 地址這兩個欄位。
Feb 3 13:42:01 grafana sshd[401901]: Failed password for administrator from 192.168.0.92 port 57374 ssh2
Named Capturing Group (?P)
大多數的 Regex 引擎都支持 Named Group,意思就是你可以幫每個 Capturing Group 命名,以方便後續的讀取和引用。
例如:(?P\S+ {1,2}) 將匹配的字串組命名為 user
在 LogQL 就可以方便的用 | line_format “USER = {{.user}}” 取得匹配結果。
我們先從簡單的語法開始說明
^(?P<user>\S+ {1,2})
表示我要抓取開頭由多個為非空白字元(\S)所組成的字串且後面出現一到兩次空白的字串,並將其命名為 user 變數。
再加入一點變化
(^(?P<user>\S+ {1,2}){9})
代表我們要取出符合該 patten 的第 9 個字串,剛好就是登入的用戶名稱。
白話一點就是我要取出用空白做間格的第 9 個字串
A repeated capturing group will only capture the last iteration.
提取登錄失敗的用戶
{filename="/var/log/auth.log"}
|~ "Failed password"
| regexp "(^(?P<user>\\S+ {1,2}){9})"
| line_format "USER = {{.user}}"
接下來透過 Multiple Parsers,就可以輕鬆提取登錄失敗的用戶和 IP 地址。
{filename="/var/log/auth.log"}
|~ "Failed password"
| regexp "(^(?P<user>\\S+ {1,2}){9})"
| regexp "(^(?P<ip>\\S+ {1,2}){11})"
| line_format "USER = {{.user}}\tIP = {{.ip}}"
為避免轉義特殊字符,您可以在引用字符串時使用 `(backtick) 而不是 “。
例如 `\S+` 與 “\\S+” 相同。
練習3:模式解析器 Pattern Parser
覺得正規表達式太難、記不住?
沒關係,Loki 2.3 之後 LogQL 支援模式解析器使從非結構化日誌中提取數據變得更加容易。
通過指定以下內容在 LogQL 查詢中調用模式解析器
| pattern "<pattern-expression>"
捕獲定義字段名稱並由<和>字符分隔,未命名的捕獲<_>會跳過並忽略日誌行中的匹配內容。捕獲從行的開頭匹配,或者從前一組文字到行尾,或者到下一組文字。如果捕獲不匹配,模式解析器將停止處理日誌行。
從 /var/log/auth.log 中,提取登錄失敗的用戶和 IP 地址。
{filename="/var/log/auth.log"}
|~ "Failed password"
| pattern "<_>for <user> from <ip> <_>"
| line_format "USER = {{.user}}\tIP = {{.ip}}"
過濾的結果如下
USER = administrator IP = 192.168.0.92
驚不驚喜,意不意外,開不開心。
今天簡單的介紹如何從非結構化的日誌中透過 LogQL 提取出重要的指標,接下來就可以透過指標查詢(Metric Queries)從雜亂的日誌進行統計分析,做出有參考價值的儀表板。
參考文件