io.MultiWriter gotcha!
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.