v0.1.0 initial commit

This commit is contained in:
2026-08-06 10:18:22 -04:00
commit 28e42523e1
31 changed files with 5055 additions and 0 deletions
+166
View File
@@ -0,0 +1,166 @@
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<<uint(c.Meta.Lvl)) != 0 }
// Has reports whether level l passes.
func (f *Level) Has(l logfile.Level) bool { return f.Mask&(1<<uint(l)) != 0 }
// Toggle flips one level.
func (f *Level) Toggle(l logfile.Level) { f.Mask ^= 1 << uint(l) }
// SetAll enables or disables every level.
func (f *Level) SetAll(on bool) {
if on {
f.Mask = (1 << uint(logfile.LevelCount)) - 1
} else {
f.Mask = 0
}
}
// Threshold enables the ordered levels at or above l, leaving PROC and BAD alone.
func (f *Level) Threshold(l logfile.Level) {
for i := range logfile.Level(logfile.OrderedCount) {
if i >= 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<<uint(logfile.LevelCount))-1) == (1<<uint(logfile.LevelCount))-1
}
// Shift moves the threshold by d, clamped to the ordered range.
func (f *Level) Shift(d int) {
t := int(f.LowestOrdered())
t += d
if t < 0 {
t = 0
}
if t >= 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
}