1 package log
2
3 import (
4 "bytes"
5 e "errors"
6 "fmt"
7 "strings"
8 "sync"
9
10 "text/template"
11
12 "github.com/go-errors/errors"
13 "golang.org/x/net/context"
14 )
15
16
17 type LogLevel int
18
19
20 const (
21
22 LevelDebug LogLevel = iota
23
24
25 LevelInfo LogLevel = iota
26
27
28 LevelWarning LogLevel = iota
29
30
31 LevelError LogLevel = iota
32 )
33
34
35
36
37 type LogLevelName string
38
39 const (
40 levelNameDebug LogLevelName = "debug"
41 levelNameInfo LogLevelName = "info"
42 levelNameWarning LogLevelName = "warning"
43 levelNameError LogLevelName = "error"
44 )
45
46
47 var (
48 levelNameMap = map[LogLevelName]LogLevel{
49 levelNameDebug: LevelDebug,
50 levelNameInfo: LevelInfo,
51 levelNameWarning: LevelWarning,
52 levelNameError: LevelError,
53 }
54
55 levelNameMapR = map[LogLevel]LogLevelName{
56 LevelDebug: levelNameDebug,
57 LevelInfo: levelNameInfo,
58 LevelWarning: levelNameWarning,
59 LevelError: levelNameError,
60 }
61 )
62
63
64 var (
65 includeFilters = make(map[string]bool)
66 useIncludeFilters = false
67 excludeFilters = make(map[string]bool)
68 useExcludeFilters = false
69
70 adapters = make(map[string]LogAdapter)
71
72
73 excludeBypassLevel LogLevel = -1
74 )
75
76
77 func AddIncludeFilter(noun string) {
78 includeFilters[noun] = true
79 useIncludeFilters = true
80 }
81
82
83 func RemoveIncludeFilter(noun string) {
84 delete(includeFilters, noun)
85 if len(includeFilters) == 0 {
86 useIncludeFilters = false
87 }
88 }
89
90
91 func AddExcludeFilter(noun string) {
92 excludeFilters[noun] = true
93 useExcludeFilters = true
94 }
95
96
97 func RemoveExcludeFilter(noun string) {
98 delete(excludeFilters, noun)
99 if len(excludeFilters) == 0 {
100 useExcludeFilters = false
101 }
102 }
103
104
105 func AddAdapter(name string, la LogAdapter) {
106 if _, found := adapters[name]; found == true {
107 Panic(e.New("adapter already registered"))
108 }
109
110 if la == nil {
111 Panic(e.New("adapter is nil"))
112 }
113
114 adapters[name] = la
115
116 if GetDefaultAdapterName() == "" {
117 SetDefaultAdapterName(name)
118 }
119 }
120
121
122 func ClearAdapters() {
123 adapters = make(map[string]LogAdapter)
124 SetDefaultAdapterName("")
125 }
126
127
128 type LogAdapter interface {
129
130 Debugf(lc *LogContext, message *string) error
131
132
133 Infof(lc *LogContext, message *string) error
134
135
136 Warningf(lc *LogContext, message *string) error
137
138
139 Errorf(lc *LogContext, message *string) error
140 }
141
142
143
144
145
146
147
148 type MessageContext struct {
149 Level *LogLevelName
150 Noun *string
151 Message *string
152 ExcludeBypass bool
153 }
154
155
156 type LogContext struct {
157 logger *Logger
158 ctx context.Context
159 }
160
161
162 type Logger struct {
163 isConfigured bool
164 an string
165 la LogAdapter
166 t *template.Template
167 systemLevel LogLevel
168 noun string
169 }
170
171
172
173 func NewLoggerWithAdapterName(noun string, adapterName string) (l *Logger) {
174 l = &Logger{
175 noun: noun,
176 an: adapterName,
177 }
178
179 return l
180 }
181
182
183 func NewLogger(noun string) (l *Logger) {
184 l = NewLoggerWithAdapterName(noun, "")
185
186 return l
187 }
188
189
190 func (l *Logger) Noun() string {
191 return l.noun
192 }
193
194
195 func (l *Logger) Adapter() LogAdapter {
196 return l.la
197 }
198
199 var (
200 configureMutex sync.Mutex
201 )
202
203 func (l *Logger) doConfigure(force bool) {
204 configureMutex.Lock()
205 defer configureMutex.Unlock()
206
207 if l.isConfigured == true && force == false {
208 return
209 }
210
211 if IsConfigurationLoaded() == false {
212 Panic(e.New("can not configure because configuration is not loaded"))
213 }
214
215 if l.an == "" {
216 l.an = GetDefaultAdapterName()
217 }
218
219
220
221
222 if l.an != "" {
223 la, found := adapters[l.an]
224 if found == false {
225 Panic(fmt.Errorf("adapter is not valid: %s", l.an))
226 }
227
228 l.la = la
229 }
230
231
232
233 systemLevel, found := levelNameMap[levelName]
234 if found == false {
235 Panic(fmt.Errorf("log-level not valid: [%s]", levelName))
236 }
237
238 l.systemLevel = systemLevel
239
240
241
242 if format == "" {
243 Panic(e.New("format is empty"))
244 }
245
246 if t, err := template.New("logItem").Parse(format); err != nil {
247 Panic(err)
248 } else {
249 l.t = t
250 }
251
252 l.isConfigured = true
253 }
254
255 func (l *Logger) flattenMessage(lc *MessageContext, format *string, args []interface{}) (string, error) {
256 m := fmt.Sprintf(*format, args...)
257
258 lc.Message = &m
259
260 var b bytes.Buffer
261 if err := l.t.Execute(&b, *lc); err != nil {
262 return "", err
263 }
264
265 return b.String(), nil
266 }
267
268 func (l *Logger) allowMessage(noun string, level LogLevel) bool {
269 if _, found := includeFilters[noun]; found == true {
270 return true
271 }
272
273
274
275 if useIncludeFilters == true {
276 return false
277 }
278
279 if _, found := excludeFilters[noun]; found == true {
280 return false
281 }
282
283 return true
284 }
285
286 func (l *Logger) makeLogContext(ctx context.Context) *LogContext {
287 return &LogContext{
288 ctx: ctx,
289 logger: l,
290 }
291 }
292
293 type logMethod func(lc *LogContext, message *string) error
294
295 func (l *Logger) log(ctx context.Context, level LogLevel, lm logMethod, format string, args []interface{}) error {
296 if l.systemLevel > level {
297 return nil
298 }
299
300
301
302
303
304
305 canExcludeBypass := level >= excludeBypassLevel && excludeBypassLevel != -1
306 didExcludeBypass := false
307
308 n := l.Noun()
309
310 if l.allowMessage(n, level) == false {
311 if canExcludeBypass == false {
312 return nil
313 }
314
315 didExcludeBypass = true
316 }
317
318 levelName, found := levelNameMapR[level]
319 if found == false {
320 Panicf("level not valid: (%d)", level)
321 }
322
323 levelName = LogLevelName(strings.ToUpper(string(levelName)))
324
325 mc := &MessageContext{
326 Level: &levelName,
327 Noun: &n,
328 ExcludeBypass: didExcludeBypass,
329 }
330
331 s, err := l.flattenMessage(mc, &format, args)
332 PanicIf(err)
333
334 lc := l.makeLogContext(ctx)
335
336 err = lm(lc, &s)
337 PanicIf(err)
338
339 if level == LevelError {
340 return e.New(s)
341 }
342
343 return nil
344 }
345
346 func (l *Logger) mergeStack(err interface{}, format string, args []interface{}) (string, []interface{}) {
347 if format != "" {
348 format += "\n%s"
349 } else {
350 format = "%s"
351 }
352
353 var stackified *errors.Error
354 stackified, ok := err.(*errors.Error)
355 if ok == false {
356 stackified = errors.Wrap(err, 2)
357 }
358
359 args = append(args, stackified.ErrorStack())
360
361 return format, args
362 }
363
364
365 func (l *Logger) Debugf(ctx context.Context, format string, args ...interface{}) {
366 l.doConfigure(false)
367
368 if l.la != nil {
369 l.log(ctx, LevelDebug, l.la.Debugf, format, args)
370 }
371 }
372
373
374 func (l *Logger) Infof(ctx context.Context, format string, args ...interface{}) {
375 l.doConfigure(false)
376
377 if l.la != nil {
378 l.log(ctx, LevelInfo, l.la.Infof, format, args)
379 }
380 }
381
382
383 func (l *Logger) Warningf(ctx context.Context, format string, args ...interface{}) {
384 l.doConfigure(false)
385
386 if l.la != nil {
387 l.log(ctx, LevelWarning, l.la.Warningf, format, args)
388 }
389 }
390
391
392 func (l *Logger) Errorf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
393 l.doConfigure(false)
394
395 var err interface{}
396
397 if errRaw != nil {
398 _, ok := errRaw.(*errors.Error)
399 if ok == true {
400 err = errRaw
401 } else {
402 err = errors.Wrap(errRaw, 1)
403 }
404 }
405
406 if l.la != nil {
407 if errRaw != nil {
408 format, args = l.mergeStack(err, format, args)
409 }
410
411 l.log(ctx, LevelError, l.la.Errorf, format, args)
412 }
413 }
414
415
416 func (l *Logger) ErrorIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
417 if errRaw == nil {
418 return
419 }
420
421 var err interface{}
422
423 _, ok := errRaw.(*errors.Error)
424 if ok == true {
425 err = errRaw
426 } else {
427 err = errors.Wrap(errRaw, 1)
428 }
429
430 l.Errorf(ctx, err, format, args...)
431 }
432
433
434 func (l *Logger) Panicf(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
435 l.doConfigure(false)
436
437 var wrapped interface{}
438
439 _, ok := errRaw.(*errors.Error)
440 if ok == true {
441 wrapped = errRaw
442 } else {
443 wrapped = errors.Wrap(errRaw, 1)
444 }
445
446 if l.la != nil {
447 format, args = l.mergeStack(wrapped, format, args)
448 wrapped = l.log(ctx, LevelError, l.la.Errorf, format, args)
449 }
450
451 Panic(wrapped)
452 }
453
454
455 func (l *Logger) PanicIff(ctx context.Context, errRaw interface{}, format string, args ...interface{}) {
456 if errRaw == nil {
457 return
458 }
459
460
461
462
463
464 var err interface{}
465
466 _, ok := errRaw.(*errors.Error)
467 if ok == true {
468 err = errRaw
469 } else {
470 err = errors.Wrap(errRaw, 1)
471 }
472
473 l.Panicf(ctx, err.(error), format, args...)
474 }
475
476
477 func Wrap(err interface{}) *errors.Error {
478 es, ok := err.(*errors.Error)
479 if ok == true {
480 return es
481 }
482
483 return errors.Wrap(err, 1)
484 }
485
486
487 func Errorf(message string, args ...interface{}) *errors.Error {
488 err := fmt.Errorf(message, args...)
489 return errors.Wrap(err, 1)
490 }
491
492
493 func Panic(err interface{}) {
494 _, ok := err.(*errors.Error)
495 if ok == true {
496 panic(err)
497 } else {
498 panic(errors.Wrap(err, 1))
499 }
500 }
501
502
503 func Panicf(message string, args ...interface{}) {
504 err := Errorf(message, args...)
505 Panic(err)
506 }
507
508
509 func PanicIf(err interface{}) {
510 if err == nil {
511 return
512 }
513
514 _, ok := err.(*errors.Error)
515 if ok == true {
516 panic(err)
517 } else {
518 panic(errors.Wrap(err, 1))
519 }
520 }
521
522
523
524
525 func Is(actual, against error) bool {
526
527 if _, ok := actual.(*errors.Error); ok == false {
528 return actual == against
529 }
530
531 return errors.Is(actual, against)
532 }
533
534
535
536 func PrintError(err error) {
537 wrapped := Wrap(err)
538 fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
539 }
540
541
542
543 func PrintErrorf(err error, format string, args ...interface{}) {
544 wrapped := Wrap(err)
545
546 fmt.Printf(format, args...)
547 fmt.Printf("\n")
548 fmt.Printf("Stack:\n\n%s\n", wrapped.ErrorStack())
549 }
550
551 func init() {
552 if format == "" {
553 format = defaultFormat
554 }
555
556 if levelName == "" {
557 levelName = defaultLevelName
558 }
559
560 if includeNouns != "" {
561 for _, noun := range strings.Split(includeNouns, ",") {
562 AddIncludeFilter(noun)
563 }
564 }
565
566 if excludeNouns != "" {
567 for _, noun := range strings.Split(excludeNouns, ",") {
568 AddExcludeFilter(noun)
569 }
570 }
571
572 if excludeBypassLevelName != "" {
573 excludeBypassLevelName = LogLevelName(strings.ToLower(string(excludeBypassLevelName)))
574
575 var found bool
576 if excludeBypassLevel, found = levelNameMap[excludeBypassLevelName]; found == false {
577 panic(e.New("exclude bypass-level is invalid"))
578 }
579 }
580 }
581
View as plain text