-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlogger.go
executable file
·331 lines (303 loc) · 8 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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package logger
import (
"encoding/json"
"fmt"
"os"
"sync"
"time"
)
const (
DebugLevel uint8 = 1
InfoLevel uint8 = 1 << 1
WarnLevel uint8 = 1 << 2
ErrorLevel uint8 = 1 << 3
FatalLevel uint8 = 1 << 4
AllLevels uint8 = byte(DebugLevel | InfoLevel | WarnLevel | ErrorLevel | FatalLevel)
)
var (
flush = &sync.WaitGroup{}
exitchn = make(chan bool, 1)
)
type Fields map[string]string
type Writer interface {
Write(e Entry)
Init() error
}
type Logger struct {
writersMap *map[int][]*WrappedWriter
mu sync.Mutex
modeSafe bool
wait sync.WaitGroup
fields Fields
fieldsString string
}
type WrappedWriter struct {
lock *sync.Mutex
chn chan Entry
writer Writer
wait sync.WaitGroup
}
type Entry struct {
Content string
Timestamp int64
Milliseconds int64
TimestampMilliseconds int64
Level uint8
LevelString string
Fields map[string]string
FieldsString string
}
type MiniLogger interface {
Debug(v ...interface{}) MiniLogger
Info(v ...interface{}) MiniLogger
Warn(v ...interface{}) MiniLogger
Error(v ...interface{}) MiniLogger
Fatal(v ...interface{})
Debugf(format string, v ...interface{}) MiniLogger
Infof(format string, v ...interface{}) MiniLogger
Warnf(format string, v ...interface{}) MiniLogger
Errorf(format string, v ...interface{}) MiniLogger
Fatalf(format string, v ...interface{})
Debugln(v ...interface{}) MiniLogger
Infoln(v ...interface{}) MiniLogger
Warnln(v ...interface{}) MiniLogger
Errorln(v ...interface{}) MiniLogger
Fatalln(v ...interface{})
AddWriter(w Writer, levels uint8) MiniLogger
Safe() MiniLogger
Unsafe() MiniLogger
With(fields Fields) MiniLogger
}
func getLevelString(level uint8) string {
switch level {
case DebugLevel:
return "DEBUG"
case InfoLevel:
return "INFO"
case WarnLevel:
return "WARN"
case ErrorLevel:
return "ERROR"
case FatalLevel:
return "FATAL"
}
return "UNKOWN"
}
// New returns a new logger
//modeSafe when false : must call logger.Flush() in main defer
//if not do that, may be lost message,but this mode has a highest performance
//when true : each message can be processed , but this mode may be has a little lower performance
//beacuse of logger must wait for all writers process done with each messsage.
//note: if you do logging before call os.Exit(), you had better to set safeMode to true before call os.Exit().
//fields same as With()'s args,more about fields to see With().
func New(modeSafe bool, fields Fields) MiniLogger {
if fields == nil {
fields = Fields{}
}
return &Logger{
fields: fields,
fieldsString: "",
mu: sync.Mutex{},
modeSafe: modeSafe,
wait: sync.WaitGroup{},
writersMap: &map[int][]*WrappedWriter{
int(DebugLevel): []*WrappedWriter{},
int(InfoLevel): []*WrappedWriter{},
int(WarnLevel): []*WrappedWriter{},
int(ErrorLevel): []*WrappedWriter{},
int(FatalLevel): []*WrappedWriter{},
},
}
}
//Flush wait for process all left entry
func Flush() {
exitchn <- true
flush.Wait()
}
//With return a new MiniLogger,which it's modeSafe inited same as caller MiniLogger,
//and revecived levels same as caller MiniLogger,
//and set it's fields.
//you can use it as a standard MiniLogger.
func (l *Logger) With(fields Fields) MiniLogger {
for k, v := range l.fields {
fields[k] = v
}
s, _ := json.Marshal(fields)
return &Logger{
mu: sync.Mutex{},
modeSafe: l.modeSafe,
wait: sync.WaitGroup{},
writersMap: l.writersMap,
fields: fields,
fieldsString: string(s),
}
}
//Unsafe : if you call Unsafe() , then you must call logger.Flush() in main defer
//if not do that, may be lost message,but this mode has a highest performance
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
//you call call func Safe() or Unsafe() in any where any time to switch safe mode.
func (l *Logger) Unsafe() MiniLogger {
l.modeSafe = false
return l
}
//Safe : if you call Safe(), each message can be processed immediately,
//but this mode may be has lower performance,beacuse of logger must wait for
//all writers process done with each messsage.
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
//you call func Safe() or Unsafe() in any where any time to switch safe mode.
func (l *Logger) Safe() MiniLogger {
l.modeSafe = true
return l
}
func (l *Logger) AddWriter(writer Writer, levels byte) MiniLogger {
w := &WrappedWriter{
lock: &sync.Mutex{},
writer: writer,
chn: make(chan Entry, 1024),
}
m := *l.writersMap
if DebugLevel&levels == DebugLevel {
m[int(DebugLevel)] = append(m[int(DebugLevel)], w)
}
if InfoLevel&levels == InfoLevel {
m[int(InfoLevel)] = append(m[int(InfoLevel)], w)
}
if WarnLevel&levels == WarnLevel {
m[int(WarnLevel)] = append(m[int(WarnLevel)], w)
}
if ErrorLevel&levels == ErrorLevel {
m[int(ErrorLevel)] = append(m[int(ErrorLevel)], w)
}
if FatalLevel&levels == FatalLevel {
m[int(FatalLevel)] = append(m[int(FatalLevel)], w)
}
flush.Add(1)
go func() {
defer func() {
_ = recover()
flush.Done()
}()
if err := w.writer.Init(); err != nil {
fmt.Printf("init writer fail,%s", err)
return
}
for {
select {
case entry, ok := <-w.chn:
if ok {
w.writer.Write(entry)
if l.modeSafe {
l.wait.Done()
}
if entry.Level == FatalLevel {
os.Exit(0)
}
} else {
return
}
case exit := <-exitchn:
if exit {
return
}
}
}
}()
return l
}
func (l *Logger) callWriter(level byte, t, foramt string, v ...interface{}) {
if level == FatalLevel {
l.Safe()
}
c := ""
if t == "f" && len(v) == 0 {
v = append(v, foramt)
foramt = "%s"
}
switch t {
case "ln":
c = fmt.Sprintln(v...)
case "f":
c = fmt.Sprintf(foramt, v...)
default:
c = fmt.Sprint(v...)
}
m := *l.writersMap
for _, w := range m[int(level)] {
now := time.Now().UnixNano()
nowUnix := time.Now().Unix()
mili := (now / 1000000) - nowUnix*1000
w.chn <- Entry{
Timestamp: nowUnix,
TimestampMilliseconds: now / 10000000,
Milliseconds: mili,
Content: c,
Level: level,
LevelString: getLevelString(level),
Fields: l.fields,
FieldsString: l.fieldsString,
}
if l.modeSafe {
l.wait.Add(1)
}
}
if l.modeSafe {
l.wait.Wait()
}
}
func (l *Logger) Fatal(v ...interface{}) {
l.callWriter(FatalLevel, "", "", v...)
}
func (l *Logger) Error(v ...interface{}) MiniLogger {
l.callWriter(ErrorLevel, "", "", v...)
return l
}
func (l *Logger) Warn(v ...interface{}) MiniLogger {
l.callWriter(WarnLevel, "", "", v...)
return l
}
func (l *Logger) Info(v ...interface{}) MiniLogger {
l.callWriter(InfoLevel, "", "", v...)
return l
}
func (l *Logger) Debug(v ...interface{}) MiniLogger {
l.callWriter(DebugLevel, "", "", v...)
return l
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.callWriter(FatalLevel, "f", format, v...)
}
func (l *Logger) Errorf(format string, v ...interface{}) MiniLogger {
l.callWriter(ErrorLevel, "f", format, v...)
return l
}
func (l *Logger) Warnf(format string, v ...interface{}) MiniLogger {
l.callWriter(WarnLevel, "f", format, v...)
return l
}
func (l *Logger) Infof(format string, v ...interface{}) MiniLogger {
l.callWriter(InfoLevel, "f", format, v...)
return l
}
func (l *Logger) Debugf(format string, v ...interface{}) MiniLogger {
l.callWriter(DebugLevel, "f", format, v...)
return l
}
func (l *Logger) Fatalln(v ...interface{}) {
l.callWriter(FatalLevel, "ln", "", v...)
}
func (l *Logger) Errorln(v ...interface{}) MiniLogger {
l.callWriter(ErrorLevel, "ln", "", v...)
return l
}
func (l *Logger) Warnln(v ...interface{}) MiniLogger {
l.callWriter(WarnLevel, "ln", "", v...)
return l
}
func (l *Logger) Infoln(v ...interface{}) MiniLogger {
l.callWriter(InfoLevel, "ln", "", v...)
return l
}
func (l *Logger) Debugln(v ...interface{}) MiniLogger {
l.callWriter(DebugLevel, "ln", "", v...)
return l
}