Hashicorp製OSSのロガーとしては hashicorp/logutilsが有名だが、
Terraformのコードリーディングをしていたらその内部ではhashicorp/go-hclogを利用していることに気が付いた。
どちらもGo言語標準のlogに準拠したロガーだが、どうしてロガーを変更したのか気になったのでいろいろ調べてみた。
hashicop/logutils
logutilsは非常にシンプルなロガー。
Go言語標準のlogと同一インタフェースを持ちつつ、ログメッセージ中にログレベルを埋め込むことでログレベルのフィルタリングが実現できる。
標準logと同一インタフェースを持つのでDebugメソッドやInfoメソッドなどは持たず、Printメソッドに渡すメッセージの先頭に[DEBUG]
や[INFO]
などの文字列を埋め込むことでログレベルを実現していることに結構驚く。
func main() {
filter := &logutils.LevelFilter{
Levels: []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
MinLevel: logutils.LogLevel("WARN"),
Writer: os.Stderr,
}
log.SetOutput(filter)
log.Print("[DEBUG] Debugging") // this will not print
log.Print("[WARN] Warning") // this will
}
ちなみに本体100行未満なので簡単に実装を確認できる。
hashicorp/go-hclog
hclogもシンプルなロガーではあるが、logutilsに比べより多くの機能を実装している。
logutilsと同じくPrintメソッドに渡すメッセージの冒頭にログレベル識別用の文字列を埋め込むことでログレベルを実現しつつ、オプションとして専用のInfoメソッドなども提供する。
それ以外にも、構造化ロギング、Key-Valueデータのロギング、など一般的なロギングに必要な機能が一通りそろっている。
func main() {
appLogger := hclog.New(&hclog.LoggerOptions{
Name: "my-app",
Level: hclog.LevelFromString("INFO"),
})
appLogger.Debug("this message will not print")
appLogger.Info("this message will print")
appLogger.Info("message with key-value", "key", 42)
}
// $ go run main.go
// 2023-02-26T22:19:29.622+0900 [INFO] my-app: this message will print
// 2023-02-26T22:19:29.622+0900 [INFO] my-app: message with key-value: key=42
ちばみに、terraformではInfoメソッドなどのログレベル別メソッドなどの機能は利用しておらず、logutilsと同じようにlog.Printf("[INFO] ...")
の形式でロギングしている。
参考: terraform-plugin-log (tflog)
terraformの実装にて利用しているロガーではないが、terraformがplugin向けに提供するロガーとしてterraform-plugin-log (tflog)がある。
tflogはhclogのラッパーで、plugin毎にhclogのログ出力設定を行わなくてもよく、terraform本体の設定を引き継いで出力してくれる。
おそらく、terraformがlogutilsではなくhclogに移行した理由はtflogを実現するためであり、terraform本体とプラグインでより統一的な仕組みが必要だと判断したからではないかと思う。
terraform pluginのロギングでは、出力するログレベルをterraform本体とプラグインで区別したり、特定プラグインのみログレベルを変更できたりする。例えばterraformはinfoレベルで出力し、terraform-provider-awsはdebugレベルで出力する、それ以外のpluginはログ出力しない、みたいなことが実現できる。
これを実現するためのフィルタリングをterraform本体で実装しようとするとかなり面倒そうで、そういった機能を提供するためにhclogおよびそのラッパーとしてのtflogを利用していると推測する。