2015-12-08 01:10:30 +01:00
package logging
2014-05-31 01:07:26 +02:00
import (
"io"
2015-12-08 01:10:30 +01:00
"io/ioutil"
2015-10-01 21:45:12 +02:00
"log"
2014-05-31 01:07:26 +02:00
"os"
2015-10-01 21:45:12 +02:00
"strings"
2016-10-18 22:08:23 +02:00
"syscall"
2014-05-31 01:07:26 +02:00
)
// These are the environmental variables that determine if we log, and if
// we log whether or not the log should go to a file.
2015-10-01 21:45:12 +02:00
const (
EnvLog = "TF_LOG" // Set to True
EnvLogFile = "TF_LOG_PATH" // Set to a file
)
2014-05-31 01:07:26 +02:00
2019-12-05 20:13:37 +01:00
// ValidLevels are the log level names that Terraform recognizes.
var ValidLevels = [ ] LogLevel { "TRACE" , "DEBUG" , "INFO" , "WARN" , "ERROR" }
2015-10-01 21:45:12 +02:00
2015-12-08 01:10:30 +01:00
// LogOutput determines where we should send logs (if anywhere) and the log level.
func LogOutput ( ) ( logOutput io . Writer , err error ) {
logOutput = ioutil . Discard
2016-03-14 17:17:05 +01:00
2019-12-05 20:13:37 +01:00
logLevel := CurrentLogLevel ( )
2016-03-14 17:17:05 +01:00
if logLevel == "" {
2015-10-01 21:45:12 +02:00
return
}
logOutput = os . Stderr
if logPath := os . Getenv ( EnvLogFile ) ; logPath != "" {
var err error
2016-10-18 22:08:23 +02:00
logOutput , err = os . OpenFile ( logPath , syscall . O_CREAT | syscall . O_RDWR | syscall . O_APPEND , 0666 )
2015-10-01 21:45:12 +02:00
if err != nil {
return nil , err
2014-05-31 01:07:26 +02:00
}
}
2019-12-05 21:19:22 +01:00
if logLevel == "TRACE" {
// Just pass through logs directly then, without any level filtering at all.
return logOutput , nil
}
// Otherwise we'll use our level filter, which is a heuristic-based
// best effort thing that is not totally reliable but helps to reduce
// the volume of logs in some cases.
2019-12-05 20:13:37 +01:00
logOutput = & LevelFilter {
2017-10-14 15:35:36 +02:00
Levels : ValidLevels ,
2019-12-05 20:13:37 +01:00
MinLevel : LogLevel ( logLevel ) ,
2016-03-14 17:17:05 +01:00
Writer : logOutput ,
}
2019-12-05 21:19:22 +01:00
return logOutput , nil
2016-03-14 17:17:05 +01:00
}
2015-10-01 21:45:12 +02:00
2016-08-01 23:16:22 +02:00
// SetOutput checks for a log destination with LogOutput, and calls
// log.SetOutput with the result. If LogOutput returns nil, SetOutput uses
// ioutil.Discard. Any error from LogOutout is fatal.
func SetOutput ( ) {
out , err := LogOutput ( )
if err != nil {
log . Fatal ( err )
}
if out == nil {
out = ioutil . Discard
}
log . SetOutput ( out )
}
2019-12-05 20:13:37 +01:00
// CurrentLogLevel returns the current log level string based the environment vars
func CurrentLogLevel ( ) string {
2016-03-14 17:17:05 +01:00
envLevel := os . Getenv ( EnvLog )
if envLevel == "" {
return ""
}
logLevel := "TRACE"
2015-10-01 21:45:12 +02:00
if isValidLogLevel ( envLevel ) {
// allow following for better ux: info, Info or INFO
2016-03-14 17:17:05 +01:00
logLevel = strings . ToUpper ( envLevel )
2015-10-01 21:45:12 +02:00
} else {
log . Printf ( "[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v" ,
2017-10-14 15:35:36 +02:00
envLevel , ValidLevels )
2015-10-01 21:45:12 +02:00
}
2019-12-05 20:49:11 +01:00
if logLevel != "TRACE" {
log . Printf ( "[WARN] Log levels other than TRACE are currently unreliable, and are supported only for backward compatibility.\n Use TF_LOG=TRACE to see Terraform's internal logs.\n ----" )
}
2015-10-01 21:45:12 +02:00
2016-03-14 17:17:05 +01:00
return logLevel
}
2015-10-01 21:45:12 +02:00
2016-03-14 17:17:05 +01:00
// IsDebugOrHigher returns whether or not the current log level is debug or trace
func IsDebugOrHigher ( ) bool {
2019-12-05 20:13:37 +01:00
level := string ( CurrentLogLevel ( ) )
2016-03-14 17:17:05 +01:00
return level == "DEBUG" || level == "TRACE"
2014-05-31 01:07:26 +02:00
}
2015-10-01 21:45:12 +02:00
func isValidLogLevel ( level string ) bool {
2017-10-14 15:35:36 +02:00
for _ , l := range ValidLevels {
2015-10-01 21:45:12 +02:00
if strings . ToUpper ( level ) == string ( l ) {
return true
}
}
return false
}