348bace269
in the Logrus context it's the caller, so use that internally. Label stays as 'method' since in the context of the log event that seems more correct.
179 lines
3.8 KiB
Go
179 lines
3.8 KiB
Go
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 ReportCaller() {
|
|
f.appendKeyValue(b, "method", entry.Caller)
|
|
}
|
|
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]
|
|
|
|
caller := ""
|
|
if ReportCaller() {
|
|
caller = fmt.Sprintf(" %s()", entry.Caller)
|
|
}
|
|
|
|
if !f.FullTimestamp {
|
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText,
|
|
miniTS(), caller, entry.Message)
|
|
} else {
|
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText,
|
|
entry.Time.Format(timestampFormat), caller, 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)
|
|
}
|
|
}
|