From e1bca37bcf0767a5e9a524e53de61981c062fbcc Mon Sep 17 00:00:00 2001 From: Aleksandr Razumov Date: Thu, 24 Dec 2020 14:30:59 +0300 Subject: [PATCH] Add WithClock Option Allow passing current time source. --- clock.go | 36 ++++++++++++++++++++++++++++++++++++ clock_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ logger.go | 8 +++++--- options.go | 7 +++++++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 clock.go create mode 100644 clock_test.go diff --git a/clock.go b/clock.go new file mode 100644 index 000000000..06d42e8b2 --- /dev/null +++ b/clock.go @@ -0,0 +1,36 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import "time" + +// Clock represents time source. +type Clock interface { + // Now returns current time. + Now() time.Time +} + +// systemClock implements default Clock that uses system time. +type systemClock struct{} + +func (systemClock) Now() time.Time { + return time.Now() +} diff --git a/clock_test.go b/clock_test.go new file mode 100644 index 000000000..0d87a03ed --- /dev/null +++ b/clock_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest/observer" +) + +type constantClock struct { + now time.Time +} + +func (c constantClock) Now() time.Time { return c.now } + +func TestWithClock(t *testing.T) { + date := time.Date(2077, 1, 23, 10, 15, 13, 441, time.UTC) + clock := constantClock{now: date} + withLogger(t, DebugLevel, []Option{WithClock(clock)}, func(log *Logger, logs *observer.ObservedLogs) { + log.Info("") + require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.") + assert.Equal(t, date, logs.All()[0].Entry.Time, "Unexpected entry time.") + }) +} diff --git a/logger.go b/logger.go index bdcc5d666..0b8b1c39e 100644 --- a/logger.go +++ b/logger.go @@ -26,7 +26,6 @@ import ( "os" "runtime" "strings" - "time" "go.uber.org/zap/zapcore" ) @@ -51,6 +50,8 @@ type Logger struct { addStack zapcore.LevelEnabler callerSkip int + + clock Clock } // New constructs a new Logger from the provided zapcore.Core and Options. If @@ -71,6 +72,7 @@ func New(core zapcore.Core, options ...Option) *Logger { core: core, errorOutput: zapcore.Lock(os.Stderr), addStack: zapcore.FatalLevel + 1, + clock: systemClock{}, } return log.WithOptions(options...) } @@ -270,7 +272,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { // log message will actually be written somewhere. ent := zapcore.Entry{ LoggerName: log.name, - Time: time.Now(), + Time: log.clock.Now(), Level: lvl, Message: msg, } @@ -307,7 +309,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { if log.addCaller { frame, defined := getCallerFrame(log.callerSkip + callerSkipOffset) if !defined { - fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC()) + fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", log.clock.Now().UTC()) log.errorOutput.Sync() } diff --git a/options.go b/options.go index 0135c2092..3062317c1 100644 --- a/options.go +++ b/options.go @@ -138,3 +138,10 @@ func OnFatal(action zapcore.CheckWriteAction) Option { log.onFatal = action }) } + +// WithClock configures the Logger to use Clock while creating entries. +func WithClock(clock Clock) Option { + return optionFunc(func(log *Logger) { + log.clock = clock + }) +}