github.com/fasmat/buffalo@v0.11.0/logger_formatter.go (about) 1 package buffalo 2 3 // I really don't want to have this, but until (if) https://github.com/sirupsen/logrus/pull/606 is merged we're stuck with all this code. And yes, this is ALL needed just to remove some blank space in the logs 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 "os" 10 "regexp" 11 "sort" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/sirupsen/logrus" 17 "golang.org/x/crypto/ssh/terminal" 18 ) 19 20 const ( 21 nocolor = 0 22 red = 31 23 green = 32 24 yellow = 33 25 blue = 36 26 gray = 37 27 ) 28 29 // textFormatter formats logs into text 30 type textFormatter struct { 31 ForceColors bool 32 isTerminal bool 33 sync.Once 34 } 35 36 func (f *textFormatter) init(entry *logrus.Entry) { 37 if entry.Logger != nil { 38 f.isTerminal = f.checkIfTerminal(entry.Logger.Out) 39 } 40 } 41 42 func (f *textFormatter) checkIfTerminal(w io.Writer) bool { 43 switch v := w.(type) { 44 case *os.File: 45 return terminal.IsTerminal(int(v.Fd())) 46 default: 47 return false 48 } 49 } 50 51 const defaultTimestampFormat = time.RFC3339 52 53 // Format renders a single log entry 54 func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) { 55 var b *bytes.Buffer 56 keys := make([]string, 0, len(entry.Data)) 57 for k := range entry.Data { 58 keys = append(keys, k) 59 } 60 61 sort.Strings(keys) 62 if entry.Buffer != nil { 63 b = entry.Buffer 64 } else { 65 b = &bytes.Buffer{} 66 } 67 68 prefixFieldClashes(entry.Data) 69 70 f.Do(func() { f.init(entry) }) 71 72 isColored := (f.ForceColors || f.isTerminal) 73 74 if isColored { 75 f.printColored(b, entry, keys) 76 } else { 77 f.appendKeyValue(b, "level", entry.Level.String()) 78 if entry.Message != "" { 79 f.appendKeyValue(b, "msg", entry.Message) 80 } 81 for _, key := range keys { 82 f.appendKeyValue(b, key, entry.Data[key]) 83 } 84 } 85 86 b.WriteByte('\n') 87 return b.Bytes(), nil 88 } 89 90 func (f *textFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string) { 91 var levelColor int 92 switch entry.Level { 93 case logrus.DebugLevel: 94 levelColor = gray 95 case logrus.WarnLevel: 96 levelColor = yellow 97 case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: 98 levelColor = red 99 default: 100 levelColor = blue 101 } 102 103 levelText := strings.ToUpper(entry.Level.String())[0:4] 104 105 fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]", levelColor, levelText, entry.Time.Format(defaultTimestampFormat)) 106 107 if entry.Message != "" { 108 fmt.Fprintf(b, " %s", entry.Message) 109 } 110 111 for _, k := range keys { 112 v := entry.Data[k] 113 fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) 114 f.appendValue(b, v) 115 } 116 } 117 118 func (f *textFormatter) needsQuoting(text string) bool { 119 if len(text) == 0 { 120 return true 121 } 122 matched, err := regexp.MatchString("[^a-zA-Z\\-\\._\\/@\\^\\+]", text) 123 if err != nil { 124 return false 125 } 126 return matched 127 } 128 129 func (f *textFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { 130 if b.Len() > 0 { 131 b.WriteByte(' ') 132 } 133 b.WriteString(key) 134 b.WriteByte('=') 135 f.appendValue(b, value) 136 } 137 138 func (f *textFormatter) appendValue(b *bytes.Buffer, value interface{}) { 139 stringVal, ok := value.(string) 140 if !ok { 141 stringVal = fmt.Sprint(value) 142 } 143 144 if !f.needsQuoting(stringVal) { 145 b.WriteString(stringVal) 146 } else { 147 b.WriteString(fmt.Sprintf("%q", stringVal)) 148 } 149 } 150 151 func prefixFieldClashes(data logrus.Fields) { 152 if t, ok := data["time"]; ok { 153 data["fields.time"] = t 154 } 155 156 if m, ok := data["msg"]; ok { 157 data["fields.msg"] = m 158 } 159 160 if l, ok := data["level"]; ok { 161 data["fields.level"] = l 162 } 163 }