BioErrorLog Tech Blog

試行錯誤の記録

TerraformでDEBUGレベルのログを出力する

Terraformで出力ログレベルを変更するやり方の備忘録です。

はじめに

こんにちは、@bioerrorlogです。

不具合の切り分けをする際、ログは重要な情報源です。

そして十分な情報がログから得られない場合は、ログの出力レベルを上げてログ出力量を増やすのも有効です。

今回は、Terraformでログの出力レベルを変更する方法を備忘にまとめつつ、ついでにソースコードにも目を通してみます。

TerraformでDEBUGレベルのログを出力する

Terraformでログの出力レベルを変更するには、環境変数TF_LOGを設定します。

ドキュメントはこちら: Debugging | Terraform by HashiCorp

例えばterraform planでDEBUGレベルのログを出力するには、環境変数TF_LOGDEBUGに設定します。

# コマンド一回限りでTF_LOGを設定する場合
TF_LOG=DEBUG terraform plan

# TF_LOGを設定した上でコマンド実行する場合
export TF_LOG=DEBUG
terraform plan

TF_LOGに設定できるログレベルは下記の5つです。

  • TRACE
  • DEBUG
  • INFO
  • WARN
  • ERROR

必要な情報量に応じて使い分けましょう。

TerraformとProviderで個別にログの出力レベルを設定する

TerraformそのものとProviderで別々にログレベルを設定することもできます。

Terraformそのもののログレベルを設定するには環境変数TF_LOG_COREを、

Providerのログレベルを設定するには環境変数TF_LOG_PROVIDERを利用します。

# TerraformそのもののログレベルをDEBUGに設定してplan実行
TF_LOG_CORE=DEBUG terraform plan

# ProviderのログレベルをDEBUGに設定してplan実行
TF_LOG_PROVIDER=DEBUG terraform plan

TF_LOG_CORE TF_LOG_PROVIDERともに、設定できるレベルはTF_LOGと同じです:

  • TRACE
  • DEBUG
  • INFO
  • WARN
  • ERROR

おまけ: Terraformのソースコードを読む

せっかくなので、Terraformのログレベルを設定している部分のソースコードにも目を通してみます。

github.com

// newHCLogger returns a new hclog.Logger instance with the given name
func newHCLogger(name string) hclog.Logger {
    logOutput := io.Writer(os.Stderr)
    logLevel, json := globalLogLevel()

    if logPath := os.Getenv(envLogFile); logPath != "" {
        f, err := os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
        if err != nil {
            fmt.Fprintf(os.Stderr, "Error opening log file: %v\n", err)
        } else {
            logOutput = f
        }
    }

    return hclog.NewInterceptLogger(&hclog.LoggerOptions{
        Name:              name,
        Level:             logLevel,
        Output:            logOutput,
        IndependentLevels: true,
        JSONFormat:        json,
    })
}

// NewLogger returns a new logger based in the current global logger, with the
// given name appended.
func NewLogger(name string) hclog.Logger {
    if name == "" {
        panic("logger name required")
    }
    return &logPanicWrapper{
        Logger: logger.Named(name),
    }
}

// NewProviderLogger returns a logger for the provider plugin, possibly with a
// different log level from the global logger.
func NewProviderLogger(prefix string) hclog.Logger {
    l := &logPanicWrapper{
        Logger: logger.Named(prefix + "provider"),
    }

    level := providerLogLevel()
    logger.Debug("created provider logger", "level", level)

    l.SetLevel(level)
    return l
}

// CurrentLogLevel returns the current log level string based the environment vars
func CurrentLogLevel() string {
    ll, _ := globalLogLevel()
    return strings.ToUpper(ll.String())
}

func providerLogLevel() hclog.Level {
    providerEnvLevel := strings.ToUpper(os.Getenv(envLogProvider))
    if providerEnvLevel == "" {
        providerEnvLevel = strings.ToUpper(os.Getenv(envLog))
    }

    return parseLogLevel(providerEnvLevel)
}

func globalLogLevel() (hclog.Level, bool) {
    var json bool
    envLevel := strings.ToUpper(os.Getenv(envLog))
    if envLevel == "" {
        envLevel = strings.ToUpper(os.Getenv(envLogCore))
    }
    if envLevel == "JSON" {
        json = true
    }
    return parseLogLevel(envLevel), json
}

GlobalなログレベルはglobalLogLevelで取得してnewHCLogger時に設定、ProviderのログレベルはproviderLogLevelで取得してNewProviderLoggerで設定されているのが分かります。

ログレベルとして受け取り可能な値はここで指定されています:

   // ValidLevels are the log level names that Terraform recognizes.
    ValidLevels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"}

terraform/logging.go at c047958b5708e4f500fe61000a662c169d048fff · hashicorp/terraform · GitHub

ちなみにログレベルの値の解釈時にはstrings.ToUpperで包まれているので、大文字/小文字は影響ないようです。

おわりに

以上、Terraformで出力ログレベルを変更するやり方をまとめました。

ちょっと覚えておくだけでも対応スピードが上がるので、こういった引き出しも増やしていきたいものです。

[関連記事]

www.bioerrorlog.work

www.bioerrorlog.work

www.bioerrorlog.work

参考

Debugging | Terraform by HashiCorp

terraform/logging.go at 271352620b43872aa99f7837f9c1c243b417a45c · hashicorp/terraform · GitHub