在開發 jt-glogarch(Graylog Open 的記錄歸檔工具)時,有一個需求是:查出每個 index set、每個 stream 最早一筆記錄的時間,用來判斷要從哪裡開始歸檔、還有多少資料沒歸檔。
這種「只要一個值」的指標型查詢,寫法不同,傳回的資料量可以差上百倍。只查一次感覺不出來,但要批次遍歷所有 index set 時差距就很明顯。這篇整理我比較過的幾種做法,以及官方 API 的幾個細節。
方法比較
| 方法 | 查詢對象 | 傳回大小 | 適用情境 |
|---|---|---|---|
| Index Ranges API | Graylog(MongoDB 裡的記錄) | 小,不查 OpenSearch | 依 index 判斷時間範圍,最省 |
| 直接查 OpenSearch | OpenSearch | 約 100 bytes | 要精確到某個 index pattern,批次掃描 |
| Views 匯出 API | Graylog | 約 50 bytes(CSV) | 依 stream 查,走 Graylog 權限 |
| Universal Search(Legacy) | Graylog | 數百 bytes(JSON) | 舊程式相容 |
Index Ranges API
很多人不知道,Graylog 本身就有記錄每個 index 的時間範圍(index range),存在 MongoDB 裡,搜尋時就是靠它決定要查哪些 index。所以如果只是要知道「某個 index set 最早的資料是什麼時候」,根本不用查 OpenSearch:
curl -s -u "your-token:token" -H "Accept: application/json" \
"https://graylog.example.com/api/system/indices/ranges"
傳回每個 index 的 index_name、begin、end、calculated_at。依 index set 的前綴(例如 graylog_)篩選,取最小的 begin 就是最早的時間。
查單一 index:
curl -s -u "your-token:token" -H "Accept: application/json" \
"https://graylog.example.com/api/system/indices/ranges/graylog_120"
注意:index range 是在 index 輪替(rotate)時計算的,目前正在寫入的 index 範圍會持續變動;如果懷疑資料不準,可以在「System → Indices」對該 index set 重新計算,或呼叫 POST /api/system/indices/ranges/rebuild。另外它只能精確到 index,無法依 stream 區分。
直接查 OpenSearch
要精確查某個 index pattern 的最早一筆,直接打 OpenSearch 最划算。重點是不要拿 _source,改用 docvalue_fields,連 _source 解壓縮都省掉:
curl -s -u "admin:your-password" \
"https://opensearch.example.com:9200/graylog_*/_search?filter_path=hits.hits.fields.timestamp" \
-H "Content-Type: application/json" \
-d '{
"size": 1,
"sort": [{"timestamp": "asc"}],
"_source": false,
"docvalue_fields": ["timestamp"],
"track_total_hits": false
}'
幾個壓縮重點:
_source: false+docvalue_fields
直接從 doc values 讀時間欄位,不用讀取與解壓縮整筆訊息filter_path
把回應裁到只剩需要的欄位,整個回應可以壓到 100 bytes 以內track_total_hits: false
不計算符合的總筆數,大 index 上可以省下不少時間
傳回結果長這樣:
{"hits":{"hits":[{"fields":{"timestamp":["2024-03-15 08:23:11.000"]}}]}}
也可以用 min 聚合,語意更直接:
curl -s -u "admin:your-password" \
"https://opensearch.example.com:9200/graylog_*/_search?filter_path=aggregations.earliest.value_as_string" \
-H "Content-Type: application/json" \
-d '{"size": 0, "aggs": {"earliest": {"min": {"field": "timestamp"}}}}'
注意:OpenSearch 帳密請勿直接寫在指令或腳本裡,批次腳本可以改用 .netrc 或環境變數;憑證是自簽的話,測試時才用 -k,正式環境請指定 CA(--cacert)。另外直接查 OpenSearch 會繞過 Graylog 的權限控管,帳號要限縮在唯讀。
Views 匯出 API
如果要依 stream 查,或是不想把 OpenSearch 帳號交給程式,就走 Graylog 的 API。/api/views/search/messages 是 Graylog 匯出訊息的端點,可以用 fields_in_order 指定只傳回 timestamp:
curl -s -u "your-token:token" \
-H "X-Requested-By: cli" \
-H "Content-Type: application/json" \
-H "Accept: text/csv" \
-X POST "https://graylog.example.com/api/views/search/messages" \
-d '{
"streams": ["STREAM_ID"],
"query_string": {"type": "elasticsearch", "query_string": "*"},
"timerange": {"type": "relative", "range": 0},
"fields_in_order": ["timestamp"],
"limit": 1
}'
傳回的就是 CSV,沒有任何包裝,大約 50 bytes:
"timestamp"
"2024-03-15T08:23:11.000Z"
幾個細節:
- 不需要也不能指定排序
我看了 Graylog 原始碼,這個端點的請求格式沒有sort欄位,匯出時一律依timestamp由舊到新排序(加上 tiebreaker 欄位),所以limit: 1拿到的就是最早一筆 range: 0代表全部時間fields_in_order只放timestamp
預設會帶timestamp、source、message,不指定的話傳回量會大很多X-Requested-By標頭
Graylog 的 POST 請求一定要帶,內容隨意,沒帶會被拒絕
Universal Search(Legacy)
/api/search/universal/relative 是舊版的搜尋 API,在 Graylog 的 API 文件裡已經歸類在 Legacy,新寫的程式不建議再用,但舊腳本可能還在跑,還是說明一下:
curl -s -u "your-token:token" -H "Accept: application/json" \
"https://graylog.example.com/api/search/universal/relative" -G \
--data-urlencode "query=*" \
--data-urlencode "range=0" \
--data-urlencode "limit=1" \
--data-urlencode "sort=timestamp:asc" \
--data-urlencode "fields=timestamp" \
--data-urlencode "streams=STREAM_ID"
注意:這裡一定要用 JSON(Accept: application/json)。這個 API 的 CSV 版本沒有 sort 參數,加了也不會生效;而 JSON 版本沒指定排序時預設是 timestamp 由新到舊,忘了加 sort=timestamp:asc 會拿到最新一筆而不是最早一筆。
結論
查最早時間這種指標型查詢,我的選擇順序是:
- 只需要精確到 index:Index Ranges API,完全不碰 OpenSearch
- 要精確、要批次掃描:直接查 OpenSearch,搭配
docvalue_fields與filter_path - 要依 stream、要走 Graylog 權限:Views 匯出 API,記得用 CSV
jt-glogarch 歸檔掃描就是用這幾種方式組合,掃完全部 index set 的時間比原本快很多。
參考資料
- Graylog 文件:Graylog REST API
https://go2docs.graylog.org/current/setting_up_graylog/rest_api.html - OpenSearch 文件:Search API
https://docs.opensearch.org/latest/api-reference/search-apis/search/ - OpenSearch 文件:Retrieve specific fields
https://docs.opensearch.org/latest/search-plugins/searching-data/retrieve-specific-fields/ - OpenSearch 文件:Common REST parameters(filter_path)
https://docs.opensearch.org/latest/api-reference/common-parameters/ - jt-glogarch:Graylog Open 記錄歸檔與還原工具
https://github.com/jasoncheng7115/jt-glogarch



