-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d1f1a05
commit ff5f358
Showing
5 changed files
with
219 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package log | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
|
||
"github.com/go-logr/logr" | ||
"go.uber.org/zap/zapcore" | ||
"k8s.io/klog/v2" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/log/zap" | ||
) | ||
|
||
var ( | ||
// Log is a singleton base logger that can be used across the system, | ||
// either directly or to create other loggers with name, with values, | ||
// and/or locked to a given log level. | ||
// It is initialized to the promise delegation log provided by | ||
// sigs.k8s.io/controller-runtime, which points to a no-op (null) logger | ||
// until `SetLogger` is called. | ||
// This is also useful for mocking the default logger tests. | ||
Log Logger = ctrl.Log | ||
) | ||
|
||
type Logger = logr.Logger | ||
|
||
type LogLevel zapcore.Level | ||
|
||
func (l *LogLevel) String() string { | ||
return zapcore.Level(*l).String() | ||
} | ||
|
||
// ToLogLevel converts a string to a log level. | ||
func ToLogLevel(level string) LogLevel { | ||
var l zapcore.Level | ||
_ = l.UnmarshalText([]byte(level)) | ||
return LogLevel(l) | ||
} | ||
|
||
// LogMode defines the log output mode. | ||
type LogMode int8 | ||
|
||
const ( | ||
// LogModeProd is the log mode for production. | ||
LogModeProd LogMode = iota | ||
// LogModeDev is for more human-readable outputs, extra stack traces | ||
// and logging info. (aka Zap's "development config".) | ||
LogModeDev | ||
) | ||
|
||
func (f *LogMode) String() string { | ||
switch *f { | ||
case LogModeProd: | ||
return "production" | ||
case LogModeDev: | ||
return "development" | ||
default: | ||
return "unknown" | ||
} | ||
} | ||
|
||
// ToLogMode converts a string to a log mode. | ||
// Use either 'production' for `LogModeProd` or 'development' for `LogModeDev`. | ||
func ToLogMode(mode string) LogMode { | ||
switch strings.ToLower(mode) { | ||
case "production": | ||
return LogModeProd | ||
case "development": | ||
return LogModeDev | ||
default: | ||
panic("unknown log mode") | ||
} | ||
} | ||
|
||
// Options is a set of options for a configured logger. | ||
type Options struct { | ||
Level LogLevel | ||
Mode LogMode | ||
} | ||
|
||
// SetLogger sets up a logger. | ||
func SetLogger(logger Logger) { | ||
Log = logger | ||
|
||
ctrl.SetLogger(Log) // fulfills `logger` as the de facto logger used by controller-runtime | ||
klog.SetLogger(Log) | ||
} | ||
|
||
// WithName uses the singleton logger to create a new logger with the given name. | ||
func WithName(name string) Logger { | ||
return Log.WithName(name) | ||
} | ||
|
||
// WithName uses the singleton logger to create a new logger with the given values. | ||
func WithValues(keysAndValues ...interface{}) Logger { | ||
return Log.WithValues(keysAndValues...) | ||
} | ||
|
||
// V uses the singleton logger to create a new logger for the given log level. | ||
func V(level int) Logger { | ||
return Log.V(level) | ||
} | ||
|
||
// IntoContext takes a context and sets the logger as one of its values. | ||
// Use FromContext function to retrieve the logger. | ||
func IntoContext(ctx context.Context, log Logger) context.Context { | ||
return logr.NewContext(ctx, log) | ||
} | ||
|
||
// FromContext returns a logger with predefined values from a context.Context. | ||
func FromContext(ctx context.Context, keysAndValues ...interface{}) Logger { | ||
var l logr.Logger = Log | ||
if ctx != nil { | ||
if logger, err := logr.FromContext(ctx); err == nil { | ||
l = logger | ||
} | ||
} | ||
return l.WithValues(keysAndValues...) | ||
} | ||
|
||
// NewLogger returns a new logger with the given options. | ||
// `logger` param is the actual logger implementation; when omitted, a new | ||
// logger based on sigs.k8s.io/controller-runtime/pkg/log/zap is created. | ||
func NewLogger(opts Options) Logger { | ||
return zap.New( | ||
zap.Level(zapcore.Level(opts.Level)), | ||
zap.UseDevMode(opts.Mode == LogModeDev), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package log | ||
|
||
import ( | ||
"testing" | ||
|
||
"gotest.tools/assert" | ||
) | ||
|
||
func TestLogLevelToString(t *testing.T) { | ||
level := LogLevel(-1) | ||
assert.Equal(t, level.String(), "debug") | ||
|
||
level = LogLevel(0) | ||
assert.Equal(t, level.String(), "info") | ||
|
||
level = LogLevel(1) | ||
assert.Equal(t, level.String(), "warn") | ||
|
||
level = LogLevel(2) | ||
assert.Equal(t, level.String(), "error") | ||
|
||
level = LogLevel(3) | ||
assert.Equal(t, level.String(), "dpanic") | ||
|
||
level = LogLevel(4) | ||
assert.Equal(t, level.String(), "panic") | ||
|
||
level = LogLevel(5) | ||
assert.Equal(t, level.String(), "fatal") | ||
} | ||
|
||
func TestToLogLevel(t *testing.T) { | ||
assert.Equal(t, int(ToLogLevel("debug")), -1) | ||
assert.Equal(t, int(ToLogLevel("info")), 0) | ||
assert.Equal(t, int(ToLogLevel("warn")), 1) | ||
assert.Equal(t, int(ToLogLevel("error")), 2) | ||
assert.Equal(t, int(ToLogLevel("dpanic")), 3) | ||
assert.Equal(t, int(ToLogLevel("panic")), 4) | ||
assert.Equal(t, int(ToLogLevel("fatal")), 5) | ||
assert.Equal(t, int(ToLogLevel("invalid")), 0) // falls back to default log level (info) without panicking | ||
} | ||
|
||
func TestLogModeToString(t *testing.T) { | ||
level := LogMode(0) | ||
assert.Equal(t, level.String(), "production") | ||
|
||
level = LogMode(1) | ||
assert.Equal(t, level.String(), "development") | ||
} | ||
|
||
func TestToLogMode(t *testing.T) { | ||
assert.Equal(t, int(ToLogMode("production")), 0) | ||
assert.Equal(t, int(ToLogMode("development")), 1) | ||
|
||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf(`ToLogMode("invalid") was expected to panic and it did not.`) | ||
} | ||
}() | ||
_ = ToLogMode("invalid") | ||
} |