配置文件本身非常脆弱!所以修改配置文件自然會引入部署失敗的風險。如果能夠對配置文件進行自動化測試將會極大的降低這種風險。本文將介紹一個可以自動化測試 logstash 配置文件的工具,讓大家可以像寫單元測試用例一樣為 logstash 配置文件創建測試 case,並以快速迭代的方式變更 logstash 配置文件。
噩夢
在很多真實環境中,變更 logstash 的配置文件幾乎是個噩夢。日志不是結構化的格式(甚至是亂七八糟!),導致我們幾乎必須一個一個地處理日志,並對解析什么和不解析什么做出邏輯判定。這會導致我們在 logstash 的配置文件中寫出復雜的日志處理邏輯。更加悲催的是,我們編寫的僅僅是一個配置文件,大多數時候你找不到像 VisualStudio 那樣豪華的帶有拼寫檢查和語法檢查的編輯工具。所以我們迫切的需要一個可以對 logstash 的配置文件做單元測試的工具,讓我們更好的控制變更、執行迭代。
logstash-test-runner
logstash-test-runner 就是這樣一個可以讓我們應用 DevOps 原則來解決這個問題的工具。它具有下面幾個特點:
- 它是一個測試框架
- 非常容易寫測試用例
- 不用學習一個新的 DSL(Domain-Specific-Language)
- 能夠快速的獲得反饋
logstash-test-runner 的作者應該是飽受 logstash 的配置文件蹂躪之后創建的這個工具,所以這是一個寫給自己用的工具,在使用的時候你能夠切身體會到它的便利!
前提條件
運行 logstash-test-runner 依賴於下面三個條件:
- Docker
- NodeJS > v8
- Bash > v4
其中 logstash 的測試用例是在 docker 容器中運行的,默認使用的鏡像中 logstash 的版本為 5.5.1,我們可以通過第二個命令行參數指定其它的版本。
NodeJS 程序用來對比實際運行的輸出結果與期望的輸出結果。
logstash-test-runner 其實就是一個 Bash 腳本,把上面的工具組織起來完成任務。
在運行 logstash-test-runner 前請確保你的環境滿足上述條件。然后從這里克隆 logstash-test-runner。如果你確定你要運行的 logstash 的版本,最好是提前先把對應的容器鏡像拉下來,比如筆者最常用的是 logstash 6.2.4:
$ docker pull docker.elastic.co/logstash/logstash:6.2.4
運行下內置的 demo
准備就緒,讓我們先運行下 logstash-test-runner 內置的 demo 體驗一下。進入到 logstash-test-runner 目錄下,執行下面的命令:
$ npm install $ ./test.sh __tests__
test.sh 腳本的第一個參數是保存測試用例的根目錄,第二個參數是 logstash 的 docker 鏡像。也可以不指定 logstash 鏡像,默認使用的鏡像中的 logstash 版本為 5.5.1。該命令執行的結果如下:
__tests__ 目錄的結構如下:
每個子目錄代表一個測試用例,demo 中有三個測試用例,分別是 clustering、crawlers 和 mongo。每個測試用例下有三個文件,分別是 input.log、logstash.conf 和 output.log,他們的名稱是固定的。input.log 模擬日志文件,保存的是日志記錄;logstash.conf 則是待測的 logstash 配置文件;output.log 中是需要我們寫入的期望結果(這就是 logstash 版的單元測試呀!)。
添加自己的測試用例
下面我們添加自己的測試用例來測試 multiline 功能(把應用程序中的異常堆棧合並到一條日志記錄中)。先創建目錄 __nick__/mlines,然后在 __nick__/mlines 目錄中創建文件 input.log、logstash.conf 和 output.log。input.log 文件的內容如下:
2019-1
hello
world
2019-2
aa
2019-3
bb
2019-4
模擬的日志記錄以日期開頭,非數字開頭的記錄則是異常堆棧。
logstash.conf 文件的內容如下:
input { stdin { codec => multiline { pattern => "^\d" negate => "true" what => "previous" } } } filter { mutate { replace => { "host" => "testing_host" } } }
filter 中設置 host 主要目的是讓測試用例忽略真正的 host 值(在容器中運行這個值是隨機的字符串,無法執行比較操作,所以總是讓他輸出 testing_host)。
output.log 文件的內容如下:
{"tags":["multiline"],"host":"testing_host","message":"2019-1\nhello\nworld","@version":"1","@timestamp":"2019-03-05T09:30:26.820Z"} {"tags":["multiline"],"host":"testing_host","message":"2019-2\naa","@version":"1","@timestamp":"2019-03-05T09:30:26.823Z"} {"tags":["multiline"],"host":"testing_host","message":"2019-3\n bb","@version":"1","@timestamp":"2019-03-05T09:30:26.824Z"}
這是我們期望 logstash 輸出的 JSON 格式的日志記錄。當然,在默認情況下 logstash-test-runner 會忽略 @timestamp。
接下來執行下面的命令運行測試:
$ ./test.sh __nick__ docker.elastic.co/logstash/logstash:6.2.4
該命令中筆者指定了 logstash 鏡像,所以測試用例都是由 logstash 6.2.4 執行的,結果如下:
上圖顯示測試用例都通過了。如果有測試用例失敗,就會輸出詳細的失敗信息,非常有助於排除問題。
難點在於寫 output.log
logstash-test-runner 非常簡單易用,其實在整個使用過程中,筆者覺得寫 output.log 是最困難的。因為你很難根據日志記錄和 logstash 的配置文件直接推導出 logstash 輸出的記錄應該是什么樣子的。logstash-test-runner 直接把 logstash 輸出的日志記錄輸出到了 console 中:
這樣我們就可以輕松的根據 console 中的輸出來調整 output.log 文件了!
總結
使用者為自己編寫的工具往往是直擊痛點,logstash-test-runner 就是這樣的工具。它讓我們可以快速迭代 logstash 的配置變更。我們還可以自動化的執行 logstash-test-runner,作為 CI/CD 的一部分,從而確保每個配置變更都通過了測試。
對 logstash 之類的基礎設施工具進行測試符合 DevOps(構建→測試→發布→運行)的實踐。我想測試基礎設施配置的實踐會越來越普遍,並且在提高基礎設施可靠性和交付方面體現出巨大的價值。