-
Notifications
You must be signed in to change notification settings - Fork 2
/
logger.go
250 lines (213 loc) · 7.57 KB
/
logger.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
249
250
// Package logger allows configurable, per-package logging.
package logger
import (
"bytes"
"fmt"
"sort"
)
// Copy of the last string displayed to gracefully handle repeated text.
// TODO: making this into a FIFO with a configurable size would be awesome.
// (I now regret moving fifo.go to the ppu package...)
var lastMessage string
// Count of how many times the last message was repeated.
var lastMessageCount uint
// LogLevel representing the priority of a log message. An attempt to log a
// message whose LogLevel is above logger.Level will be silently ignored.
type LogLevel uint8
// Supported log levels.
const (
Fatal LogLevel = iota
Warning
Info
Debug
Desperate // The kind of level used in a Pixel FIFO, for instance...
)
// Levels that can be specified on the command line.
var Levels = map[string]LogLevel{
"fatal": Fatal,
"warning": Warning,
"info": Info,
"debug": Debug,
"desperate": Desperate,
}
// Level is the global log level above which nothing will be displayed.
var Level = Info // Sensible default
// Enabled setting controls whether logging will occur for a given module name
// when the usual methods are called. Default is no logging. Enabling 'all'
// will turn on debug output for every module.
var Enabled = make(map[string]bool)
// Loggers is a registry of currently defined package-specific loggers.
var Loggers = make(map[string]*Logger)
// Context is a function returning a string that will be prepended to every
// log message, if defined. This can be used to insert timestamps, CPU info...
// The default does nothing and debug messages will be displayed as given.
var Context = func() string { return "" }
// Logger represents a name-based logger for "simple" debugging. Each package
// should register itself with logger, which will allow for a complete listing
// of all possible logger package/modules that can be used with the -debug
// flag.
type Logger struct {
Name string // Package or module name for this logger
Help string // Help text to be displayed when calling with -debug help
wildcard string // Cached '<pkg name>/*' for quick lookup.
modules map[string]*Logger // Submodules (nil if Logger is already one)
}
// New returns a Logger instance specific to the given package, after
// registering it with our base logger package so this logger and its modules
// can be listed from the command-line.
// Will panic if a logger with the same name is already defined.
func New(name, help string) *Logger {
if Loggers[name] != nil {
panic(fmt.Sprintf("logger '%s' already exists", name))
}
l := &Logger{name, help, fmt.Sprintf("%s/*", name), make(map[string]*Logger)}
// Record this so we have exactly one logger per package.
Loggers[name] = l
return l
}
// Add a sub-module to a package logger, which can then be enabled by using
// -debug <pkg>/<module> or -debug <pkg>/* on the command line.
func (l *Logger) Add(name, help string) {
modName := fmt.Sprintf("%s/%s", l.Name, name)
if l.modules[name] != nil {
panic(fmt.Sprintf("logger '%s' already exists", modName))
}
l.modules[name] = &Logger{modName, help, l.wildcard, nil}
}
// Sub returns a sub-module with the given name. If called with a module name
// that hasn't been registered with Add() first, returns the main logger.
// Otherwise, returns a Logger instance to allow chain call to print methods.
func (l *Logger) Sub(name string) *Logger {
if sub := l.modules[name]; sub != nil {
return sub
}
fmt.Printf(" !!! sub-logger %s/%s not found\n", l.Name, name)
return l
}
func (l *Logger) Enabled() bool {
return Enabled["all"] || Enabled[l.Name] || Enabled[l.wildcard]
}
// Output log message if the given package/subpackage is enabled and if the
// global log level permits it.
func (l *Logger) log(level LogLevel, format string, a ...interface{}) {
// "Do we need to log this?"
if level > Level {
return
}
if !l.Enabled() {
return
}
msg := fmt.Sprintf("%s: %s", l.Name, fmt.Sprintf(format, a...))
if msg == lastMessage {
lastMessageCount++
fmt.Printf(" ... repeated %d times\r", lastMessageCount)
} else {
if lastMessageCount > 1 {
fmt.Println()
}
lastMessage = msg
lastMessageCount = 1
fmt.Print(Context())
fmt.Println(msg)
}
}
// Fatal prints a message (then panics regardless of debug level).
func (l *Logger) Fatal(msg string) {
l.log(Fatal, "%s", msg)
panic(msg)
}
// Warning prints a message if the global log level is Warning or more.
func (l *Logger) Warning(msg string) {
l.log(Warning, "%s", msg)
}
// Info prints a message if the global log level is Info (the default) or more.
func (l *Logger) Info(msg string) {
l.log(Info, "%s", msg)
}
// Log is an alias for Info.
func (l *Logger) Log(msg string) {
l.log(Info, "%s", msg)
}
// Debug prints a message if the global log level is Debug or more.
func (l *Logger) Debug(msg string) {
l.log(Debug, "%s", msg)
}
// Desperate prints a message if the global log level is the maximum.
func (l *Logger) Desperate(msg string) {
l.log(Desperate, "%s", msg)
}
// Fatalf format-prints a message (then panics regardless of debug level).
func (l *Logger) Fatalf(format string, a ...interface{}) {
l.log(Fatal, format, a...)
panic(fmt.Sprintf(format, a...))
}
// Warningf format-prints a message if the global log level is Warning or more.
func (l *Logger) Warningf(format string, a ...interface{}) {
l.log(Warning, format, a...)
}
// Infof format-prints a message if the global log level is Info (the default) or more.
func (l *Logger) Infof(format string, a ...interface{}) {
l.log(Info, format, a...)
}
// Logf is an alias for Infof.
func (l *Logger) Logf(format string, a ...interface{}) {
l.log(Info, format, a...)
}
// Debugf format-prints a message if the global log level is Debug or more.
func (l *Logger) Debugf(format string, a ...interface{}) {
l.log(Debug, format, a...)
}
// Desperatef format-prints a message if the global log level is the maximum.
func (l *Logger) Desperatef(format string, a ...interface{}) {
l.log(Desperate, format, a...)
}
// String returns the description of a logger and all its sub-loggers if any.
func (l *Logger) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "%s: %s\n", l.Name, l.Help)
if l.modules != nil {
names := make([]string, 0, len(l.modules))
for n := range l.modules {
names = append(names, n)
}
sort.Strings(names)
for _, n := range names {
fmt.Fprint(&b, l.modules[n])
}
}
return b.String()
}
// Help prints all registered loggers and sub-loggers so the user knows what
// can be enabled.
func Help() {
// Sort loggers by package name.
names := make([]string, 0, len(Loggers))
for n := range Loggers {
names = append(names, n)
}
sort.Strings(names)
fmt.Println("The following debug modules can be specified with -debug.")
fmt.Println("To debug a full package and submodules, use -debug <mod>/*")
fmt.Println("(might have to be quoted depending on your shell).")
fmt.Println()
// Display all loggers and their submodules (if any, also sorted).
for _, n := range names {
fmt.Print(Loggers[n])
}
}
// HelpLevels lists the available log levels. Hardcoded in there because the
// order in which we put them in the Levels mapping is not guaranteed anyway.
// TODO: sort levels by enum value, auto-detect default based on Level value.
func HelpLevels() {
fmt.Println("The following debug levels can be specified with -level")
fmt.Println("(by decreasing order of importance):")
fmt.Println()
fmt.Println("fatal")
fmt.Println("warning")
fmt.Println("info (default)")
fmt.Println("debug")
fmt.Println("desperate")
fmt.Println()
fmt.Println("Debug modules must be enabled for anything to be displayed")
fmt.Println("(see -debug help).")
}