forked from fluxio/logging
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
220 lines (191 loc) · 6.52 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
// Package logging defines an injectable logging interface.
package logging
import (
"bytes"
"fmt"
"log"
"os"
"path"
"runtime"
"strings"
"time"
)
var os_Exit = os.Exit
func init() {
// Set the default log library to prefix output with the name of the current
// binary and output the file & line producing the log messages.
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
log.SetPrefix(fmt.Sprintf("» [%s] ", binaryName()))
}
func binaryName() string { return path.Base(os.Args[0]) }
// Logger defines a standard logging interface that may be backed by different
// implementations. For example, this can easily defer to pre-existing logging
// libraries such as seelog or the standard Go log lib. Using this shim allows
// us to inject special log handling in different parts of the application. For
// example, each block holds a Logger interface, but the actual log output can
// be recorded to disk indepdently or unified, individually streamed to the
// client, filtered, etc.
type Logger interface {
Trace(vals ...interface{})
Tracef(fmt string, args ...interface{})
Debug(vals ...interface{})
Debugf(fmt string, args ...interface{})
Info(vals ...interface{})
Infof(fmt string, args ...interface{})
Error(vals ...interface{})
Errorf(fmt string, args ...interface{})
LogLevel() Level
SetLogLevel(Level)
}
// System is a single global logger for convenience. By default, it prints
// to stderr and uses the current binary name as the context.
var System Logger = NewTextLogger(os.Stderr, binaryName(), TraceLevel)
// Convenience accessors to the system logger.
func Trace(vals ...interface{}) { System.Trace(vals...) }
func Debug(vals ...interface{}) { System.Debug(vals...) }
func Info(vals ...interface{}) { System.Info(vals...) }
func Error(vals ...interface{}) { System.Error(vals...) }
func Tracef(fmt string, args ...interface{}) { System.Tracef(fmt, args...) }
func Debugf(fmt string, args ...interface{}) { System.Debugf(fmt, args...) }
func Infof(fmt string, args ...interface{}) { System.Infof(fmt, args...) }
func Errorf(fmt string, args ...interface{}) { System.Errorf(fmt, args...) }
// Fatal is a package-level only log function that logs to Error and then exits
// the process.
func Fatal(vals ...interface{}) {
System.Error(vals...)
System.Error("Failed at:\n" + stack())
os_Exit(-1)
}
// Fatalf is a package-level only log function that logs to Errorf and then
// exits the process.
func Fatalf(fmt string, args ...interface{}) {
System.Errorf(fmt, args...)
System.Error("Failed at:\n" + stack())
os_Exit(-1)
}
func FatalOnErr(err error) {
if err == nil {
return
}
Fatal(err)
}
// Writer is the interface for types that can format and write log entries.
type Writer interface {
Write(e Entry) error
}
// Entry is a single log entry.
type Entry struct {
Level Level
Time time.Time
File string // empty string indicates unknown
Line int // -1 indicates unknown
Context string
Fmt string
Args []interface{}
}
// Level describes the log level.
type Level int32
const (
TraceLevel = Level(1)
DebugLevel = Level(2)
InfoLevel = Level(3)
ErrorLevel = Level(4)
)
func (l Level) String() string {
switch l {
case TraceLevel:
return "T"
case DebugLevel:
return "D"
case InfoLevel:
return "I"
case ErrorLevel:
return "E"
}
panic(fmt.Errorf("No such log level: %d", l))
}
// ParseLevelOrDie parses the string into a Level. If the string is invalid, it
// panics.
func ParseLevelOrDie(levelstr string) Level {
lvl, err := ParseLevel(levelstr)
if err != nil {
panic(err)
}
return lvl
}
// ParseLevel parses the string into a Level. If the string is invalid, it
// returns a nice error object.
func ParseLevel(levelstr string) (Level, error) {
switch strings.ToLower(levelstr) {
case "t", "trace":
return TraceLevel, nil
case "d", "debug":
return DebugLevel, nil
case "i", "info":
return InfoLevel, nil
case "e", "error":
return ErrorLevel, nil
}
return ErrorLevel, fmt.Errorf("Unknown level: %q", levelstr)
}
// Return the current stack as a string.
func stack() string {
var stack [64]uintptr
n := runtime.Callers(3, stack[:])
var buf bytes.Buffer
for _, pc := range stack[:n] {
f := runtime.FuncForPC(pc)
file, line := f.FileLine(pc)
fmt.Fprintf(&buf, " %-30s \t %s:%d\n", f.Name(), file, line)
}
return buf.String()
}
var trackTimeFormat string = "%s spent performing operation: "
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer TraceTrackTime(time.Now())
// Put at the top of a function for most clarity.
func TraceTrackTime(start time.Time) {
pc, _, _, _ := runtime.Caller(1)
caller := runtime.FuncForPC(pc).Name()
TraceTrackTimef(start, "%s", caller)
}
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer TraceTrackTimef(time.Now(), "Loading Project %d", pid)
// Put at the top of a function for most clarity.
func TraceTrackTimef(start time.Time, fmt string, args ...interface{}) {
elapsed := time.Since(start)
args = append([]interface{}{elapsed}, args...)
System.Tracef(trackTimeFormat+fmt, args...)
}
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer DebugTrackTime(time.Now())
// Put at the top of a function for most clarity.
func DebugTrackTime(start time.Time) {
pc, _, _, _ := runtime.Caller(1)
caller := runtime.FuncForPC(pc).Name()
DebugTrackTimef(start, "%s", caller)
}
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer DebugTrackTime(time.Now(), "Loading Project %d", pid)
// Put at the top of a function for most clarity.
func DebugTrackTimef(start time.Time, fmt string, args ...interface{}) {
elapsed := time.Since(start)
args = append([]interface{}{elapsed}, args...)
System.Debugf(trackTimeFormat+fmt, args...)
}
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer InfoTrackTime(time.Now())
// Put at the top of a function for most clarity.
func InfoTrackTime(start time.Time) {
pc, _, _, _ := runtime.Caller(1)
caller := runtime.FuncForPC(pc).Name()
InfoTrackTimef(start, "%s", caller)
}
// Logs the time since the starttime occured. This works best as a defer statement:
// e.g. defer InfoTrackTimef(time.Now(), "Loading Project %d", pid)
// Put at the top of a function for most clarity.
func InfoTrackTimef(start time.Time, fmt string, args ...interface{}) {
elapsed := time.Since(start)
args = append([]interface{}{elapsed}, args...)
System.Infof(trackTimeFormat+fmt, args...)
}