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