150 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
package logrus
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"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 keys []string = make([]string, 0, len(entry.Data))
 | 
						|
	for k := range entry.Data {
 | 
						|
		keys = append(keys, k)
 | 
						|
	}
 | 
						|
 | 
						|
	if !f.DisableSorting {
 | 
						|
		sort.Strings(keys)
 | 
						|
	}
 | 
						|
 | 
						|
	b := &bytes.Buffer{}
 | 
						|
 | 
						|
	prefixFieldClashes(entry.Data)
 | 
						|
 | 
						|
	isColored := (f.ForceColors || isTerminal) && !f.DisableColors
 | 
						|
 | 
						|
	if f.TimestampFormat == "" {
 | 
						|
		f.TimestampFormat = DefaultTimestampFormat
 | 
						|
	}
 | 
						|
	if isColored {
 | 
						|
		f.printColored(b, entry, keys)
 | 
						|
	} else {
 | 
						|
		if !f.DisableTimestamp {
 | 
						|
			f.appendKeyValue(b, "time", entry.Time.Format(f.TimestampFormat))
 | 
						|
		}
 | 
						|
		f.appendKeyValue(b, "level", entry.Level.String())
 | 
						|
		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) {
 | 
						|
	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(f.TimestampFormat), entry.Message)
 | 
						|
	}
 | 
						|
	for _, k := range keys {
 | 
						|
		v := entry.Data[k]
 | 
						|
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, 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 false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
 | 
						|
	switch value.(type) {
 | 
						|
	case string:
 | 
						|
		if needsQuoting(value.(string)) {
 | 
						|
			fmt.Fprintf(b, "%v=%s ", key, value)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(b, "%v=%q ", key, value)
 | 
						|
		}
 | 
						|
	case error:
 | 
						|
		if needsQuoting(value.(error).Error()) {
 | 
						|
			fmt.Fprintf(b, "%v=%s ", key, value)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(b, "%v=%q ", key, value)
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		fmt.Fprintf(b, "%v=%v ", key, value)
 | 
						|
	}
 | 
						|
}
 |