Original post

I’m working on a project where I’m implementing a logger on top of Zap. The wisdom of this withstanding (I have my orders…), the idea is to create ONE common logging system that doesn’t expose Zap to the other developers on the project so that there’s only one way to log things, thus resulting in consistent use of Zap.

Anyway, I need to implement a method like this:

log.Infof("This should be a %s string! Hello %s!", "formatted", "world")

What I’m doing right now is:

// LogObject is a struct with a Logger in it that's a pointer to zap.Logger
// and a string called ServiceName since this is part of a SOA (common package)
func (l *LogObject) Infof(msg, args ...interface{}) {
  defer l.Logger.Sync()
  sugar := l.Logger.Sugar()
  sugar.Infow(fmt.Sprintf(msg, args), "service", l.ServiceName)

The problem with this approach is that I wind up with log output like:

2020-11-20T18:51:33.149-0700       INFO    logger/logger.:55     Starting [Compute development] service in %!s(MISSING) mode...  {"service": "COMPUTE"}

Where it should say “Starting Compute service in development mode”.

The good news with this approach is that by using ...interface{} I’m able to log struct objects too, like the configuration struct I’m creating for the app. I need to keep that ability, but how do I delegate to fmt.Sprintf() correctly so I don’t have weird mismatched string replacements like this?