github.com/zak-blake/goa@v1.4.1/logging.go (about) 1 package goa 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 8 "context" 9 ) 10 11 // ErrMissingLogValue is the value used to log keys with missing values 12 const ErrMissingLogValue = "MISSING" 13 14 type ( 15 // LogAdapter is the logger interface used by goa to log informational and error messages. 16 // Adapters to different logging backends are provided in the logging sub-packages. 17 // goa takes care of initializing the logging context with the service, controller and 18 // action names. 19 LogAdapter interface { 20 // Info logs an informational message. 21 Info(msg string, keyvals ...interface{}) 22 // Error logs an error. 23 Error(msg string, keyvals ...interface{}) 24 // New appends to the logger context and returns the updated logger logger. 25 New(keyvals ...interface{}) LogAdapter 26 } 27 28 // adapter is the stdlib logger adapter. 29 adapter struct { 30 *log.Logger 31 keyvals []interface{} 32 } 33 ) 34 35 // NewLogger returns a goa log adpater backed by a log logger. 36 func NewLogger(logger *log.Logger) LogAdapter { 37 return &adapter{Logger: logger} 38 } 39 40 // Logger returns the logger stored in the context if any, nil otherwise. 41 func Logger(ctx context.Context) *log.Logger { 42 logger := ContextLogger(ctx) 43 if a, ok := logger.(*adapter); ok { 44 return a.Logger 45 } 46 return nil 47 } 48 49 func (a *adapter) Info(msg string, keyvals ...interface{}) { 50 a.logit(msg, keyvals, false) 51 } 52 53 func (a *adapter) Error(msg string, keyvals ...interface{}) { 54 a.logit(msg, keyvals, true) 55 } 56 57 func (a *adapter) New(keyvals ...interface{}) LogAdapter { 58 if len(keyvals) == 0 { 59 return a 60 } 61 kvs := append(a.keyvals, keyvals...) 62 if len(kvs)%2 != 0 { 63 kvs = append(kvs, ErrMissingLogValue) 64 } 65 return &adapter{ 66 Logger: a.Logger, 67 // Limiting the capacity of the stored keyvals ensures that a new 68 // backing array is created if the slice must grow. 69 keyvals: kvs[:len(kvs):len(kvs)], 70 } 71 } 72 73 func (a *adapter) logit(msg string, keyvals []interface{}, iserror bool) { 74 n := (len(keyvals) + 1) / 2 75 if len(keyvals)%2 != 0 { 76 keyvals = append(keyvals, ErrMissingLogValue) 77 } 78 m := (len(a.keyvals) + 1) / 2 79 n += m 80 var fm bytes.Buffer 81 lvl := "INFO" 82 if iserror { 83 lvl = "EROR" 84 } 85 fm.WriteString(fmt.Sprintf("[%s] %s", lvl, msg)) 86 vals := make([]interface{}, n) 87 offset := len(a.keyvals) 88 for i := 0; i < offset; i += 2 { 89 k := a.keyvals[i] 90 v := a.keyvals[i+1] 91 vals[i/2] = v 92 fm.WriteString(fmt.Sprintf(" %s=%%+v", k)) 93 } 94 for i := 0; i < len(keyvals); i += 2 { 95 k := keyvals[i] 96 v := keyvals[i+1] 97 vals[i/2+offset/2] = v 98 fm.WriteString(fmt.Sprintf(" %s=%%+v", k)) 99 } 100 a.Logger.Printf(fm.String(), vals...) 101 } 102 103 // LogInfo extracts the logger from the given context and calls Info on it. 104 // This is intended for code that needs portable logging such as the internal code of goa and 105 // middleware. User code should use the log adapters instead. 106 func LogInfo(ctx context.Context, msg string, keyvals ...interface{}) { 107 if l := ctx.Value(logKey); l != nil { 108 if logger, ok := l.(LogAdapter); ok { 109 logger.Info(msg, keyvals...) 110 } 111 } 112 } 113 114 // LogError extracts the logger from the given context and calls Error on it. 115 // This is intended for code that needs portable logging such as the internal code of goa and 116 // middleware. User code should use the log adapters instead. 117 func LogError(ctx context.Context, msg string, keyvals ...interface{}) { 118 if l := ctx.Value(logKey); l != nil { 119 if logger, ok := l.(LogAdapter); ok { 120 logger.Error(msg, keyvals...) 121 } 122 } 123 }