|  | package logrus | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "fmt" | 
|  | "runtime" | 
|  | "sort" | 
|  | "strings" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | nocolor = 0 | 
|  | red     = 31 | 
|  | green   = 32 | 
|  | yellow  = 33 | 
|  | blue    = 34 | 
|  | gray    = 37 | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | baseTimestamp time.Time | 
|  | isTerminal    bool | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | baseTimestamp = time.Now() | 
|  | isTerminal = IsTerminal() | 
|  | } | 
|  |  | 
|  | func miniTS() int { | 
|  | return int(time.Since(baseTimestamp) / time.Second) | 
|  | } | 
|  |  | 
|  | type TextFormatter struct { | 
|  | // Set to true to bypass checking for a TTY before outputting colors. | 
|  | ForceColors bool | 
|  |  | 
|  | // Force disabling colors. | 
|  | DisableColors bool | 
|  |  | 
|  | // Disable timestamp logging. useful when output is redirected to logging | 
|  | // system that already adds timestamps. | 
|  | DisableTimestamp bool | 
|  |  | 
|  | // Enable logging the full timestamp when a TTY is attached instead of just | 
|  | // the time passed since beginning of execution. | 
|  | FullTimestamp bool | 
|  |  | 
|  | // TimestampFormat to use for display when a full timestamp is printed | 
|  | TimestampFormat string | 
|  |  | 
|  | // The fields are sorted by default for a consistent output. For applications | 
|  | // that log extremely frequently and don't use the JSON formatter this may not | 
|  | // be desired. | 
|  | DisableSorting bool | 
|  | } | 
|  |  | 
|  | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { | 
|  | var b *bytes.Buffer | 
|  | var keys []string = make([]string, 0, len(entry.Data)) | 
|  | for k := range entry.Data { | 
|  | keys = append(keys, k) | 
|  | } | 
|  |  | 
|  | if !f.DisableSorting { | 
|  | sort.Strings(keys) | 
|  | } | 
|  | if entry.Buffer != nil { | 
|  | b = entry.Buffer | 
|  | } else { | 
|  | b = &bytes.Buffer{} | 
|  | } | 
|  |  | 
|  | prefixFieldClashes(entry.Data) | 
|  |  | 
|  | isColorTerminal := isTerminal && (runtime.GOOS != "windows") | 
|  | isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors | 
|  |  | 
|  | timestampFormat := f.TimestampFormat | 
|  | if timestampFormat == "" { | 
|  | timestampFormat = DefaultTimestampFormat | 
|  | } | 
|  | if isColored { | 
|  | f.printColored(b, entry, keys, timestampFormat) | 
|  | } else { | 
|  | if !f.DisableTimestamp { | 
|  | f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) | 
|  | } | 
|  | f.appendKeyValue(b, "level", entry.Level.String()) | 
|  | 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 *Entry, keys []string, timestampFormat string) { | 
|  | var levelColor int | 
|  | switch entry.Level { | 
|  | case DebugLevel: | 
|  | levelColor = gray | 
|  | case WarnLevel: | 
|  | levelColor = yellow | 
|  | case ErrorLevel, FatalLevel, PanicLevel: | 
|  | levelColor = red | 
|  | default: | 
|  | levelColor = blue | 
|  | } | 
|  |  | 
|  | levelText := strings.ToUpper(entry.Level.String())[0:4] | 
|  |  | 
|  | if !f.FullTimestamp { | 
|  | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) | 
|  | } else { | 
|  | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), 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 needsQuoting(text string) bool { | 
|  | for _, ch := range text { | 
|  | if !((ch >= 'a' && ch <= 'z') || | 
|  | (ch >= 'A' && ch <= 'Z') || | 
|  | (ch >= '0' && ch <= '9') || | 
|  | ch == '-' || ch == '.') { | 
|  | return true | 
|  | } | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { | 
|  |  | 
|  | b.WriteString(key) | 
|  | b.WriteByte('=') | 
|  | f.appendValue(b, value) | 
|  | b.WriteByte(' ') | 
|  | } | 
|  |  | 
|  | func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { | 
|  | switch value := value.(type) { | 
|  | case string: | 
|  | if !needsQuoting(value) { | 
|  | b.WriteString(value) | 
|  | } else { | 
|  | fmt.Fprintf(b, "%q", value) | 
|  | } | 
|  | case error: | 
|  | errmsg := value.Error() | 
|  | if !needsQuoting(errmsg) { | 
|  | b.WriteString(errmsg) | 
|  | } else { | 
|  | fmt.Fprintf(b, "%q", errmsg) | 
|  | } | 
|  | default: | 
|  | fmt.Fprint(b, value) | 
|  | } | 
|  | } |