diff --git a/internal/sink/line_test.go b/internal/sink/line_test.go index 846e4a5..d23ddc9 100644 --- a/internal/sink/line_test.go +++ b/internal/sink/line_test.go @@ -4,33 +4,33 @@ import ( "fmt" "testing" - "github.com/ViaQ/logerr/v2/kverrors" "github.com/ViaQ/logerr/v2/internal/sink" + "github.com/ViaQ/logerr/v2/kverrors" "github.com/stretchr/testify/require" ) func TestLine_ProductionLogs(t *testing.T) { - // Logs with a level of 0 or 1 are considered to be production level logs - msg := "hello, world" - s, b := sinkWithBuffer("", 1) + // Logs with a level of 0 or 1 are considered to be production level logs + msg := "hello, world" + s, b := sinkWithBuffer("", 1) s.Info(0, msg) - logMsg := string(b.Bytes()) + logMsg := string(b.Bytes()) - require.NotEmpty(t, logMsg) + require.NotEmpty(t, logMsg) require.Contains(t, logMsg, fmt.Sprintf(`%q:%q`, sink.MessageKey, msg)) require.NotContains(t, logMsg, fmt.Sprintf(`%q`, sink.FileLineKey)) } func TestLine_DeveloperLogs(t *testing.T) { - // Logs with a higher level than 1 are considered to be developer level logs - msg := "hello, world" - s, b := sinkWithBuffer("", 2) + // Logs with a higher level than 1 are considered to be developer level logs + msg := "hello, world" + s, b := sinkWithBuffer("", 2) s.Info(1, msg) - logMsg := string(b.Bytes()) + logMsg := string(b.Bytes()) - require.NotEmpty(t, logMsg) + require.NotEmpty(t, logMsg) require.Contains(t, logMsg, fmt.Sprintf(`%q:%q`, sink.MessageKey, msg)) require.Contains(t, logMsg, fmt.Sprintf(`%q`, sink.FileLineKey)) } @@ -46,16 +46,16 @@ func TestLine_WithNoContext(t *testing.T) { } func TestLine_LogLevel(t *testing.T) { - s, b := sinkWithBuffer("", 0) + s, b := sinkWithBuffer("", 0) - for level := 0; level < 5; level++ { - b.Reset() - s.SetVerbosity(level) + for level := 0; level < 5; level++ { + b.Reset() + s.SetVerbosity(level) - s.Info(0, "hello, world") + s.Info(0, "hello, world") - require.Contains(t, string(b.Bytes()), fmt.Sprintf(`%q:"%d"`, sink.LevelKey, level)) - } + require.Contains(t, string(b.Bytes()), fmt.Sprintf(`%q:"%d"`, sink.LevelKey, level)) + } } func TestLine_WithKVError(t *testing.T) { @@ -78,7 +78,7 @@ func TestLine_WithKVError(t *testing.T) { func TestLine_WithNestedKVError(t *testing.T) { err := kverrors.New("an error", "key", "value") - wrappedErr := kverrors.Wrap(err, "main error", "key", "value") + wrappedErr := kverrors.Wrap(err, "main error", "key", "value") msg := "Error bypasses the enabled check." kverrMsg, _ := wrappedErr.(*kverrors.KVError).MarshalJSON() diff --git a/internal/sink/sink_test.go b/internal/sink/sink_test.go index 4acc65d..75bffcb 100644 --- a/internal/sink/sink_test.go +++ b/internal/sink/sink_test.go @@ -7,8 +7,8 @@ import ( "math" "testing" - "github.com/ViaQ/logerr/v2/kverrors" "github.com/ViaQ/logerr/v2/internal/sink" + "github.com/ViaQ/logerr/v2/kverrors" "github.com/go-logr/logr" "github.com/stretchr/testify/require" ) @@ -144,6 +144,15 @@ func TestSink_WithValues(t *testing.T) { require.Contains(t, string(b.Bytes()), fmt.Sprintf(`%q:%q`, "foo", "bar")) } +func TestSink_WithKeyAndNoValues(t *testing.T) { + s, b := sinkWithBuffer("", 0, "hello") + + s.Info(0, "First.") + + // Ensuring that dangling key/values are not recorded + require.NotContains(t, string(b.Bytes()), fmt.Sprintf(`%q:`, "hello")) +} + func TestSink_WithName(t *testing.T) { s, b := sinkWithBuffer("new", 0) diff --git a/log/log.go b/log/log.go index 6966473..15b7ede 100644 --- a/log/log.go +++ b/log/log.go @@ -13,33 +13,27 @@ func NewLogger(component string, keyValuePairs ...interface{}) logr.Logger { } // NewLoggerWithOptions creates a logger with the provided opts and key value pairs -func NewLoggerWithOptions(component string, opts *Options, keyValuePairs ...interface{}) logr.Logger { - sinkOpts := Options{Writer: os.Stdout, LogLevel: 0} +func NewLoggerWithOptions(component string, opts []Option, keyValuePairs ...interface{}) logr.Logger { + sink := sink.NewLogSink(component, os.Stdout, 0, sink.JSONEncoder{}, keyValuePairs...) - if opts != nil { - sinkOpts.Writer = opts.Writer - sinkOpts.LogLevel = opts.LogLevel + for _, opt := range opts { + opt(sink) } - return logr.New( - sink.NewLogSink( - component, - sinkOpts.Writer, - sink.Verbosity(sinkOpts.LogLevel), - sink.JSONEncoder{}, - keyValuePairs..., - ), - ) + return logr.New(sink) } -// NewLoggerV calls the logr.Logger V() method while updating the internal sink (if possible) -// to be compatible with the new level -func NewLoggerV(logger logr.Logger, level int) logr.Logger { - s, ok := logger.GetSink().(*sink.Sink) +// SetOptions sets the options of the logger if the `logr.Sink` is a `Sink` +func SetOptions(l logr.Logger, opts []Option) bool { + s, ok := l.GetSink().(*sink.Sink) - if ok { - s.SetVerbosity(s.GetVerbosity() + level) + if !ok { + return false } - return logger.V(level) + for _, opt := range opts { + opt(s) + } + + return true } diff --git a/log/log_test.go b/log/log_test.go index 5a986a9..762ca71 100644 --- a/log/log_test.go +++ b/log/log_test.go @@ -6,21 +6,25 @@ import ( "testing" - "github.com/ViaQ/logerr/v2/log" "github.com/ViaQ/logerr/v2/internal/sink" + "github.com/ViaQ/logerr/v2/log" "github.com/stretchr/testify/require" ) func TestNewLogger(t *testing.T) { component := "mycomponent" - buf := bytes.NewBuffer(nil) + b := bytes.NewBuffer(nil) - l := log.NewLoggerWithOptions(component, nil) - s := l.GetSink().(*sink.Sink) - s.SetOutput(buf) + l := log.NewLogger(component) + log.SetOptions( + l, + []log.Option{ + log.WithOutput(b), + }, + ) l.Info("laskdjfhiausdc") - msg := string(buf.Bytes()) + msg := string(b.Bytes()) componentValue := fmt.Sprintf(`%q:%q`, sink.ComponentKey, component) levelValue := fmt.Sprintf(`%q:"%d"`, sink.LevelKey, 0) @@ -32,13 +36,18 @@ func TestNewLogger(t *testing.T) { func TestNewLogger_WithOptions(t *testing.T) { level := 1 component := "mycomponent" - buf := bytes.NewBuffer(nil) + b := bytes.NewBuffer(nil) - opts := log.Options{Writer: buf, LogLevel: level} - l := log.NewLoggerWithOptions(component, &opts) + l := log.NewLoggerWithOptions( + component, + []log.Option{ + log.WithOutput(b), + log.WithVerbosity(level), + }, + ) l.Info("laskdjfhiausdc") - msg := string(buf.Bytes()) + msg := string(b.Bytes()) componentValue := fmt.Sprintf(`%q:%q`, sink.ComponentKey, component) levelValue := fmt.Sprintf(`%q:"%d"`, sink.LevelKey, level) @@ -46,13 +55,3 @@ func TestNewLogger_WithOptions(t *testing.T) { require.Contains(t, msg, componentValue) require.Contains(t, msg, levelValue) } - -func TestNewLoggerV(t *testing.T) { - l := log.NewLogger("mycomponent") - - ll := l.V(1) - require.False(t, ll.Enabled()) - - lv := log.NewLoggerV(l, 1) - require.True(t, lv.Enabled()) -} diff --git a/log/options.go b/log/options.go index 2f8e4c4..5523aee 100644 --- a/log/options.go +++ b/log/options.go @@ -2,10 +2,23 @@ package log import ( "io" + + "github.com/ViaQ/logerr/v2/internal/sink" ) -// Options is a configuration option -type Options struct { - Writer io.Writer - LogLevel int +// Option is a configuration option +type Option func(*sink.Sink) + +// WithOutput sets the output of the internal sink of the logger +func WithOutput(w io.Writer) Option { + return func(s *sink.Sink) { + s.SetOutput(w) + } +} + +// WithVerbosity sets the verbosity of the internal sink of the logger +func WithVerbosity(v int) Option { + return func(s *sink.Sink) { + s.SetVerbosity(v) + } }