最近我在尋找一種方法,讓樹梅派能夠監控系統 log,並在出現錯誤層級 Log 時發送通知。我曾考慮過 Prometheus Alertmanager,但它似乎無法查看 Log。我也看過 Loki,它確實能達到我要的效果,但我不希望建立集中式 Log,因為樹梅派的儲存空間有限。我也考慮過 mtail,但它可能無法正確解析 Log 層級,所以我最終沒有選擇它。至於 openITCOCKPIT,雖然它需要安裝資料庫,但我認為這不適合在樹梅派上進行,儘管官方有提供安裝教學。
為何我需要系統錯誤通知?

我曾在 Facebook 社群中看到有人在收到 SELinux 系統入侵的通知後,能夠立即採取行動。這篇留言提到他們是使用 Cockpit,但我發現這個工具並沒有我需要的功能。我知道 Zabbix 也是一個選擇,但我這次不打算使用它。
我注意到 Cockpit 是使用 journalctl 來查看系統 log。由於沒有現成的工具可以滿足我的需求,我決定自己開發一個😆。
準備工作
首先,我們需要安裝 jq。你可以使用以下的命令來安裝:
1
2
|
sudo apt update
sudo apt install jq
|
監聽 Log 並發送通知
我將介紹如何使用 Discord、Line Notify 和 Prometheus Alertmanager 來監聽 Log 並發送通知。你可以根據自己的需求選擇使用哪一種方法。
使用 Discord Webhook 進行通知
以下是一個 Bash 腳本,它會監聽系統的 Log,並在出現錯誤時通過 Discord Webhook 發送通知。你需要將 WEBHOOK_URL 變數設定為你的 Discord Webhook Url。此腳本會每小時檢查一次 Log,並在出現錯誤時發送通知。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#!/bin/bash
# Discord webhook URL
WEBHOOK_URL="https://discord.com/api/webhooks/...."
# Show Log Line Count
SHOW_LOG_CNT=10
# Set the priority level (0-7)
PRIORITY=3
# Get the hostname and IP address
HOSTNAME=$(hostname)
IP_ADDRESS=$(hostname -I | awk '{print $1}')
# Run the journalctl command with the specified priority
ALL_OUTPUT=$(journalctl --since=-1hours --priority=$PRIORITY --no-pager | grep -v -- "-- No entries --" | grep -v -- "-- Logs begin at")
# Count the number of error messages
ERROR_COUNT=$(echo "$ALL_OUTPUT" | wc -l)
# Extract the first SHOW_LOG_CNT lines of output
OUTPUT=$(echo "$ALL_OUTPUT" | head -n $SHOW_LOG_CNT)
# Set the color based on the priority level
COLOR=""
case $PRIORITY in
0|1|2|3) COLOR="16711680" ;; # Red for emerg, alert, crit, err
4) COLOR="16776960" ;; # Yellow for warning
5|6) COLOR="65280" ;; # Green for notice, info
7) COLOR="255" ;; # Blue for debug
esac
# Check if there are any error messages
if [ -n "$OUTPUT" ]; then
# Format the message as a JSON payload with embeds
JSON_PAYLOAD=$(jq -n --arg msg "$OUTPUT" --arg hn "$HOSTNAME" --arg ip "$IP_ADDRESS" --argjson ec "$ERROR_COUNT" --argjson color "$COLOR" \
'{content: $msg, embeds: [{title: "Error Report", color: $color, fields: [{name: "Hostname", value: $hn, inline: true}, {name: "IP Address", value: $ip, inline: true}, {name: "Number of Errors", value: $ec, inline: true}]}]}')
# Send the message to the Discord webhook
curl -X POST -H "Content-Type: application/json" -d "$JSON_PAYLOAD" $WEBHOOK_URL
fi
|
使用 Line Notify 進行通知
如果你想使用 Line Notify 進行通知,你需要設定 LINE_TOKEN。你可以參考我之前寫的這篇 Line Notify 教學設定。
以下是一個 Bash 腳本,它會監聽系統的 Log,並在出現錯誤時通過 Line Notify 發送通知。你需要將 LINE_TOKEN 變數設定為你的 Line Notify Token。此腳本會每小時檢查一次 Log,並在出現錯誤時發送通知。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#!/bin/bash
# Line Notify token
LINE_TOKEN="YOUR_LINE_NOTIFY_TOKEN"
# Show Log Line Count
SHOW_LOG_CNT=10
# Set the priority level (0-7)
PRIORITY=3
# Get the hostname and IP address
HOSTNAME=$(hostname)
IP_ADDRESS=$(hostname -I | awk '{print $1}')
# Run the journalctl command with the specified priority
OUTPUT=$(journalctl --since=-1hours --priority=$PRIORITY --no-pager -n $SHOW_LOG_CNT)
# Count the number of error messages
ERROR_COUNT=$(echo "$OUTPUT" | wc -l)
# Check if there are any error messages
if [ -n "$OUTPUT" ]; then
# Format the message
MESSAGE="Hostname: $HOSTNAME\nIP Address: $IP_ADDRESS\nNumber of Errors: $ERROR_COUNT\n$OUTPUT"
# Send the message to the Line Notify API
curl -X POST -H "Authorization: Bearer $LINE_TOKEN" -F "message=$MESSAGE" https://notify-api.line.me/api/notify
fi
|
使用 Prometheus AlertManager 進行通知
Warning
請確保你已經設定了 Prometheus AlertManager 的網址。
以下是一個 Bash 腳本,它會監聽系統的 Log,並在出現錯誤時通過 Prometheus AlertManager 發送通知。你需要將 ALERTMANAGER_API 變數設定為你的 Prometheus AlertManager API 網址。此腳本會每小時檢查一次 Log,並在出現錯誤時發送通知。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#!/bin/bash
# AlertManager API URL
ALERTMANAGER_API="http://192.168.xx.xx:9093/api/v1/alerts"
# Set the priority level (0-7)
PRIORITY=3
# Get the hostname and IP address
HOSTNAME=$(hostname)
IP_ADDRESS=$(hostname -I | awk '{print $1}')
INSTANCE="$HOSTNAME($IP_ADDRESS)"
# Run the journalctl command with the specified priority
ALL_OUTPUT=$(journalctl --since=-1hours --priority=$PRIORITY --no-pager | grep -v -- "-- No entries --" | grep -v -- "-- Logs begin at")
# Count the number of error messages
ERROR_COUNT=$(echo "$ALL_OUTPUT" | wc -l)
# Extract the first SHOW_LOG_CNT lines of output
OUTPUT=$(echo "$ALL_OUTPUT" | head -n $SHOW_LOG_CNT)
# Format the summary message
SUMMARY="總共 $ERROR_COUNT 個錯誤訊息"
# Check if there are any error messages
if [ -n "$OUTPUT" ]; then
# Format the alert as a JSON payload
JSON_PAYLOAD=$(jq -n --arg inst "$INSTANCE" --arg sum "$SUMMARY" --arg msg "$OUTPUT" \
'[{labels: {alertname: "JournalctlError", instance: $inst }, annotations: { description: $msg, summary: $sum}}]')
# Send the alert to the AlertManager API
curl -X POST -H "Content-Type: application/json" -d "$JSON_PAYLOAD" $ALERTMANAGER_API
fi
|
排程設定
在 Linux 系統中,我們可以使用 crontab 來設定定時任務。以下是如何設定每小時執行一次腳本的步驟:
-
首先,打開終端機並輸入 crontab -e 來編輯你的 crontab 文件。
-
在文件中,你可以添加或修改任務。每一行都代表一個任務,並且由六個字段組成:分、時、日、月、週和命令。例如,0 * * * * /home/username/scripts/hourly_error_report.sh > /dev/null 2>&1 這行命令的意思是每小時的第 0 分鐘執行 /home/username/scripts/hourly_error_report.sh 這個腳本。
Info
bash ... > /dev/null 2>&1 這段程式碼的作用是將指令的輸出和錯誤輸出都重定向到/dev/null,這樣就可以將它們丟棄,不會在終端上顯示或存儲。這在某些情況下很有用,例如當你只關心指令是否成功執行,而不關心輸出或錯誤訊息時。
這段程式碼的每個部分:
>:這是一個重定向運算符,用於將指令的輸出重定向到指定的位置。
/dev/null:這是一個特殊的設備文件,它可以被視為一個黑洞。將輸出重定向到/dev/null意味著將輸出丟棄,不會在終端上顯示或存儲。
2>&1:這是一個錯誤輸出重定向的語法。數字2代表錯誤輸出,而&1代表標準輸出。這個語法的意思是將錯誤輸出重定向到與標準輸出相同的位置,也就是/dev/null。
- 最後,儲存並關閉文件。你的任務將會在設定的時間自動執行。
以下是具體的命令:
1
2
3
4
|
crontab -e
# 每小時執行
0 * * * * /home/username/scripts/hourly_error_report.sh > /dev/null 2>&1
|
彩蛋
monit