1package log 2 3import ( 4 "io" 5 "log" 6 "regexp" 7 "strings" 8) 9 10// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's 11// designed to be passed to a Go kit logger as the writer, for cases where 12// it's necessary to redirect all Go kit log output to the stdlib logger. 13// 14// If you have any choice in the matter, you shouldn't use this. Prefer to 15// redirect the stdlib log to the Go kit logger via NewStdlibAdapter. 16type StdlibWriter struct{} 17 18// Write implements io.Writer. 19func (w StdlibWriter) Write(p []byte) (int, error) { 20 log.Print(strings.TrimSpace(string(p))) 21 return len(p), nil 22} 23 24// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib 25// logger's SetOutput. It will extract date/timestamps, filenames, and 26// messages, and place them under relevant keys. 27type StdlibAdapter struct { 28 Logger 29 timestampKey string 30 fileKey string 31 messageKey string 32} 33 34// StdlibAdapterOption sets a parameter for the StdlibAdapter. 35type StdlibAdapterOption func(*StdlibAdapter) 36 37// TimestampKey sets the key for the timestamp field. By default, it's "ts". 38func TimestampKey(key string) StdlibAdapterOption { 39 return func(a *StdlibAdapter) { a.timestampKey = key } 40} 41 42// FileKey sets the key for the file and line field. By default, it's "caller". 43func FileKey(key string) StdlibAdapterOption { 44 return func(a *StdlibAdapter) { a.fileKey = key } 45} 46 47// MessageKey sets the key for the actual log message. By default, it's "msg". 48func MessageKey(key string) StdlibAdapterOption { 49 return func(a *StdlibAdapter) { a.messageKey = key } 50} 51 52// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed 53// logger. It's designed to be passed to log.SetOutput. 54func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { 55 a := StdlibAdapter{ 56 Logger: logger, 57 timestampKey: "ts", 58 fileKey: "caller", 59 messageKey: "msg", 60 } 61 for _, option := range options { 62 option(&a) 63 } 64 return a 65} 66 67func (a StdlibAdapter) Write(p []byte) (int, error) { 68 result := subexps(p) 69 keyvals := []interface{}{} 70 var timestamp string 71 if date, ok := result["date"]; ok && date != "" { 72 timestamp = date 73 } 74 if time, ok := result["time"]; ok && time != "" { 75 if timestamp != "" { 76 timestamp += " " 77 } 78 timestamp += time 79 } 80 if timestamp != "" { 81 keyvals = append(keyvals, a.timestampKey, timestamp) 82 } 83 if file, ok := result["file"]; ok && file != "" { 84 keyvals = append(keyvals, a.fileKey, file) 85 } 86 if msg, ok := result["msg"]; ok { 87 keyvals = append(keyvals, a.messageKey, msg) 88 } 89 if err := a.Logger.Log(keyvals...); err != nil { 90 return 0, err 91 } 92 return len(p), nil 93} 94 95const ( 96 logRegexpDate = `(?P<date>[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` 97 logRegexpTime = `(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?[ ]?` 98 logRegexpFile = `(?P<file>.+?:[0-9]+)?` 99 logRegexpMsg = `(: )?(?P<msg>.*)` 100) 101 102var ( 103 logRegexp = regexp.MustCompile(logRegexpDate + logRegexpTime + logRegexpFile + logRegexpMsg) 104) 105 106func subexps(line []byte) map[string]string { 107 m := logRegexp.FindSubmatch(line) 108 if len(m) < len(logRegexp.SubexpNames()) { 109 return map[string]string{} 110 } 111 result := map[string]string{} 112 for i, name := range logRegexp.SubexpNames() { 113 result[name] = string(m[i]) 114 } 115 return result 116} 117