v0.26.3 web viewer improvement

This commit is contained in:
lixen committed 2026-10-05 03:33:24 -04:00
1 parent d745a128a2
commit c00c076d9b
14 files changed
+159 -85

No files matched your search

+17 -40
View File
@@ -69,18 +69,14 @@ const (
DefaultConsoleColor = "auto"
)
// levelColors paints the first of a level's names in an entry, ANSI 16 so
// the terminal's theme picks the shades and a text console renders them.
// Names run longest first; cyan, not blue, stays readable on black.
var levelColors = map[string]struct {
names []string
style inline.Style
}{
"ERROR": {[]string{"ERROR", "FATAL", "ERR"}, inline.FgANSI(color.ANSIRed).Bold()},
"WARN": {[]string{"WARNING", "WARN"}, inline.FgANSI(color.ANSIYellow)},
"INFO": {[]string{"INFO", "INF"}, inline.FgANSI(color.ANSIGreen)},
"DEBUG": {[]string{"DEBUG", "DBG"}, inline.FgANSI(color.ANSICyan)},
"TRACE": {[]string{"TRACE"}, inline.FgANSI(color.ANSIBrightBlack)},
// levelStyles colors the levels in ANSI 16, so the terminal's theme picks the
// shades and a text console renders them; cyan, not blue, stays readable on black
var levelStyles = map[string]inline.Style{
"ERROR": inline.FgANSI(color.ANSIRed).Bold(),
"WARN": inline.FgANSI(color.ANSIYellow),
"INFO": inline.FgANSI(color.ANSIGreen),
"DEBUG": inline.FgANSI(color.ANSICyan),
"TRACE": inline.FgANSI(color.ANSIBrightBlack),
}
// NewConsoleSinkPlugin creates a console sink through plugin factory
@@ -273,42 +269,23 @@ func (cs *ConsoleSink) write(event core.TransportEvent) {
cs.lastProcessed.Store(time.Now())
}
// paintLevel colors the first whole-word, case-insensitive occurrence of a
// name of level, after escaping: the escape sequences are the sink's own.
// paintLevel colors the first word naming the entry's level, after escaping:
// the escape sequences are the sink's own
func paintLevel(p *inline.Printer, payload []byte, level string) []byte {
lc, ok := levelColors[level]
level, _, _ = core.LevelWord(level, "")
style, ok := levelStyles[level]
if !ok {
return payload
}
for _, name := range lc.names {
for i := 0; i+len(name) <= len(payload); i++ {
j := i + len(name)
if !nameAt(payload[i:j], name) || i > 0 && word(payload[i-1]) || j < len(payload) && word(payload[j]) {
continue
}
out := make([]byte, 0, len(payload)+16)
out = append(out, payload[:i]...)
out = append(out, p.Paint(string(payload[i:j]), lc.style)...)
return append(out, payload[j:]...)
}
if _, i, j := core.LevelWord(payload, level); i < j {
out := make([]byte, 0, len(payload)+16)
out = append(out, payload[:i]...)
out = append(out, p.Paint(string(payload[i:j]), style)...)
return append(out, payload[j:]...)
}
return payload
}
// nameAt compares ASCII letters without case; name is upper case
func nameAt(b []byte, name string) bool {
for k := range len(name) {
if c := b[k]; c != name[k] && c != name[k]+'a'-'A' {
return false
}
}
return true
}
func word(c byte) bool {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_'
}
// escapeControls writes what a terminal would act on or reorder (C0 and C1
// controls but tab, DEL, bidi controls, line separators, invalid UTF-8) as
// <hex>, keeping the final newline: a log line cannot drive the terminal.
+3 -2
View File
@@ -29,8 +29,8 @@ func TestEscapeControlsKeepsOnlyLayout(t *testing.T) {
}
}
// The entry's level is painted where its line first names it as a whole
// word, in any case, longest name first; other words and levels stay plain.
// The entry's level, under any of its names, is painted where its line first
// names it as a whole word, in any case; other words and levels stay plain.
func TestPaintLevelColorsTheFirstWholeName(t *testing.T) {
p := inline.New(io.Discard)
p.SetColor(true)
@@ -38,6 +38,7 @@ func TestPaintLevelColorsTheFirstWholeName(t *testing.T) {
for _, c := range []struct{ in, level, want string }{
{"2026 ERROR boom ERROR\n", "ERROR", "2026 " + red + "ERROR\x1b[0m boom ERROR\n"},
{"[warning] disk\n", "WARN", "[" + yellow + "warning\x1b[0m] disk\n"},
{"INFO: WARN disk\n", "warning", "INFO: " + yellow + "WARN\x1b[0m disk\n"},
{"terror_x ERR:1\n", "ERROR", "terror_x " + red + "ERR\x1b[0m:1\n"},
{"INFOS informal\n", "INFO", "INFOS informal\n"},
{"ERROR without level\n", "", "ERROR without level\n"},