1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
package jl
import (
"fmt"
"io"
"strings"
"unicode"
)
// CompactPrinter can print logs in a variety of compact formats, specified by FieldFormats.
type CompactPrinter struct {
Out io.Writer
// Disable colors disables adding color to fields.
DisableColor bool
// Disable truncate disables the Ellipsize and Truncate transforms.
DisableTruncate bool
// FieldFormats specifies the format the printer should use for logs. It defaults to DefaultCompactPrinterFieldFmt. Fields
// are formatted in the order they are provided. If a FieldFmt produces a field that does not end with a whitespace,
// a space character is automatically appended.
FieldFormats []FieldFmt
}
// FieldFmt specifies a single field formatted by the CompactPrinter.
type FieldFmt struct {
// Name of the field. This is used to find the field by key name if Finders is not set.
Name string
// List of FieldFinders to use to locate the field. Finders are executed in order until the first one that returns
// non-nil.
Finders []FieldFinder
// Takes the output of the Finder and turns it into a string. If not set, DefaultStringer is used.
Stringer Stringer
// List of transformers to run on the field found to format the field.
Transformers []Transformer
}
// DefaultCompactPrinterFieldFmt is a format for the CompactPrinter that tries to present logs in an easily skimmable manner
// for most types of logs.
var DefaultCompactPrinterFieldFmt = []FieldFmt{{
Name: "level",
Finders: []FieldFinder{ByNames("level", "severity", "logLevel")},
Transformers: []Transformer{Truncate(4), UpperCase, ColorMap(LevelColors)},
}, {
Name: "time",
Finders: []FieldFinder{ByNames("timestamp", "time", "ts")},
}, {
Name: "thread",
Transformers: []Transformer{Ellipsize(16), Format("[%s]"), RightPad(18), ColorSequence(AllColors)},
}, {
Name: "logger",
Finders: []FieldFinder{ByNames("logger", "caller")},
Transformers: []Transformer{Ellipsize(20), Format("%s|"), LeftPad(21), ColorSequence(AllColors)},
}, {
Name: "traceId",
Transformers: []Transformer{Format("%s|"), ColorSequence(AllColors)},
}, {
Name: "message",
Finders: []FieldFinder{ByNames("message", "msg", "textPayload", "jsonPayload.message")},
}, {
Name: "errors",
Finders: []FieldFinder{LogrusErrorFinder, ByNames("exceptions", "exception", "error")},
Stringer: ErrorStringer,
}}
// NewCompactPrinter allocates and returns a new compact printer.
func NewCompactPrinter(w io.Writer) *CompactPrinter {
return &CompactPrinter{
Out: w,
FieldFormats: DefaultCompactPrinterFieldFmt,
}
}
func (p *CompactPrinter) Print(entry *Entry) {
if entry.Partials == nil {
fmt.Fprintln(p.Out, string(entry.Raw))
return
}
for i, fieldFmt := range p.FieldFormats {
ctx := Context{
DisableColor: p.DisableColor,
DisableTruncate: p.DisableTruncate,
}
formattedField := fieldFmt.format(&ctx, entry)
if formattedField != "" {
if i != 0 && !strings.HasPrefix(formattedField, "\n") {
p.Out.Write([]byte(" "))
}
p.Out.Write([]byte(formattedField))
}
}
p.Out.Write([]byte("\n"))
}
func (f *FieldFmt) format(ctx *Context, entry *Entry) string {
var v interface{}
// Find the value
if len(f.Finders) > 0 {
for _, finder := range f.Finders {
if v = finder(entry); v != nil {
break
} else {
}
}
} else {
v = entry.Partials[f.Name]
}
if v == nil {
return ""
}
// Stringify the value
var s string
if f.Stringer != nil {
s = f.Stringer(ctx, v)
} else {
s = DefaultStringer(ctx, v)
}
s = strings.TrimRightFunc(s, unicode.IsSpace)
if s == "" {
return ""
}
original := s
ctx.Original = original
// Apply transforms
for _, transform := range f.Transformers {
s = transform.Transform(ctx, s)
}
if s == "" {
return ""
}
return s
}
|