1. 程式人生 > 實用技巧 >Logstash中建ES索引的時區問題

Logstash中建ES索引的時區問題

最近工作中遇到一個Logstash中建ES索引的時區問題,對資料統計造成了一定的影響。

logstash.conf檔案(簡化了業務程式碼):

input{
  ...
}

filter{
  date {
    match => ["access_time", "yyyy/MM/dd HH:mm:ss Z"]
    target => "@timestamp"
  }
}
output {
     #stdout { codec => rubydebug }
     elasticsearch {
         hosts => [...]
         index 
=> "log-%{+YYYY.MM.dd}" } }

問題描述:

我們把日誌中的時間存進變數access_time欄位,按天建立ES中的索引統計日誌,預想情況是例如access_time為2020.12.15的日誌都存在ES的log-2020.12.15索引中。

我們後續採用Grafana查詢ES,比如根據@timestamp查某個時間範圍內的資料。由於logstash會預設把@timestamp轉換成UTC時間(格林威治標準時間),我們專門進行了時間轉換,把日誌中的access_time欄位(北京時間)通過match的格式匹配轉換,使得@timestamp欄位也是北京時間。此時日誌中的access_time與logstash中的@timestamp完全一致

,建ES索引時,%{+YYYY.MM.dd}會根據@timestamp欄位的值取出年月日。

看似很順利,但問題出現了。Elasticsearch 內部,對時間型別欄位,是統一採用 UTC 時間,存成 long 長整形資料的!對日誌統一採用 UTC 時間儲存,是國際安全/運維界的一個通識——歐美公司的伺服器普遍廣泛分佈在多個時區裡——不像中國,地域橫跨五個時區卻只用北京時間。因此ES在建索引時會把@timestamp這個時間型別欄位又轉成UTC時間(-8小時)。

此時資料就會出現統計偏差,access_time為 2020/12/15 00:00 — 2020/12/15 8:00 之間的日誌資料,本該存放在log-2020.12.15索引下,但由於時區轉換問題,ES讀取的@timestamp被減了8小時,為 2020/12/14 16:00 — 2020/12/15 00:00,導致這些日誌資料會被統計到log-2020.12.14索引下。

而log-2020.12.15索引下統計的日誌,實際上它們的access_time欄位是在 2020/12/15 8:00 — 2020/12/16 8:00

圖1

圖2

如圖1所示,對於頁面檢視,ELK 的解決方案是在 Kibana 上,讀取瀏覽器的當前時區,然後在頁面上轉換時間內容的顯示。

如圖2所示,我們可以看到圖2中Kibana已經自動把@timestamp轉成了北京時間。這是一條access_time為 2020/12/15 06:00:00的日誌,@timesstamp與access_time一致,但卻是統計在log-2020.12.14索引中。

解決方法:

其實Kibana上看到的@timestamp已經是正確的了(與日誌中的access_time一致),我們只需要解決索引的問題。

由於@timestamp欄位是正確的,我們不改變該欄位,而是用一個變數index_day代替@timestamp去建立索引

這個變數中儲存的時間是@timestamp+8小時,這樣ES對於這個時間變數index_day就會-8小時,此時index_day在ES中儲存的值就是北京時間,相當於建立索引時以北京時間為依據,也就對上了。

input{
  ...
}

filter{
  date {
    match => ["access_time", "yyyy/MM/dd HH:mm:ss Z"]
    target => "@timestamp"
  }
   ruby{
       code => "event.set('index_day', (event.get('@timestamp').time.localtime + 8*60*60).strftime('%Y.%m.%d'))"
   }
}
output {
     #stdout { codec => rubydebug }
     elasticsearch {
         hosts => [...]
index => "log-%{index_day}" #index
=> "log-%{+YYYY.MM.dd}" } }

如果想及時知道建立的索引名字是否正確,可以先使用strftime('%Y.%m.%d.%H')替換strftime('%Y.%m.%d')進行測試。此時ES中索引的名字會顯示小時,可以根據小時是否為北京時間判斷是否更改成功。