package filter import ( "fmt" "strings" "github.com/lixenwraith/vif-log/internal/logfile" ) func init() { Register(Desc{Kind: "level", Help: "level set (IWE) or threshold (>=WARN)", New: newLevel}) } // Level filters by a bitmask over logfile.Level. Threshold mode is a mutation // of the mask, not a second representation. type Level struct{ Mask uint16 } // NewLevel returns a level filter with every level enabled. func NewLevel() *Level { return &Level{Mask: (1 << uint(logfile.LevelCount)) - 1} } func newLevel(arg string) (Filter, error) { f := NewLevel() arg = strings.TrimSpace(arg) if arg == "" { return f, nil } if t, ok := strings.CutPrefix(arg, ">="); ok { l, found := levelByName(strings.TrimSpace(t)) if !found { return nil, fmt.Errorf("filter/level: unknown level %q", t) } f.Threshold(l) return f, nil } f.Mask = 0 for i := 0; i < len(arg); i++ { l, ok := logfile.LevelByInitial(arg[i] &^ 0x20) if !ok { return nil, fmt.Errorf("filter/level: unknown level initial %q", arg[i]) } f.Mask |= 1 << uint(l) } return f, nil } func levelByName(s string) (logfile.Level, bool) { s = strings.ToUpper(s) for i := logfile.Level(0); i < logfile.LevelCount; i++ { if i.String() == s { return i, true } } return logfile.LevelBad, false } func (f *Level) Kind() string { return "level" } func (f *Level) Needs() Need { return 0 } func (f *Level) Match(c *Ctx) bool { return f.Mask&(1<= l { f.Mask |= 1 << uint(i) } else { f.Mask &^= 1 << uint(i) } } } // ThresholdToggle applies the threshold at l, or restores every ordered level // when the mask already has exactly that shape. Digit keys use this: 2 hides // TRACE, 2 again brings it back. func (f *Level) ThresholdToggle(l logfile.Level) { if f.isThreshold(l) { for i := range logfile.Level(logfile.OrderedCount) { f.Mask |= 1 << uint(i) } return } f.Threshold(l) } // isThreshold reports whether the ordered levels are exactly those at or above l. func (f *Level) isThreshold(l logfile.Level) bool { for i := range logfile.Level(logfile.OrderedCount) { if f.Has(i) != (i >= l) { return false } } return true } // AllOn reports whether no level is filtered out. func (f *Level) AllOn() bool { return f.Mask&((1<= logfile.OrderedCount { t = logfile.OrderedCount - 1 } f.Threshold(logfile.Level(t)) } // LowestOrdered returns the lowest enabled ordered level, ERROR if none. func (f *Level) LowestOrdered() logfile.Level { for i := logfile.Level(0); i < logfile.Level(logfile.OrderedCount); i++ { if f.Has(i) { return i } } return logfile.LevelError } // Label reports the level filter only when it hides something; the header // strip shows the per-level state. func (f *Level) Label() string { if f.AllOn() { return "" } if l, ok := f.thresholdShape(); ok { return "lvl>=" + l.String() } var b strings.Builder b.WriteString("lvl:") for i := range logfile.LevelCount { if f.Has(i) { b.WriteByte(i.Initial()) } } return b.String() } // thresholdShape reports whether the mask is "ordered >= t, plus PROC and BAD". func (f *Level) thresholdShape() (logfile.Level, bool) { if !f.Has(logfile.LevelProc) || !f.Has(logfile.LevelBad) { return 0, false } t := f.LowestOrdered() if t == 0 || !f.isThreshold(t) { return 0, false } return t, true }