-
Notifications
You must be signed in to change notification settings - Fork 42
/
log.go
248 lines (198 loc) · 5.9 KB
/
log.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright © 2020 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package log
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"github.com/clearlinux/clr-installer/conf"
"github.com/clearlinux/clr-installer/errors"
"github.com/clearlinux/clr-installer/utils"
)
const (
// LogLevelError specified the log level as: ERROR
LogLevelError = 1
// LogLevelWarning specified the log level as: WARNING
LogLevelWarning = 2
// LogLevelInfo specified the log level as: INFO
LogLevelInfo = 3
// LogLevelDebug specified the log level as: DEBUG
LogLevelDebug = 4
// LogLevelVerbose specified the log level as: VERBOSE
// This is the same as Debug, but without the repeat filtering
LogLevelVerbose = 5
// configFilePreInstalPrefix is the prefix to create a configuration// file name
configFilePreInstalPrefix = "pre-install-"
)
var (
level = LogLevelInfo
levelMap = map[int]string{}
filehandle *os.File
logFileName string
preConfName string
lineLast string
lineCount int
)
func init() {
levelMap[LogLevelError] = "LogLevelError"
levelMap[LogLevelWarning] = "LogLevelWarning"
levelMap[LogLevelInfo] = "LogLevelInfo"
levelMap[LogLevelDebug] = "LogLevelDebug"
levelMap[LogLevelVerbose] = "LogLevelVerbose"
}
// SetLogLevel sets the default log level to l
func SetLogLevel(l int) {
if l < LogLevelError {
level = LogLevelError
logTag("WRN", "Log Level '%d' too low, forcing to %s (%d)", l, levelMap[level], level)
} else if l > LogLevelVerbose {
level = LogLevelVerbose
logTag("WRN", "Log Level '%d' too high, forcing to %s (%d)", l, levelMap[level], level)
} else {
level = l
Debug("Log Level set to %s (%d)", levelMap[level], l)
}
}
// SetOutputFilename ... sets the default log output to filename instead of stdout/stderr
func SetOutputFilename(logFile string) (*os.File, error) {
logFileName = logFile
var err error
filehandle, err = os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return nil, err
}
log.SetOutput(filehandle)
preConfName = filepath.Join(filepath.Dir(logFileName), configFilePreInstalPrefix+conf.ConfigFile)
return filehandle, nil
}
// GetCrashInfoMsg returns the crash info message.
func GetCrashInfoMsg() string {
msg := utils.Locale.Get("Please report this crash using %s", "GitHub Issues:")
msg += "\n" + "https://github.com/clearlinux/clr-installer/issues"
msg += "\n\n" + utils.Locale.Get("Include the following as attachments to enable diagnosis:")
msg += "\n" + preConfName
msg += "\n" + logFileName
msg += "\n\n" + utils.Locale.Get("You may need to remove any personal data of concern from the attachments.")
msg += "\n" + utils.Locale.Get("The Installer will now exit.")
return msg
}
// RequestCrashInfo prints information for the user on how to properly report the
// crash of the installer and how to gather more information
func RequestCrashInfo() {
fmt.Println(GetCrashInfoMsg())
}
// GetLogFileName ... returns the filename of the current log
func GetLogFileName() string {
return logFileName
}
// GetPreConfFile ... returns the filename of where to store the pre-configuration file
func GetPreConfFile() string {
return preConfName
}
// ArchiveLogFile copies the contents of the log to the given filename
func ArchiveLogFile(archiveFile string) error {
if filehandle == nil {
return errors.Errorf("Log output should be set, see log.SetOutputFilename()")
}
a, err := os.OpenFile(archiveFile, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}
defer func() {
_ = a.Close()
// Jump back to the end of the log file
_, _ = filehandle.Seek(0, 2)
}()
_ = filehandle.Sync()
// Jump to the beginning of the file
_, err = filehandle.Seek(0, 0)
if err != nil {
Error("Failed to seek log file (%v)", err)
}
var bytesCopied int64
bytesCopied, err = io.Copy(a, filehandle)
if err != nil {
Error("Failed to archive log file (%v) %q", err, archiveFile)
}
Debug("Archived %d bytes to file %q", bytesCopied, archiveFile)
_ = a.Sync()
return err
}
// LevelStr converts level to its text equivalent, if level is invalid
// an error is returned
func LevelStr(level int) (string, error) {
for k, v := range levelMap {
if k == level {
return v, nil
}
}
return "", fmt.Errorf("Invalid log level: %d", level)
}
func logTag(tag string, format string, a ...interface{}) {
// If there are no variable to pass to the format,
// then we can escape any % signs.
if len(a) < 1 {
format = strings.ReplaceAll(format, "%", "%%")
}
f := "[" + tag + "] " + format + "\n"
output := fmt.Sprintf(f, a...)
if level >= LogLevelVerbose {
log.Print(output)
return
}
if output != lineLast {
// output the previous repeated line
if lineCount > 0 {
plural := ""
if lineCount > 1 {
plural = "s"
}
repeat := fmt.Sprintf("[%s] [Previous line repeated %d time%s]\n", tag, lineCount, plural)
log.Print(repeat)
}
log.Print(output)
lineLast = output
lineCount = 0
} else { // Repeated line
lineCount++
}
}
// Debug prints a debug log entry with DBG tag
func Debug(format string, a ...interface{}) {
if level < LogLevelDebug {
return
}
logTag("DBG", format, a...)
}
// Error prints an error log entry with ERR tag
func Error(format string, a ...interface{}) {
logTag("ERR", format, a...)
}
// ErrorError prints an error log entry with ERR tag, it takes an
// error instead of format and args, if a TraceableError is provided
// then we also include the trace information in the error message
func ErrorError(err error) {
msg := err.Error()
if e, ok := err.(errors.TraceableError); ok {
msg = fmt.Sprintf("%s %s", e.Trace, e.What)
}
logTag("ERR", msg)
}
// Info prints an info log entry with INF tag
func Info(format string, a ...interface{}) {
if level < LogLevelInfo {
return
}
logTag("INF", format, a...)
}
// Warning prints an warning log entry with WRN tag
func Warning(format string, a ...interface{}) {
if level < LogLevelWarning {
return
}
logTag("WRN", format, a...)
}