package logger // 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 import ( "bytes" "fmt" "sort" "strings" "sync" "time" "github.com/sirupsen/logrus" ) const ( red = 31 yellow = 33 blue = 36 gray = 37 ) // textFormatter formats logs into text type textFormatter struct { ForceColors bool isTerminal bool sync.Once } func (f *textFormatter) init(entry *logrus.Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) } } const defaultTimestampFormat = time.RFC3339 // Format renders a single log entry func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) { prefixFieldClashes(entry.Data) keys := make([]string, 0, len(entry.Data)) for k := range entry.Data { keys = append(keys, k) } sort.Strings(keys) var b *bytes.Buffer if entry.Buffer != nil { b = entry.Buffer } else { b = &bytes.Buffer{} } f.Do(func() { f.init(entry) }) isColored := (f.ForceColors || f.isTerminal) if isColored { f.printColored(b, entry, keys) } else { f.appendKeyValue(b, "level", entry.Level.String()) f.appendKeyValue(b, "time", entry.Time.Format(defaultTimestampFormat)) if entry.Message != "" { f.appendKeyValue(b, "msg", entry.Message) } for _, key := range keys { f.appendKeyValue(b, key, entry.Data[key]) } } b.WriteByte('\n') return b.Bytes(), nil } func (f *textFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry, keys []string) { var levelColor int switch entry.Level { case logrus.DebugLevel: levelColor = gray case logrus.WarnLevel: levelColor = yellow case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: levelColor = red default: levelColor = blue } levelText := strings.ToUpper(entry.Level.String())[0:4] fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]", levelColor, levelText, entry.Time.Format(defaultTimestampFormat)) if entry.Message != "" { fmt.Fprintf(b, " %s", entry.Message) } for _, k := range keys { v := entry.Data[k] fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) f.appendValue(b, v) } } func (f *textFormatter) needsQuoting(text string) bool { if len(text) == 0 { return true } for _, ch := range text { if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { return true } } return false } func (f *textFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { if b.Len() > 0 { b.WriteByte(' ') } b.WriteString(key) b.WriteByte('=') f.appendValue(b, value) } func (f *textFormatter) appendValue(b *bytes.Buffer, value interface{}) { stringVal, ok := value.(string) if !ok { stringVal = fmt.Sprint(value) } if !f.needsQuoting(stringVal) { b.WriteString(stringVal) } else { b.WriteString(fmt.Sprintf("%q", stringVal)) } } func prefixFieldClashes(data logrus.Fields) { if t, ok := data["time"]; ok { data["fields.time"] = t } if m, ok := data["msg"]; ok { data["fields.msg"] = m } if l, ok := data["level"]; ok { data["fields.level"] = l } }