/
logs.go
222 lines (194 loc) · 6.41 KB
/
logs.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
package logging
//
//Copyright 2018 Telenor Digital AS
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//
import (
"fmt"
"log"
"log/syslog"
"os"
"sync/atomic"
)
// Debug is the log file for debug logging. It is disabled by default. These
// messages aren't really useful for anything except the developers.
var debug = log.New(os.Stderr, " ", stderrFlags)
// Info is the log for info-level logs. It is disabled by default. These messages
// are typically "application created", "user registered", "device deleted" and
// so on. They are useful when doing detailed monitoring of the service
var info = log.New(os.Stderr, " ", stderrFlags)
// Warning is the log for warning-level logs, typically inconsistencies that
// users might notice. There are *some* messages in this but not a lot.
var warning = log.New(os.Stderr, " ", stderrFlags)
// Error is the log for severe error logs; database errors, data inconsistencies,
// failures and issues that require immediate action. There are very few issues
// on this scale.
var errlog = log.New(os.Stderr, " ", stderrFlags)
// LogLevel is the log detail level
const (
// DebugLevel is the most detailed logging level. It will emit all log levels.
DebugLevel uint = iota
// InfoLevel is the log level that will log info, warning and errors
InfoLevel
// WarningLevel is the log level that will log warnings and errors
WarningLevel
// ErrorLevel is the log level that only logs errors
ErrorLevel
)
var currentLevel uint32
const syslogFlags = log.Lshortfile
const stderrFlags = log.Ldate + log.Ltime + log.Lshortfile
func init() {
EnableStderr(true)
SetLogLevel(WarningLevel)
}
// SetLogLevel sets the logging level
func SetLogLevel(level uint) {
atomic.StoreUint32(¤tLevel, uint32(level))
// currentLevel = level
}
func setFlags(flags int) {
log.SetFlags(flags)
debug.SetFlags(flags)
info.SetFlags(flags)
warning.SetFlags(flags)
errlog.SetFlags(flags)
}
// EnableNamedSyslog enables sending logs to syslog with the given name.
func EnableNamedSyslog(name string) {
errorLog, err := syslog.New(syslog.LOG_ERR|syslog.LOG_DAEMON, name)
if err != nil {
errlog.Printf("Unable to set up error syslog: %v", err)
return
}
warningLog, err := syslog.New(syslog.LOG_WARNING|syslog.LOG_DAEMON, name)
if err != nil {
errlog.Printf("Unable to set up warning syslog: %v", err)
return
}
infoLog, err := syslog.New(syslog.LOG_INFO|syslog.LOG_DAEMON, name)
if err != nil {
errlog.Printf("Unable to set up info syslog: %v", err)
return
}
debugLog, err := syslog.New(syslog.LOG_DEBUG|syslog.LOG_DAEMON, name)
if err != nil {
errlog.Printf("Unable to set up debug syslog: %v", err)
return
}
log.SetOutput(debugLog)
debug.SetOutput(debugLog)
info.SetOutput(infoLog)
warning.SetOutput(warningLog)
errlog.SetOutput(errorLog)
// Syslog includes time stamp so we just need the source file
setFlags(syslogFlags)
// Set text prefixes since that makes it easier to search the syslog
log.SetPrefix("")
debug.SetPrefix("")
info.SetPrefix("")
warning.SetPrefix("")
errlog.SetPrefix("")
SetLogLevel(uint(currentLevel))
}
// EnableSyslog enables syslog logging with the name "congress"
func EnableSyslog() {
EnableNamedSyslog("congress")
}
// EnableMemoryLogger turns on logging to a memory logger.
func EnableMemoryLogger(logs []*MemoryLogger) {
if len(logs) < int(ErrorLevel) {
fmt.Fprintf(os.Stderr, "Expected %d logs for memory log, got %d", ErrorLevel, len(logs))
return
}
log.SetOutput(logs[DebugLevel])
debug.SetOutput(logs[DebugLevel])
info.SetOutput(logs[InfoLevel])
warning.SetOutput(logs[WarningLevel])
errlog.SetOutput(logs[ErrorLevel])
setFlags(MemoryLoggerFlags)
log.SetPrefix("")
debug.SetPrefix("")
info.SetPrefix("")
warning.SetPrefix("")
errlog.SetPrefix("")
}
// ANSI escape codes for colored log lines. This will only show up on the stderr
// logs. Printf statements on stdout will be broken but we don't do printf's do
// we?
const (
debugText = "\x1b[0m" // White
infoText = "\x1b[34;1m" // Bright blue
warningText = "\x1b[33;1m" // Bright yellow
errorText = "\x1b[31;1m" // Bright red
resetText = "\x1b[0m" // Reset
)
// EnableStderr enables logging to stderr
func EnableStderr(plainText bool) {
logwriter := os.Stderr
log.SetOutput(logwriter)
debug.SetOutput(logwriter)
info.SetOutput(logwriter)
warning.SetOutput(logwriter)
errlog.SetOutput(logwriter)
setFlags(stderrFlags)
if plainText {
// Use plain text logging
log.SetPrefix("LOG ")
debug.SetPrefix("DEBUG ")
info.SetPrefix("INFO ")
warning.SetPrefix("WARNING ")
errlog.SetPrefix("ERROR ")
} else {
// Use fancy emojis as prefix since this is something we'll look a *lot* at.
log.SetPrefix(debugText + "💡 ")
debug.SetPrefix(debugText + " ")
info.SetPrefix(infoText + "ℹ️ ")
warning.SetPrefix(warningText + "⚠️ ")
errlog.SetPrefix(errorText + "🛑 ")
}
SetLogLevel(uint(currentLevel))
}
// Debug adds a debug-level log message to the log. If the log level is set
// higher than DebugLevel the message will be discarded.
func Debug(format string, v ...interface{}) {
level := atomic.LoadUint32(¤tLevel)
if uint(level) == DebugLevel {
debug.Output(2, fmt.Sprintf(format, v...))
}
}
// Info adds an info-level log message to the log if the log level is set
// to InfoLevel or lower.
func Info(format string, v ...interface{}) {
level := atomic.LoadUint32(¤tLevel)
if uint(level) <= InfoLevel {
info.Output(2, fmt.Sprintf(format, v...))
}
}
// Warning adds a warning-level log message if the log level is set to
// WarningLevel or lower.
func Warning(format string, v ...interface{}) {
level := atomic.LoadUint32(¤tLevel)
if uint(level) <= WarningLevel {
warning.Output(2, fmt.Sprintf(format, v...))
}
}
// Error adds an error-level log message to the log.
func Error(format string, v ...interface{}) {
errlog.Output(2, fmt.Sprintf(format, v...))
}
// ResetColors prints the ANSI color reset code
func ResetColors() {
fmt.Print(resetText)
}