Skip to content

Commit

Permalink
log: Allow eggos implementations to programatically set log levels (#96)
Browse files Browse the repository at this point in the history
When running applications on environments where there is no environment, calls to os.Setenv to set a loglevel have inconsistent results. Similarly, setting this environment variable before the first log event, and thus making that call moot, is racy.

Instead, then, export log levels, drop the potential stutter in log.LogLvl to log.Level, and allow implementations/ callers of this package to set a log level accordingly, while maintaining the default behaviour
  • Loading branch information
jspc committed Feb 4, 2022
1 parent 8613a4e commit 86eec44
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 23 deletions.
59 changes: 36 additions & 23 deletions log/log.go
Expand Up @@ -4,13 +4,22 @@ import (
"bytes"
"fmt"
"os"
"sync"

"github.com/icexin/eggos/console"
"github.com/icexin/eggos/drivers/uart"
"github.com/icexin/eggos/kernel/sys"
)

type LogLevel int8

const (
LoglvlDebug LogLevel = iota
LoglvlInfo
LoglvlWarn
LoglvlError
LoglvlNone
)

const (
loglvlEnv = "EGGOS_LOGLVL"
loglvlEnvDebug = "debug"
Expand All @@ -19,39 +28,43 @@ const (
loglvlEnvError = "error"
loglvlEnvNone = "none"

loglvlDebug = iota
loglvlInfo
loglvlWarn
loglvlError
loglvlNone

defaultLoglvl = loglvlError
defaultLoglvl = LoglvlError
)

var (
loglvl int
loglvlonce sync.Once
Level LogLevel

ErrInvalidLogLevel = fmt.Errorf("invalid log level")
)

func setLoglvl() {
func init() {
lvl := os.Getenv("EGGOS_LOGLVL")
switch lvl {
case loglvlEnvDebug:
loglvl = loglvlDebug
Level = LoglvlDebug
case loglvlEnvInfo:
loglvl = loglvlInfo
Level = LoglvlInfo
case loglvlEnvWarn:
loglvl = loglvlWarn
Level = LoglvlWarn
case loglvlEnvError:
loglvl = loglvlError
Level = LoglvlError
default:
loglvl = defaultLoglvl
Level = defaultLoglvl
}
}

func logf(lvl int, fmtstr string, args ...interface{}) {
loglvlonce.Do(setLoglvl)
if lvl < loglvl {
func SetLevel(l LogLevel) error {
if l < LoglvlDebug || l > LoglvlNone {
return ErrInvalidLogLevel
}

Level = l

return nil
}

func logf(lvl LogLevel, fmtstr string, args ...interface{}) {
if lvl < Level {
return
}

Expand All @@ -62,19 +75,19 @@ func logf(lvl int, fmtstr string, args ...interface{}) {
}

func Debugf(fmtstr string, args ...interface{}) {
logf(loglvlDebug, fmtstr, args...)
logf(LoglvlDebug, fmtstr, args...)
}

func Infof(fmtstr string, args ...interface{}) {
logf(loglvlInfo, fmtstr, args...)
logf(LoglvlInfo, fmtstr, args...)
}

func Warnf(fmtstr string, args ...interface{}) {
logf(loglvlWarn, fmtstr, args...)
logf(LoglvlWarn, fmtstr, args...)
}

func Errorf(fmtstr string, args ...interface{}) {
logf(loglvlError, fmtstr, args...)
logf(LoglvlError, fmtstr, args...)
}

//go:nosplit
Expand Down
48 changes: 48 additions & 0 deletions log/log_test.go
@@ -0,0 +1,48 @@
package log_test

import (
"testing"

"github.com/icexin/eggos/log"
)

func ExampleSetLogLevel() {
// Set the log level to Debug
log.Level = log.LoglvlDebug
}

func TestLog_SetLevel(t *testing.T) {
for _, test := range []struct {
name string
l log.LogLevel
expectError bool
}{
// These two cases require users to do something pretty explcitly
// wrong to hit, but they're worth catching
{"log level is too low", log.LogLevel(-1), true},
{"log level is too high", log.LogLevel(6), true},

// Included log levels
{"log.LoglvlDebug is valid", log.LoglvlDebug, false},
{"log.LoglvlInfo is valid", log.LoglvlInfo, false},
{"log.LoglvlWarn is valid", log.LoglvlWarn, false},
{"log.LoglvlError is valid", log.LoglvlError, false},
{"log.LoglvlNone is valid", log.LoglvlNone, false},
} {
t.Run(test.name, func(t *testing.T) {
err := log.SetLevel(test.l)

if err == nil && test.expectError {
t.Error("expected error, received none")
} else if err != nil && !test.expectError {
t.Errorf("unexpected error: %#v", err)
}

if !test.expectError {
if log.Level != test.l {
t.Errorf("log.Level should be %#v, received %#v", log.Level, test.l)
}
}
})
}
}

0 comments on commit 86eec44

Please sign in to comment.