github.com/go-kit/log@v0.2.1/stdlib.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "io" 6 "log" 7 "regexp" 8 "strings" 9 ) 10 11 // StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's 12 // designed to be passed to a Go kit logger as the writer, for cases where 13 // it's necessary to redirect all Go kit log output to the stdlib logger. 14 // 15 // If you have any choice in the matter, you shouldn't use this. Prefer to 16 // redirect the stdlib log to the Go kit logger via NewStdlibAdapter. 17 type StdlibWriter struct{} 18 19 // Write implements io.Writer. 20 func (w StdlibWriter) Write(p []byte) (int, error) { 21 log.Print(strings.TrimSpace(string(p))) 22 return len(p), nil 23 } 24 25 // StdlibAdapter wraps a Logger and allows it to be passed to the stdlib 26 // logger's SetOutput. It will extract date/timestamps, filenames, and 27 // messages, and place them under relevant keys. 28 type StdlibAdapter struct { 29 Logger 30 timestampKey string 31 fileKey string 32 messageKey string 33 prefix string 34 joinPrefixToMsg bool 35 } 36 37 // StdlibAdapterOption sets a parameter for the StdlibAdapter. 38 type StdlibAdapterOption func(*StdlibAdapter) 39 40 // TimestampKey sets the key for the timestamp field. By default, it's "ts". 41 func TimestampKey(key string) StdlibAdapterOption { 42 return func(a *StdlibAdapter) { a.timestampKey = key } 43 } 44 45 // FileKey sets the key for the file and line field. By default, it's "caller". 46 func FileKey(key string) StdlibAdapterOption { 47 return func(a *StdlibAdapter) { a.fileKey = key } 48 } 49 50 // MessageKey sets the key for the actual log message. By default, it's "msg". 51 func MessageKey(key string) StdlibAdapterOption { 52 return func(a *StdlibAdapter) { a.messageKey = key } 53 } 54 55 // Prefix configures the adapter to parse a prefix from stdlib log events. If 56 // you provide a non-empty prefix to the stdlib logger, then your should provide 57 // that same prefix to the adapter via this option. 58 // 59 // By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to 60 // true if you want to include the parsed prefix in the msg. 61 func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption { 62 return func(a *StdlibAdapter) { a.prefix = prefix; a.joinPrefixToMsg = joinPrefixToMsg } 63 } 64 65 // NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed 66 // logger. It's designed to be passed to log.SetOutput. 67 func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { 68 a := StdlibAdapter{ 69 Logger: logger, 70 timestampKey: "ts", 71 fileKey: "caller", 72 messageKey: "msg", 73 } 74 for _, option := range options { 75 option(&a) 76 } 77 return a 78 } 79 80 func (a StdlibAdapter) Write(p []byte) (int, error) { 81 p = a.handlePrefix(p) 82 83 result := subexps(p) 84 keyvals := []interface{}{} 85 var timestamp string 86 if date, ok := result["date"]; ok && date != "" { 87 timestamp = date 88 } 89 if time, ok := result["time"]; ok && time != "" { 90 if timestamp != "" { 91 timestamp += " " 92 } 93 timestamp += time 94 } 95 if timestamp != "" { 96 keyvals = append(keyvals, a.timestampKey, timestamp) 97 } 98 if file, ok := result["file"]; ok && file != "" { 99 keyvals = append(keyvals, a.fileKey, file) 100 } 101 if msg, ok := result["msg"]; ok { 102 msg = a.handleMessagePrefix(msg) 103 keyvals = append(keyvals, a.messageKey, msg) 104 } 105 if err := a.Logger.Log(keyvals...); err != nil { 106 return 0, err 107 } 108 return len(p), nil 109 } 110 111 func (a StdlibAdapter) handlePrefix(p []byte) []byte { 112 if a.prefix != "" { 113 p = bytes.TrimPrefix(p, []byte(a.prefix)) 114 } 115 return p 116 } 117 118 func (a StdlibAdapter) handleMessagePrefix(msg string) string { 119 if a.prefix == "" { 120 return msg 121 } 122 123 msg = strings.TrimPrefix(msg, a.prefix) 124 if a.joinPrefixToMsg { 125 msg = a.prefix + msg 126 } 127 return msg 128 } 129 130 const ( 131 logRegexpDate = `(?P<date>[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` 132 logRegexpTime = `(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?[ ]?` 133 logRegexpFile = `(?P<file>.+?:[0-9]+)?` 134 logRegexpMsg = `(: )?(?P<msg>(?s:.*))` 135 ) 136 137 var ( 138 logRegexp = regexp.MustCompile(logRegexpDate + logRegexpTime + logRegexpFile + logRegexpMsg) 139 ) 140 141 func subexps(line []byte) map[string]string { 142 m := logRegexp.FindSubmatch(line) 143 if len(m) < len(logRegexp.SubexpNames()) { 144 return map[string]string{} 145 } 146 result := map[string]string{} 147 for i, name := range logRegexp.SubexpNames() { 148 result[name] = strings.TrimRight(string(m[i]), "\n") 149 } 150 return result 151 }