-
Notifications
You must be signed in to change notification settings - Fork 232
/
logging.go
140 lines (118 loc) · 3.63 KB
/
logging.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package logging
import (
"fmt"
"io"
"log"
"os"
"strings"
"syscall"
"github.com/hashicorp/logutils"
testing "github.com/mitchellh/go-testing-interface"
)
// These are the environmental variables that determine if we log, and if
// we log whether or not the log should go to a file.
const (
EnvLog = "TF_LOG" // See ValidLevels
EnvLogFile = "TF_LOG_PATH" // Set to a file
EnvAccLogFile = "TF_ACC_LOG_PATH" // Set to a file
// EnvLogPathMask splits test log files by name.
EnvLogPathMask = "TF_LOG_PATH_MASK"
)
var ValidLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"}
// LogOutput determines where we should send logs (if anywhere) and the log
// level. This only effects this log.Print* functions called in the provider
// under test. Dependency providers for the provider under test will have their
// logging controlled by Terraform itself and managed with the TF_ACC_LOG_PATH
// environment variable. Calls to tflog.* will have their output managed by the
// tfsdklog sink.
func LogOutput(t testing.T) (logOutput io.Writer, err error) {
logOutput = io.Discard
logLevel := LogLevel()
if logLevel == "" {
if os.Getenv(EnvAccLogFile) != "" {
// plugintest defaults to TRACE when TF_ACC_LOG_PATH is
// set for Terraform and dependency providers of the
// provider under test. We should do the same for the
// provider under test.
logLevel = "TRACE"
} else {
return
}
}
logOutput = os.Stderr
if logPath := os.Getenv(EnvLogFile); logPath != "" {
var err error
logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
if err != nil {
return nil, err
}
}
if logPath := os.Getenv(EnvAccLogFile); logPath != "" {
var err error
logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
if err != nil {
return nil, err
}
}
if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" {
// Escape special characters which may appear if we have subtests
testName := strings.Replace(t.Name(), "/", "__", -1)
logPath := fmt.Sprintf(logPathMask, testName)
var err error
logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
if err != nil {
return nil, err
}
}
// This was the default since the beginning
logOutput = &logutils.LevelFilter{
Levels: ValidLevels,
MinLevel: logutils.LogLevel(logLevel),
Writer: logOutput,
}
return
}
// SetOutput checks for a log destination with LogOutput, and calls
// log.SetOutput with the result. If LogOutput returns nil, SetOutput uses
// io.Discard. Any error from LogOutput is fatal.
func SetOutput(t testing.T) {
out, err := LogOutput(t)
if err != nil {
log.Fatal(err)
}
if out == nil {
out = io.Discard
}
log.SetOutput(out)
}
// LogLevel returns the current log level string based the environment vars
func LogLevel() string {
envLevel := os.Getenv(EnvLog)
if envLevel == "" {
return ""
}
logLevel := "TRACE"
if isValidLogLevel(envLevel) {
// allow following for better ux: info, Info or INFO
logLevel = strings.ToUpper(envLevel)
} else {
log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v",
envLevel, ValidLevels)
}
return logLevel
}
// IsDebugOrHigher returns whether or not the current log level is debug or trace
func IsDebugOrHigher() bool {
level := LogLevel()
return level == "DEBUG" || level == "TRACE"
}
func isValidLogLevel(level string) bool {
for _, l := range ValidLevels {
if strings.ToUpper(level) == string(l) {
return true
}
}
return false
}