Skip to content

Commit e12ce40

Browse files
committed
Add support for arbitrary numeric log level strings
Support numeric log level ranges defined by slog library. Signed-off-by: Derek McGowan <derek@mcg.dev>
1 parent 909c7de commit e12ce40

2 files changed

Lines changed: 115 additions & 1 deletion

File tree

context.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ package log
4040
import (
4141
"context"
4242
"fmt"
43+
"strconv"
44+
"strings"
4345

4446
"github.com/sirupsen/logrus"
4547
)
@@ -120,8 +122,19 @@ const (
120122
// - "error" ([ErrorLevel])
121123
// - "fatal" ([FatalLevel])
122124
// - "panic" ([PanicLevel])
125+
//
126+
// In addition, a numeric value can be provided using
127+
// the level range defined by Go's slog library:
128+
//
129+
// - -8: trace
130+
// - -4: debug
131+
// - 0: info
132+
// - 4: warn
133+
// - 8: error
134+
// - 10: fatal
135+
// - 12: panic
123136
func SetLevel(level string) error {
124-
lvl, err := logrus.ParseLevel(level)
137+
lvl, err := parseLevel(level)
125138
if err != nil {
126139
return err
127140
}
@@ -130,6 +143,50 @@ func SetLevel(level string) error {
130143
return nil
131144
}
132145

146+
func parseLevel(level string) (Level, error) {
147+
switch strings.ToLower(level) {
148+
case "trace":
149+
return TraceLevel, nil
150+
case "debug":
151+
return DebugLevel, nil
152+
case "info":
153+
return InfoLevel, nil
154+
case "warn", "warning":
155+
return WarnLevel, nil
156+
case "error":
157+
return ErrorLevel, nil
158+
case "fatal":
159+
return FatalLevel, nil
160+
case "panic":
161+
return PanicLevel, nil
162+
}
163+
164+
// Default to parsing as numeric level
165+
if v, err := strconv.Atoi(level); err == nil {
166+
return numericLevel(v), nil
167+
}
168+
return InfoLevel, fmt.Errorf("unknown log level: %s", level)
169+
}
170+
171+
// numericLevel returns the logrus level for the given integer value,
172+
// choosing the nearest level without going above the given value.
173+
func numericLevel(v int) Level {
174+
if v <= -8 {
175+
return TraceLevel
176+
} else if v <= -4 {
177+
return DebugLevel
178+
} else if v <= 0 {
179+
return InfoLevel
180+
} else if v <= 4 {
181+
return WarnLevel
182+
} else if v <= 8 {
183+
return ErrorLevel
184+
} else if v <= 10 {
185+
return FatalLevel
186+
}
187+
return PanicLevel
188+
}
189+
133190
// GetLevel returns the current log level.
134191
func GetLevel() Level {
135192
return L.Logger.GetLevel()

context_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,63 @@ func TestLoggerContext(t *testing.T) {
3838
}
3939
}
4040

41+
func TestSetLevel(t *testing.T) {
42+
for _, tc := range []struct {
43+
input string
44+
expected Level
45+
}{
46+
// Named levels
47+
{input: "trace", expected: TraceLevel},
48+
{input: "debug", expected: DebugLevel},
49+
{input: "info", expected: InfoLevel},
50+
{input: "warn", expected: WarnLevel},
51+
{input: "warning", expected: WarnLevel},
52+
{input: "error", expected: ErrorLevel},
53+
{input: "fatal", expected: FatalLevel},
54+
{input: "panic", expected: PanicLevel},
55+
{input: "INFO", expected: InfoLevel},
56+
{input: "DEBUG", expected: DebugLevel},
57+
58+
// Exact slog integer levels
59+
{input: "-8", expected: TraceLevel},
60+
{input: "-4", expected: DebugLevel},
61+
{input: "0", expected: InfoLevel},
62+
{input: "4", expected: WarnLevel},
63+
{input: "8", expected: ErrorLevel},
64+
{input: "10", expected: FatalLevel},
65+
{input: "12", expected: PanicLevel},
66+
67+
// Nearest numeric levels (without going above)
68+
{input: "-10", expected: TraceLevel},
69+
{input: "-6", expected: DebugLevel},
70+
{input: "-5", expected: DebugLevel},
71+
{input: "-2", expected: InfoLevel},
72+
{input: "1", expected: WarnLevel},
73+
{input: "2", expected: WarnLevel},
74+
{input: "3", expected: WarnLevel},
75+
{input: "6", expected: ErrorLevel},
76+
{input: "7", expected: ErrorLevel},
77+
{input: "9", expected: FatalLevel},
78+
{input: "11", expected: PanicLevel},
79+
{input: "13", expected: PanicLevel},
80+
{input: "100", expected: PanicLevel},
81+
82+
// Unparseable defaults to panic
83+
{input: "bogus", expected: PanicLevel},
84+
{input: "", expected: PanicLevel},
85+
} {
86+
t.Run(tc.input, func(t *testing.T) {
87+
err := SetLevel(tc.input)
88+
if err != nil {
89+
t.Fatalf("unexpected error: %v", err)
90+
}
91+
if actual := GetLevel(); actual != tc.expected {
92+
t.Errorf("expected level %v, got %v", tc.expected, actual)
93+
}
94+
})
95+
}
96+
}
97+
4198
func TestCompat(t *testing.T) {
4299
expected := Fields{
43100
"hello1": "world1",

0 commit comments

Comments
 (0)