joriszwart.nl

I code dreams™

io.MultiWriter gotcha!

Golang GAAS Gotcha! RTFM

The issue

I ran into a problem where no logging was being written to a file.

The problematic code was this:

f, err := os.OpenFile(...)
if err != nil {
	return err
}

mw := io.MultiWriter(os.Stdout, f)
log.SetOutput(mw)

This construct is used to log to a file and for convenience to os.Stdout as well.

At first glance it seems nothing wrong with it. However, reading the docs for io.MultiWriter carefully, it says:

Each write is written to each listed writer, one at a time. If a listed writer returns an error, that overall write operation stops and returns the error; it does not continue down the list.

So what's going on? Why would writing to STDOUT return an error? There can be several reasons, but mine was using github.com/kardianos/service (awesome!) to run the application at startup.

The point is - when running as a service - there is no os.Stdout because services are not interactive (in short).

There you have it 😃

The solution

This was easily remedied by reversing the order of arguments to MultiWriter.

/*
  Non-interactive writers must be specified first, otherwise there
  will be no logging when running as a service (because there is no
  Stdout and the MultiWriter fails)
*/

log.SetOutput(io.MultiWriter(logf, os.Stdout))

But what if writing to a file fails? Next up!

Lessons learned

Do not assume STDOUT is available and RTFM.