Repository: uber-go/zap
File: zapgrpc/zapgrpc.go (Go)

Code before:
	return func(loggerOptions *loggerOptions) {
		loggerOptions.printFunc = (*zap.SugaredLogger).Debug
		loggerOptions.printfFunc = (*zap.SugaredLogger).Debugf
	}
}

// NewLogger returns a new Logger.
func NewLogger(l *zap.Logger, options ...LoggerOption) *Logger {
	loggerOptions := newLoggerOptions(options...)
	return &Logger{
		l.Sugar(),
		(*zap.SugaredLogger).Fatal,
		(*zap.SugaredLogger).Fatalf,
		loggerOptions.printFunc,
		loggerOptions.printfFunc,
	}
}

// Logger is a logger that is compatible with the grpclog Logger interface.
type Logger struct {
	sugaredLogger *zap.SugaredLogger
	fatalFunc     func(*zap.SugaredLogger, ...interface{})
	fatalfFunc    func(*zap.SugaredLogger, string, ...interface{})
	printFunc     func(*zap.SugaredLogger, ...interface{})
	printfFunc    func(*zap.SugaredLogger, string, ...interface{})
}

// Fatal implements grpclog.Logger#Fatal.
func (l *Logger) Fatal(args ...interface{}) {
	l.fatalFunc(l.sugaredLogger, args...)
}

// Fatalf implements grpclog.Logger#Fatalf.
func (l *Logger) Fatalf(format string, args ...interface{}) {
	l.fatalfFunc(l.sugaredLogger, format, args...)
}

// Fatalln implements grpclog.Logger#Fatalln.
func (l *Logger) Fatalln(args ...interface{}) {
	l.fatalFunc(l.sugaredLogger, args...)
}

// Print implements grpclog.Logger#Print.
func (l *Logger) Print(args ...interface{}) {
	l.printFunc(l.sugaredLogger, args...)
}

// Printf implements grpclog.Logger#Printf.
func (l *Logger) Printf(format string, args ...interface{}) {
	l.printfFunc(l.sugaredLogger, format, args...)
}

Review comment (performance):
What's the benefit of keeping these four function pointers instead of

```
type Logger struct {
  sugar *zap.SugaredLogger
  development bool
}
```

and checking `development` in `Print` and friends? I don't think that the performance impact of a single boolean check will even be measurable relative to the cost of string formatting.

This also lets us get rid of the the `loggerOptions` type in favor of `type Option func(*Logger)` (or even `type Option interface { apply(*Logger) }`).

Code after:
type optionFunc func(*Logger)

func (f optionFunc) apply(log *Logger) {
	f(log)
}

// WithDebug configures a Logger to print at zap's DebugLevel instead of
// InfoLevel.
func WithDebug() Option {
	return optionFunc(func(logger *Logger) {
		logger.print = (*zap.SugaredLogger).Debug
		logger.printf = (*zap.SugaredLogger).Debugf
	})
}

// NewLogger returns a new Logger.
//
// By default, Loggers print at zap's InfoLevel.
func NewLogger(l *zap.Logger, options ...Option) *Logger {
	logger := &Logger{
		log:    l.Sugar(),
		fatal:  (*zap.SugaredLogger).Fatal,
		fatalf: (*zap.SugaredLogger).Fatalf,
		print:  (*zap.SugaredLogger).Info,
		printf: (*zap.SugaredLogger).Infof,
	}
	for _, option := range options {
		option.apply(logger)
	}
	return logger
}

// Logger adapts zap's Logger to be compatible with grpclog.Logger.
type Logger struct {
	log    *zap.SugaredLogger
	fatal  func(*zap.SugaredLogger, ...interface{})
	fatalf func(*zap.SugaredLogger, string, ...interface{})
	print  func(*zap.SugaredLogger, ...interface{})
	printf func(*zap.SugaredLogger, string, ...interface{})
}

// Fatal implements grpclog.Logger.
func (l *Logger) Fatal(args ...interface{}) {
	l.fatal(l.log, args...)
}

// Fatalf implements grpclog.Logger.
func (l *Logger) Fatalf(format string, args ...interface{}) {
	l.fatalf(l.log, format, args...)
}