diff --git a/config.go b/config.go index e76e4e64f..d046f15ce 100644 --- a/config.go +++ b/config.go @@ -237,12 +237,12 @@ func NewDevelopmentConfig() Config { // Build constructs a logger from the Config and Options. func (cfg Config) Build(opts ...Option) (*Logger, error) { - enc, err := cfg.buildEncoder() + enc, err := cfg.buildEncoder(filterOptions[wrapEncoderOption](opts...)...) if err != nil { return nil, err } - sink, errSink, err := cfg.openSinks() + sink, errSink, err := cfg.openSinks(filterOptions[wrapSinkerOption](opts...)...) if err != nil { return nil, err } @@ -256,7 +256,7 @@ func (cfg Config) Build(opts ...Option) (*Logger, error) { cfg.buildOptions(errSink)..., ) if len(opts) > 0 { - log = log.WithOptions(opts...) + log = log.WithOptions(filterOptions[Option](opts...)...) } return log, nil } @@ -312,19 +312,38 @@ func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option { return opts } -func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { +func (cfg Config) openSinks(opts ...wrapSinkerOption) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { sink, closeOut, err := Open(cfg.OutputPaths...) if err != nil { return nil, nil, err } - errSink, _, err := Open(cfg.ErrorOutputPaths...) + errSink, errCloseOut, err := Open(cfg.ErrorOutputPaths...) if err != nil { closeOut() return nil, nil, err } + + for _, opt := range opts { + sink, errSink, err = opt.wrapSink(cfg.OutputPaths, sink, cfg.ErrorOutputPaths, errSink) + if err != nil { + closeOut() + errCloseOut() + return nil, nil, err + } + } return sink, errSink, nil } -func (cfg Config) buildEncoder() (zapcore.Encoder, error) { - return newEncoder(cfg.Encoding, cfg.EncoderConfig) +func (cfg Config) buildEncoder(opts ...wrapEncoderOption) (zapcore.Encoder, error) { + enc, err := newEncoder(cfg.Encoding, cfg.EncoderConfig) + if err != nil { + return nil, err + } + for _, opt := range opts { + enc, err = opt.wrapEncoder(cfg.Encoding, cfg.EncoderConfig, enc) + if err != nil { + return nil, err + } + } + return enc, nil } diff --git a/options.go b/options.go index 43d357ac9..2d4dbe145 100644 --- a/options.go +++ b/options.go @@ -26,11 +26,49 @@ import ( "go.uber.org/zap/zapcore" ) +func filterOptions[T Option](in ...Option) []T { + var opts []T + for _, opt := range in { + if o, ok := opt.(T); ok { + opts = append(opts, o) + } + } + return opts +} + // An Option configures a Logger. type Option interface { apply(*Logger) } +// wrapEncoderOption wraps a func to make it satisfy the WrapEncoderOption interface. +type wrapEncoderOption func(encoding string, cfg zapcore.EncoderConfig, encoder zapcore.Encoder) (zapcore.Encoder, error) + +func (f wrapEncoderOption) wrapEncoder(encoding string, cfg zapcore.EncoderConfig, encoder zapcore.Encoder) (zapcore.Encoder, error) { + return f(encoding, cfg, encoder) +} + +func (f wrapEncoderOption) apply(_ *Logger) {} + +// WrapEncoder wraps or replaces the Logger's underlying zapcore.Encoder. +func WrapEncoder(fn func(encoding string, cfg zapcore.EncoderConfig, encoder zapcore.Encoder) (zapcore.Encoder, error)) Option { + return wrapEncoderOption(fn) +} + +// wrapEncoderOption wraps a func to make it satisfy the WrapSinkersOption interface. +type wrapSinkerOption func(paths []string, sink zapcore.WriteSyncer, errPath []string, errSink zapcore.WriteSyncer) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) + +func (f wrapSinkerOption) wrapSink(paths []string, sink zapcore.WriteSyncer, errPath []string, errSink zapcore.WriteSyncer) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { + return f(paths, sink, errPath, errSink) +} + +func (f wrapSinkerOption) apply(_ *Logger) {} + +// WrapSinker wraps or replaces the Logger's underlying zapcore.Encoder. +func WrapSinker(fn func(paths []string, sink zapcore.WriteSyncer, errPath []string, errSink zapcore.WriteSyncer) (zapcore.WriteSyncer, zapcore.WriteSyncer, error)) Option { + return wrapSinkerOption(fn) +} + // optionFunc wraps a func so it satisfies the Option interface. type optionFunc func(*Logger)