之前介紹過logstash的過濾器中有一種throttle過濾器,他可以設置一種規則來進行流量控制,當流量超過預設值時就會爲後續的消息添加指定的tag。可以通過使用此過濾器結合阿里釘釘機器人實現發送警報信息到釘釘聊天中。
釘釘機器人的配置
關於釘釘機器人的配置可以查看:釘釘開放平臺——機器人開發——自定義機器人開發
logstash配置
數據輸入的配置
input {
redis {
key => "logstash-dingtalk"
host => "localhost"
password => "root"
port => 6379
db => "0"
data_type => "list"
type => "dingtalk"
codec => plain{
charset=>"UTF-8"
}
}
}
數據的輸入沒有固定的要求,這裏取決於每個人不同的業務場景和需求,只需要能把數據引入logstash中就可以。
過濾器的配置
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:log_date} %{LOGLEVEL:log_info} %{DATA:thread} %{NOTSPACE} %{SPACE} %{NOTSPACE} %{JAVACLASS:log_class} %{SPACE}: %{GREEDYDATA:log_message}" }
}
if "_grokparsefailure" in [tags] {
drop {}
}
if [log_info] == "INFO" {
drop {}
}
if [log_info] == "ERROR" {
throttle {
before_count => -1
after_count => 3
period => 120
max_age => 240
key => "%{[log_info]}"
add_tag => "throttled"
}
}
}
數據過濾內容核心部分是:
- 通過grok對日誌信息的解析。
上面grok針對是我自己的日誌格式,日誌內容大概是這樣的
2020-05-20 21:22:23.456 ERROR 9160 [ main] o.s.j.e.a.AnnotationMBeanExporter : Exception encountered during context initialization
- 對流量的限制。其他部分就要根據不同的業務場景去選擇需不需要添加。
對流量限制,上面配置中限制了2分鐘最多隻有3條消息的上限限制(爲了測試所以設置的比較小),下限並沒有限制。
數據輸出的配置
output {
if "throttled" in [tags] {
http {
url => "https://oapi.dingtalk.com/robot/send?access_token={你的token}"
http_method => "post"
content_type => "application/json; charset=utf-8"
format => "message"
message => '{"msgtype":"text","text":{"content":"錯誤告警:120秒內錯誤日誌超過3條,請注意排查"}}'
}
}
stdout {
codec => rubydebug
}
}
數據輸出配置的核心內容有兩部分:
- 需要告警消息的確認。
- 發送告警消息
確定需要發送告警消息
根據官方文檔,當事件流量超過限制的時候會被throttle過濾器打上tag,在上面配置中也就是throttled
。所以此時應該使用的判斷是"throttled" in [tags]
。而不是網上很多文章的"throttled" not in [tags]
此時可以正常收到預警消息
關於告警消息的優化
上面內容完成了一個超過流量就向釘釘發送告警消息的全流程配置。看起來功能實現了,但是實際上目前的配置很可能沒法投入生產環境。
假如面對偶爾的流量超限上面配置還是沒問題的。但是在很多時候,當錯誤流量或者異常消息超過預設範圍的時候很可能不只是超出一兩條而是大量數據。而上面的配置在指定範圍中,沒超出一條就會向釘釘發送一次消息,直接的結果就是導致釘釘被刷屏。
而釘釘爲了避免出現這種情況,在文檔中專門指出限制條件。
每個機器人每分鐘最多發送20條。如果超過20條,會限流10分鐘。
結果就是在遇見異常流量的時候,先是受到一波信息轟炸,然後後續十分鐘無法獲得服務後續的情況。
針對上面的情況可以加一個限制條件,我們不需要每條超過流量的消息都去發送一條告警消息,可以緩一緩。
基於數量的控制
這裏就需要使用aggregate過濾器了。
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:log_date} %{LOGLEVEL:log_info} %{DATA:thread} %{NOTSPACE} %{SPACE} %{NOTSPACE} %{JAVACLASS:log_class} %{SPACE}: %{GREEDYDATA:log_message}" }
}
if "_grokparsefailure" in [tags] {
drop {}
}
if [log_info] == "INFO" {
drop {}
}
if [log_info] == "ERROR" {
throttle {
before_count => -1
after_count => 3
period => 120
max_age => 240
key => "%{[log_info]}"
add_tag => "throttled"
}
}
if "throttled" in [tags] {
aggregate {
task_id => "%{log_info}"
code => "map['throttledNum'] ||= 0 ; map['throttledNum'] += 1
event.set('throttledNum', map['throttledNum'])"
}
if [throttledNum] >= 3 {
aggregate {
task_id => "%{log_info}"
code => "map['throttledNum'] = 0"
add_tag => "throttled-num-out"
}
}
}
}
上面配置中將已經被節流的數據數量做一個緩存存在map['throttledNum']
中,同時設置當前數量到事件中,當事件的throttledNum
大於我們預設的3時,清空map
緩存並且添加throttled-num-out
標籤。這樣就能實現每3條節流事件只發送一條告警信息。
此時消息輸出的配置需要調整爲
output {
if "throttled-num-out" in [tags] {
http {
url => "https://oapi.dingtalk.com/robot/send?access_token={你的token}"
http_method => "post"
content_type => "application/json; charset=utf-8"
format => "message"
message => '{"msgtype":"text","text":{"content":"錯誤告警:120秒內錯誤日誌超過3條,請注意排查"}}'
}
}
stdout {
codec => rubydebug
}
}
基於時間的控制
但這個還不是完美的,既然是異常我們就沒辦法估計其最終可能產生的速度,這個時候我們可以基於時間來限制發送頻率
這裏就需要使用aggregate過濾器了。
filter {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:log_date} %{LOGLEVEL:log_info} %{DATA:thread} %{NOTSPACE} %{SPACE} %{NOTSPACE} %{JAVACLASS:log_class} %{SPACE}: %{GREEDYDATA:log_message}"
}
}
if "_grokparsefailure" in [tags] {
drop {}
}
if [log_info] == "INFO" {
drop {}
}
if [log_info] == "ERROR" {
throttle {
before_count => -1
after_count => 3
period => 120
max_age => 240
key => "%{[log_info]}"
add_tag => "throttled"
}
}
if "throttled" in [tags] {
aggregate {
task_id => "%{log_info}"
code => "event.set('throttled_time',Time.parse(event.get('log_date')).to_f*1000)
map['throttled_time'] || = 0
event.set('throttled_time_out', (event.get('throttled_time') - map['throttled_time']) > 10000)
"
}
if [throttled_time_out] {
aggregate {
task_id => "%{log_info}"
code => "map['throttled_time'] = event.get('throttled_time')
"
}
}
}
}
和之前的配置不同,此時緩存的是時間。
- 首先得到日誌時間的long類型表示,設置到事件的
throttled_time
中。 - 然後判斷事件當前時間,和用來計算上一次發送釘釘預警時間是否超過預設時間(10000毫秒)。
- 當
throttled_time_out
的值爲真的時候表示距離上次發送預警信息時間間隔超過了(10000毫秒)。 - 則將
map['throttled_time']
的值更新爲當前事件時間,同時在數據輸出配置中發送預警消息。
此時消息輸出的配置需要調整爲
output {
if [throttled_time_out] {
http {
url => "https://oapi.dingtalk.com/robot/send?access_token={你的token}"
http_method => "post"
content_type => "application/json; charset=utf-8"
format => "message"
message => '{"msgtype":"text","text":{"content":"錯誤告警:120秒內錯誤日誌超過3條,請注意排查"}}'
}
}
stdout {
codec => rubydebug
}
}
上面關於logstash向釘釘發送告警消息的內容僅僅是個人思路。logstash提供了執行Ruby code的相關處理器,在使用Ruby code的情況下應該能剛好的實現上面的內容。當然獲取使用其他方式一樣可以避免出現消息轟炸。
個人水平有限,上面的內容可能存在沒有描述清楚或者錯誤的地方,假如開發同學發現了,請及時告知,我會第一時間修改相關內容。假如我的這篇內容對你有任何幫助的話,麻煩給我點一個贊。你的點贊就是我前進的動力。