Skip to content

✨ Added Encoder Config Options field. #915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ require (
github.com/onsi/gomega v1.10.1
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/client_model v0.2.0
github.com/spf13/pflag v1.0.5
go.uber.org/zap v1.15.0
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
gomodules.xyz/jsonpatch/v2 v2.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand Down Expand Up @@ -233,6 +234,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
Expand Down
24 changes: 7 additions & 17 deletions pkg/log/zap/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ limitations under the License.
package zap

import (
"flag"
"fmt"
"strconv"
"strings"

"github.com/spf13/pflag"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
Expand All @@ -42,11 +42,11 @@ var stackLevelStrings = map[string]zapcore.Level{
}

type encoderFlag struct {
setFunc func(zapcore.Encoder)
setFunc func(NewEncoderFunc)
value string
}

var _ pflag.Value = &encoderFlag{}
var _ flag.Value = &encoderFlag{}

func (ev *encoderFlag) String() string {
return ev.value
Expand All @@ -60,32 +60,22 @@ func (ev *encoderFlag) Set(flagValue string) error {
val := strings.ToLower(flagValue)
switch val {
case "json":
ev.setFunc(newJSONEncoder())
ev.setFunc(newJSONEncoder)
case "console":
ev.setFunc(newConsoleEncoder())
ev.setFunc(newConsoleEncoder)
default:
return fmt.Errorf("invalid encoder value \"%s\"", flagValue)
}
ev.value = flagValue
return nil
}

func newJSONEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
return zapcore.NewJSONEncoder(encoderConfig)
}

func newConsoleEncoder() zapcore.Encoder {
encoderConfig := zap.NewDevelopmentEncoderConfig()
return zapcore.NewConsoleEncoder(encoderConfig)
}

type levelFlag struct {
setFunc func(zapcore.LevelEnabler)
value string
}

var _ pflag.Value = &levelFlag{}
var _ flag.Value = &levelFlag{}

func (ev *levelFlag) Set(flagValue string) error {
level, validLevel := levelStrings[strings.ToLower(flagValue)]
Expand Down Expand Up @@ -120,7 +110,7 @@ type stackTraceFlag struct {
value string
}

var _ pflag.Value = &stackTraceFlag{}
var _ flag.Value = &stackTraceFlag{}

func (ev *stackTraceFlag) Set(flagValue string) error {
level, validLevel := stackLevelStrings[strings.ToLower(flagValue)]
Expand Down
53 changes: 42 additions & 11 deletions pkg/log/zap/zap.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ import (
"go.uber.org/zap/zapcore"
)

// EncoderConfigOption is a function that can modify a `zapcore.EncoderConfig`.
type EncoderConfigOption func(*zapcore.EncoderConfig)

// NewEncoderFunc is a function that creates an Encoder using the provided EncoderConfigOptions.
type NewEncoderFunc func(...EncoderConfigOption) zapcore.Encoder

// New returns a brand new Logger configured with Opts. It
// uses KubeAwareEncoder which adds Type information and
// Namespace/Name to the log.
Expand Down Expand Up @@ -65,6 +71,22 @@ func Encoder(encoder zapcore.Encoder) func(o *Options) {
}
}

func newJSONEncoder(opts ...EncoderConfigOption) zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
for _, opt := range opts {
opt(&encoderConfig)
}
return zapcore.NewJSONEncoder(encoderConfig)
}

func newConsoleEncoder(opts ...EncoderConfigOption) zapcore.Encoder {
encoderConfig := zap.NewDevelopmentEncoderConfig()
for _, opt := range opts {
opt(&encoderConfig)
}
return zapcore.NewConsoleEncoder(encoderConfig)
}

// Level sets the the minimum enabled logging level e.g Debug, Info
// See Options.Level
func Level(level zapcore.LevelEnabler) func(o *Options) {
Expand Down Expand Up @@ -99,6 +121,14 @@ type Options struct {
// Encoder configures how Zap will encode the output. Defaults to
// console when Development is true and JSON otherwise
Encoder zapcore.Encoder
// EncoderConfigOptions can modify the EncoderConfig needed to initialize an Encoder.
// See https://godoc.org/go.uber.org/zap/zapcore#EncoderConfig for the list of options
// that can be configured.
// Note that the EncoderConfigOptions are not applied when the Encoder option is already set.
EncoderConfigOptions []EncoderConfigOption
// NewEncoder configures Encoder using the provided EncoderConfigOptions.
// Note that the NewEncoder function is not used when the Encoder option is already set.
NewEncoder NewEncoderFunc
// DestWritter controls the destination of the log output. Defaults to
// os.Stderr.
DestWritter io.Writer
Expand All @@ -121,9 +151,8 @@ func (o *Options) addDefaults() {
}

if o.Development {
if o.Encoder == nil {
encCfg := zap.NewDevelopmentEncoderConfig()
o.Encoder = zapcore.NewConsoleEncoder(encCfg)
if o.NewEncoder == nil {
o.NewEncoder = newConsoleEncoder
}
if o.Level == nil {
lvl := zap.NewAtomicLevelAt(zap.DebugLevel)
Expand All @@ -136,9 +165,8 @@ func (o *Options) addDefaults() {
o.ZapOpts = append(o.ZapOpts, zap.Development())

} else {
if o.Encoder == nil {
encCfg := zap.NewProductionEncoderConfig()
o.Encoder = zapcore.NewJSONEncoder(encCfg)
if o.NewEncoder == nil {
o.NewEncoder = newJSONEncoder
}
if o.Level == nil {
lvl := zap.NewAtomicLevelAt(zap.InfoLevel)
Expand All @@ -157,6 +185,9 @@ func (o *Options) addDefaults() {
}))
}
}
if o.Encoder == nil {
o.Encoder = o.NewEncoder(o.EncoderConfigOptions...)
}
o.ZapOpts = append(o.ZapOpts, zap.AddStacktrace(o.StacktraceLevel))
}

Expand All @@ -182,7 +213,7 @@ func NewRaw(opts ...Opts) *zap.Logger {
// BindFlags will parse the given flagset for zap option flags and set the log options accordingly
// zap-devel: Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn)
// Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
// zap-encoder: Zap log encoding ('json' or 'console')
// zap-encoder: Zap log encoding (one of 'json' or 'console')
// zap-log-level: Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error',
// or any integer value > 0 which corresponds to custom debug levels of increasing verbosity")
// zap-stacktrace-level: Zap Level at and above which stacktraces are captured (one of 'info' or 'error')
Expand All @@ -195,10 +226,10 @@ func (o *Options) BindFlags(fs *flag.FlagSet) {

// Set Encoder value
var encVal encoderFlag
encVal.setFunc = func(fromFlag zapcore.Encoder) {
o.Encoder = fromFlag
encVal.setFunc = func(fromFlag NewEncoderFunc) {
o.NewEncoder = fromFlag
}
fs.Var(&encVal, "zap-encoder", "Zap log encoding ('json' or 'console')")
fs.Var(&encVal, "zap-encoder", "Zap log encoding (one of 'json' or 'console')")

// Set the Log Level
var levelVal levelFlag
Expand All @@ -221,10 +252,10 @@ func (o *Options) BindFlags(fs *flag.FlagSet) {
// UseFlagOptions configures the logger to use the Options set by parsing zap option flags from the CLI.
// opts := zap.Options{}
// opts.BindFlags(flag.CommandLine)
// flag.Parse()
// log := zap.New(zap.UseFlagOptions(&opts))
func UseFlagOptions(in *Options) Opts {
return func(o *Options) {
*o = *in
o.addDefaults()
}
}
80 changes: 80 additions & 0 deletions pkg/log/zap/zap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,84 @@ var _ = Describe("Zap log level flag options setup", func() {

})

Context("with only -zap-devel flag provided", func() {
It("Should set dev=true.", func() {
args := []string{"--zap-devel=true"}
fromFlags.BindFlags(&fs)
if err := fs.Parse(args); err != nil {
Expect(err).ToNot(HaveOccurred())
}
out := Options{}
UseFlagOptions(&fromFlags)(&out)

Expect(out.Development).To(BeTrue())
Expect(out.Encoder).To(BeNil())
Expect(out.Level).To(BeNil())
Expect(out.StacktraceLevel).To(BeNil())
Expect(out.EncoderConfigOptions).To(BeNil())
})
It("Should set dev=false", func() {
args := []string{"--zap-devel=false"}
fromFlags.BindFlags(&fs)
if err := fs.Parse(args); err != nil {
Expect(err).ToNot(HaveOccurred())
}
out := Options{}
UseFlagOptions(&fromFlags)(&out)

Expect(out.Development).To(BeFalse())
Expect(out.Encoder).To(BeNil())
Expect(out.Level).To(BeNil())
Expect(out.StacktraceLevel).To(BeNil())
Expect(out.EncoderConfigOptions).To(BeNil())

})
})

Context("with encoder options provided programmatically.", func() {

It("Should set Console Encoder, with given Nanos TimeEncoder option.", func() {
logOut := new(bytes.Buffer)
f := func(ec *zapcore.EncoderConfig) {
if err := ec.EncodeTime.UnmarshalText([]byte("nanos")); err != nil {
Expect(err).ToNot(HaveOccurred())
}
}
opts := func(o *Options) {
o.EncoderConfigOptions = append(o.EncoderConfigOptions, f)
}
log := New(UseDevMode(true), WriteTo(logOut), opts)
log.Info("This is a test message")
outRaw := logOut.Bytes()
// Assert for Console Encoder
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).ToNot(Succeed())
// Assert for Epoch Nanos TimeEncoder
Expect(string(outRaw)).ShouldNot(ContainSubstring("."))

})
It("Should set JSON Encoder, with given Millis TimeEncoder option, and MessageKey", func() {
logOut := new(bytes.Buffer)
f := func(ec *zapcore.EncoderConfig) {
ec.MessageKey = "MillisTimeFormat"
if err := ec.EncodeTime.UnmarshalText([]byte("millis")); err != nil {
Expect(err).ToNot(HaveOccurred())
}
}
opts := func(o *Options) {
o.EncoderConfigOptions = append(o.EncoderConfigOptions, f)
}
log := New(UseDevMode(false), WriteTo(logOut), opts)
log.Info("This is a test message")
outRaw := logOut.Bytes()
// Assert for JSON Encoder
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())
// Assert for Epoch Nanos TimeEncoder
Expect(string(outRaw)).Should(ContainSubstring("."))
// Assert for MessageKey
Expect(string(outRaw)).Should(ContainSubstring("MillisTimeFormat"))
})

})
})